View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.index.reader;
20  
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.io.OutputStream;
24  import java.util.HashSet;
25  import java.util.Set;
26  
27  import org.apache.maven.index.reader.WritableResourceHandler.WritableResource;
28  
29  /**
30   * A trivial caching {@link ResourceHandler} that caches forever during single session (existence of the instance).
31   */
32  public class CachingResourceHandler implements ResourceHandler {
33      private static final Resource NOT_EXISTING_RESOURCE = () -> null;
34  
35      private final WritableResourceHandler local;
36  
37      private final ResourceHandler remote;
38  
39      private final Set<String> notFoundResources;
40  
41      public CachingResourceHandler(final WritableResourceHandler local, final ResourceHandler remote) {
42          if (local == null || remote == null) {
43              throw new NullPointerException("null resource handler");
44          }
45          this.local = local;
46          this.remote = remote;
47          this.notFoundResources = new HashSet<>();
48      }
49  
50      public Resource locate(final String name) {
51          if (notFoundResources.contains(name)) {
52              return NOT_EXISTING_RESOURCE;
53          } else {
54              return new CachingResource(name);
55          }
56      }
57  
58      private class CachingResource implements Resource {
59          private final String name;
60  
61          private CachingResource(final String name) {
62              this.name = name;
63          }
64  
65          public InputStream read() throws IOException {
66              InputStream inputStream = local.locate(name).read();
67              if (inputStream != null) {
68                  return inputStream;
69              }
70              if (cacheLocally(name)) {
71                  return local.locate(name).read();
72              }
73              notFoundResources.add(name);
74              return null;
75          }
76  
77          private boolean cacheLocally(final String name) throws IOException {
78              final Resource remoteResource = remote.locate(name);
79              try (WritableResource localResource = local.locate(name);
80                      final InputStream inputStream = remoteResource.read();
81                      final OutputStream outputStream = localResource.write()) {
82                  if (inputStream != null) {
83                      int read;
84                      byte[] bytes = new byte[8192];
85                      while ((read = inputStream.read(bytes)) != -1) {
86                          outputStream.write(bytes, 0, read);
87                      }
88                      outputStream.flush();
89                      return true;
90                  }
91                  return false;
92              }
93          }
94      }
95  
96      public void close() throws IOException {
97          remote.close();
98          local.close();
99      }
100 }