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