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      @Override
51      public Resource locate(final String name) {
52          if (notFoundResources.contains(name)) {
53              return NOT_EXISTING_RESOURCE;
54          } else {
55              return new CachingResource(name);
56          }
57      }
58  
59      private class CachingResource implements Resource {
60          private final String name;
61  
62          private CachingResource(final String name) {
63              this.name = name;
64          }
65  
66          @Override
67          public InputStream read() throws IOException {
68              InputStream inputStream = local.locate(name).read();
69              if (inputStream != null) {
70                  return inputStream;
71              }
72              if (cacheLocally(name)) {
73                  return local.locate(name).read();
74              }
75              notFoundResources.add(name);
76              return null;
77          }
78  
79          private boolean cacheLocally(final String name) throws IOException {
80              final Resource remoteResource = remote.locate(name);
81              try (WritableResource localResource = local.locate(name);
82                      final InputStream inputStream = remoteResource.read();
83                      final OutputStream outputStream = localResource.write()) {
84                  if (inputStream != null) {
85                      int read;
86                      byte[] bytes = new byte[8192];
87                      while ((read = inputStream.read(bytes)) != -1) {
88                          outputStream.write(bytes, 0, read);
89                      }
90                      outputStream.flush();
91                      return true;
92                  }
93                  return false;
94              }
95          }
96      }
97  
98      @Override
99      public void close() throws IOException {
100         remote.close();
101         local.close();
102     }
103 }