1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  package org.apache.maven.index.reader;
20  
21  import java.io.Closeable;
22  import java.io.IOException;
23  import java.io.UncheckedIOException;
24  import java.text.ParseException;
25  import java.util.ArrayList;
26  import java.util.Collections;
27  import java.util.Date;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Properties;
31  import java.util.concurrent.atomic.AtomicBoolean;
32  
33  import org.apache.maven.index.reader.ResourceHandler.Resource;
34  
35  import static java.util.Objects.requireNonNull;
36  import static org.apache.maven.index.reader.Utils.loadProperties;
37  import static org.apache.maven.index.reader.Utils.storeProperties;
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  public class IndexReader implements Iterable<ChunkReader>, Closeable {
50      private final AtomicBoolean closed;
51  
52      private final WritableResourceHandler local;
53  
54      private final ResourceHandler remote;
55  
56      private final Properties localIndexProperties;
57  
58      private final Properties remoteIndexProperties;
59  
60      private final String indexId;
61  
62      private final Date publishedTimestamp;
63  
64      private final boolean incremental;
65  
66      private final List<String> chunkNames;
67  
68      public IndexReader(final WritableResourceHandler local, final ResourceHandler remote) throws IOException {
69          requireNonNull(remote, "remote resource handler null");
70          this.closed = new AtomicBoolean(false);
71          this.local = local;
72          this.remote = remote;
73          remoteIndexProperties = loadProperties(remote.locate(Utils.INDEX_FILE_PREFIX + ".properties"));
74          if (remoteIndexProperties == null) {
75              throw new IllegalArgumentException("Non-existent remote index");
76          }
77          try {
78              if (local != null) {
79                  Properties localProperties = loadProperties(local.locate(Utils.INDEX_FILE_PREFIX + ".properties"));
80                  if (localProperties != null) {
81                      this.localIndexProperties = localProperties;
82                      String remoteIndexId = remoteIndexProperties.getProperty("nexus.index.id");
83                      String localIndexId = localIndexProperties.getProperty("nexus.index.id");
84                      if (remoteIndexId == null || !remoteIndexId.equals(localIndexId)) {
85                          throw new IllegalArgumentException("local and remote index IDs does not match or is null: "
86                                  + localIndexId + ", " + remoteIndexId);
87                      }
88                      this.indexId = localIndexId;
89                      this.incremental = canRetrieveAllChunks();
90                  } else {
91                      localIndexProperties = null;
92                      this.indexId = remoteIndexProperties.getProperty("nexus.index.id");
93                      this.incremental = false;
94                  }
95              } else {
96                  localIndexProperties = null;
97                  this.indexId = remoteIndexProperties.getProperty("nexus.index.id");
98                  this.incremental = false;
99              }
100             this.publishedTimestamp =
101                     Utils.INDEX_DATE_FORMAT.parse(remoteIndexProperties.getProperty("nexus.index.timestamp"));
102             this.chunkNames = calculateChunkNames();
103         } catch (ParseException e) {
104             throw new IOException("Index properties corrupted", e);
105         }
106     }
107 
108     
109 
110 
111 
112     public String getIndexId() {
113         return indexId;
114     }
115 
116     
117 
118 
119     public Date getPublishedTimestamp() {
120         return publishedTimestamp;
121     }
122 
123     
124 
125 
126 
127     public boolean isIncremental() {
128         return incremental;
129     }
130 
131     
132 
133 
134 
135 
136     public List<String> getChunkNames() {
137         return chunkNames;
138     }
139 
140     
141 
142 
143 
144 
145 
146 
147     @Override
148     public void close() throws IOException {
149         if (closed.compareAndSet(false, true)) {
150             remote.close();
151             if (local != null) {
152                 try {
153                     syncLocalWithRemote();
154                 } finally {
155                     local.close();
156                 }
157             }
158         }
159     }
160 
161     
162 
163 
164 
165 
166     @Override
167     public Iterator<ChunkReader> iterator() {
168         return new ChunkReaderIterator(remote, chunkNames.iterator());
169     }
170 
171     
172 
173 
174 
175     private void syncLocalWithRemote() throws IOException {
176         storeProperties(local.locate(Utils.INDEX_FILE_PREFIX + ".properties"), remoteIndexProperties);
177     }
178 
179     
180 
181 
182     private List<String> calculateChunkNames() {
183         if (incremental) {
184             ArrayList<String> chunkNames = new ArrayList<>();
185             int maxCounter = Integer.parseInt(remoteIndexProperties.getProperty("nexus.index.last-incremental"));
186             int currentCounter = Integer.parseInt(localIndexProperties.getProperty("nexus.index.last-incremental"));
187             currentCounter++;
188             while (currentCounter <= maxCounter) {
189                 chunkNames.add(Utils.INDEX_FILE_PREFIX + "." + currentCounter++ + ".gz");
190             }
191             return Collections.unmodifiableList(chunkNames);
192         } else {
193             return Collections.singletonList(Utils.INDEX_FILE_PREFIX + ".gz");
194         }
195     }
196 
197     
198 
199 
200     private boolean canRetrieveAllChunks() {
201         String localChainId = localIndexProperties.getProperty("nexus.index.chain-id");
202         String remoteChainId = remoteIndexProperties.getProperty("nexus.index.chain-id");
203 
204         
205         if (localChainId == null || !localChainId.equals(remoteChainId)) {
206             return false;
207         }
208 
209         try {
210             int localLastIncremental =
211                     Integer.parseInt(localIndexProperties.getProperty("nexus.index.last-incremental"));
212             String currentLocalCounter = String.valueOf(localLastIncremental);
213             String nextLocalCounter = String.valueOf(localLastIncremental + 1);
214             
215             for (Object key : remoteIndexProperties.keySet()) {
216                 String sKey = (String) key;
217                 if (sKey.startsWith("nexus.index.incremental-")) {
218                     String value = remoteIndexProperties.getProperty(sKey);
219                     if (currentLocalCounter.equals(value) || nextLocalCounter.equals(value)) {
220                         return true;
221                     }
222                 }
223             }
224         } catch (NumberFormatException e) {
225             
226         }
227         return false;
228     }
229 
230     
231 
232 
233 
234     private static class ChunkReaderIterator implements Iterator<ChunkReader> {
235         private final ResourceHandler resourceHandler;
236 
237         private final Iterator<String> chunkNamesIterator;
238 
239         private ChunkReaderIterator(final ResourceHandler resourceHandler, final Iterator<String> chunkNamesIterator) {
240             this.resourceHandler = resourceHandler;
241             this.chunkNamesIterator = chunkNamesIterator;
242         }
243 
244         @Override
245         public boolean hasNext() {
246             return chunkNamesIterator.hasNext();
247         }
248 
249         @Override
250         public ChunkReader next() {
251             String chunkName = chunkNamesIterator.next();
252             try {
253                 Resource currentResource = resourceHandler.locate(chunkName);
254                 return new ChunkReader(chunkName, currentResource.read());
255             } catch (IOException e) {
256                 throw new UncheckedIOException("IO problem while opening chunk readers", e);
257             }
258         }
259 
260         @Override
261         public void remove() {
262             throw new UnsupportedOperationException("remove");
263         }
264     }
265 }