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.packer;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Singleton;
24  
25  import java.io.File;
26  import java.io.FileInputStream;
27  import java.io.FileOutputStream;
28  import java.io.IOException;
29  import java.io.OutputStream;
30  import java.text.SimpleDateFormat;
31  import java.util.Date;
32  import java.util.List;
33  import java.util.Properties;
34  import java.util.TimeZone;
35  
36  import org.apache.maven.index.context.IndexingContext;
37  import org.apache.maven.index.incremental.IncrementalHandler;
38  import org.apache.maven.index.updater.IndexDataWriter;
39  import org.codehaus.plexus.util.FileUtils;
40  import org.slf4j.Logger;
41  import org.slf4j.LoggerFactory;
42  
43  /**
44   * A default {@link IndexPacker} implementation. Creates the properties, legacy index zip and new gz files.
45   *
46   * @author Tamas Cservenak
47   * @author Eugene Kuleshov
48   */
49  @Singleton
50  @Named
51  public class DefaultIndexPacker implements IndexPacker {
52  
53      private final Logger logger = LoggerFactory.getLogger(getClass());
54  
55      protected Logger getLogger() {
56          return logger;
57      }
58  
59      private final IncrementalHandler incrementalHandler;
60  
61      @Inject
62      public DefaultIndexPacker(IncrementalHandler incrementalHandler) {
63          this.incrementalHandler = incrementalHandler;
64      }
65  
66      public void packIndex(IndexPackingRequest request) throws IOException, IllegalArgumentException {
67          if (request.getTargetDir() == null) {
68              throw new IllegalArgumentException("The target dir is null");
69          }
70  
71          if (request.getTargetDir().exists()) {
72              if (!request.getTargetDir().isDirectory()) {
73                  throw new IllegalArgumentException( //
74                          String.format(
75                                  "Specified target path %s is not a directory",
76                                  request.getTargetDir().getAbsolutePath()));
77              }
78              if (!request.getTargetDir().canWrite()) {
79                  throw new IllegalArgumentException(String.format(
80                          "Specified target path %s is not writtable",
81                          request.getTargetDir().getAbsolutePath()));
82              }
83          } else {
84              if (!request.getTargetDir().mkdirs()) {
85                  throw new IllegalArgumentException(
86                          "Can't create " + request.getTargetDir().getAbsolutePath());
87              }
88          }
89  
90          // These are all of the files we'll be dealing with (except for the incremental chunks of course)
91          File v1File = new File(request.getTargetDir(), IndexingContext.INDEX_FILE_PREFIX + ".gz");
92  
93          Properties info;
94  
95          try {
96              // Note that for incremental indexes to work properly, a valid index.properties file
97              // must be present
98              info = readIndexProperties(request);
99  
100             if (request.isCreateIncrementalChunks()) {
101                 List<Integer> chunk = incrementalHandler.getIncrementalUpdates(request, info);
102 
103                 if (chunk == null) {
104                     getLogger().debug("Problem with Chunks, forcing regeneration of whole index");
105                     incrementalHandler.initializeProperties(info);
106                 } else if (chunk.isEmpty()) {
107                     getLogger().debug("No incremental changes, not writing new incremental chunk");
108                 } else {
109                     File file = new File(
110                             request.getTargetDir(), //
111                             IndexingContext.INDEX_FILE_PREFIX + "."
112                                     + info.getProperty(IndexingContext.INDEX_CHUNK_COUNTER) + ".gz");
113 
114                     writeIndexData(request, chunk, file);
115 
116                     if (request.isCreateChecksumFiles()) {
117                         FileUtils.fileWrite(
118                                 new File(file.getParentFile(), file.getName() + ".sha1").getAbsolutePath(),
119                                 DigesterUtils.getSha1Digest(file));
120 
121                         FileUtils.fileWrite(
122                                 new File(file.getParentFile(), file.getName() + ".md5").getAbsolutePath(),
123                                 DigesterUtils.getMd5Digest(file));
124                     }
125                 }
126             }
127         } catch (IOException e) {
128             getLogger().info("Unable to read properties file, will force index regeneration");
129             info = new Properties();
130             incrementalHandler.initializeProperties(info);
131         }
132 
133         Date timestamp = request.getContext().getTimestamp();
134 
135         if (timestamp == null) {
136             timestamp = new Date(0); // never updated
137         }
138 
139         if (request.getFormats().contains(IndexPackingRequest.IndexFormat.FORMAT_V1)) {
140             info.setProperty(IndexingContext.INDEX_TIMESTAMP, format(timestamp));
141 
142             writeIndexData(request, null, v1File);
143 
144             if (request.isCreateChecksumFiles()) {
145                 FileUtils.fileWrite(
146                         new File(v1File.getParentFile(), v1File.getName() + ".sha1").getAbsolutePath(),
147                         DigesterUtils.getSha1Digest(v1File));
148 
149                 FileUtils.fileWrite(
150                         new File(v1File.getParentFile(), v1File.getName() + ".md5").getAbsolutePath(),
151                         DigesterUtils.getMd5Digest(v1File));
152             }
153         }
154 
155         writeIndexProperties(request, info);
156     }
157 
158     private Properties readIndexProperties(IndexPackingRequest request) throws IOException {
159         File file;
160 
161         if (request.isUseTargetProperties() || request.getContext().getIndexDirectoryFile() == null) {
162             file = new File(request.getTargetDir(), IndexingContext.INDEX_REMOTE_PROPERTIES_FILE);
163         } else {
164             file = new File(request.getContext().getIndexDirectoryFile(), IndexingContext.INDEX_PACKER_PROPERTIES_FILE);
165         }
166 
167         Properties properties = new Properties();
168 
169         try (FileInputStream fos = new FileInputStream(file)) {
170             properties.load(fos);
171         }
172 
173         return properties;
174     }
175 
176     void writeIndexData(IndexPackingRequest request, List<Integer> docIndexes, File targetArchive) throws IOException {
177         if (targetArchive.exists()) {
178             targetArchive.delete();
179         }
180 
181         try (OutputStream os = new FileOutputStream(targetArchive)) {
182             IndexDataWriter dw = new IndexDataWriter(os);
183             dw.write(request.getContext(), request.getIndexReader(), docIndexes);
184 
185             os.flush();
186         }
187     }
188 
189     void writeIndexProperties(IndexPackingRequest request, Properties info) throws IOException {
190         File propertyFile =
191                 new File(request.getContext().getIndexDirectoryFile(), IndexingContext.INDEX_PACKER_PROPERTIES_FILE);
192         File targetPropertyFile = new File(request.getTargetDir(), IndexingContext.INDEX_REMOTE_PROPERTIES_FILE);
193 
194         info.setProperty(IndexingContext.INDEX_ID, request.getContext().getId());
195 
196         try (OutputStream os = new FileOutputStream(propertyFile)) {
197             info.store(os, null);
198         }
199 
200         try (OutputStream os = new FileOutputStream(targetPropertyFile)) {
201             info.store(os, null);
202         }
203 
204         if (request.isCreateChecksumFiles()) {
205             FileUtils.fileWrite(
206                     new File(targetPropertyFile.getParentFile(), targetPropertyFile.getName() + ".sha1")
207                             .getAbsolutePath(),
208                     DigesterUtils.getSha1Digest(targetPropertyFile));
209 
210             FileUtils.fileWrite(
211                     new File(targetPropertyFile.getParentFile(), targetPropertyFile.getName() + ".md5")
212                             .getAbsolutePath(),
213                     DigesterUtils.getMd5Digest(targetPropertyFile));
214         }
215     }
216 
217     private String format(Date d) {
218         SimpleDateFormat df = new SimpleDateFormat(IndexingContext.INDEX_TIME_FORMAT);
219         df.setTimeZone(TimeZone.getTimeZone("GMT"));
220         return df.format(d);
221     }
222 }