View Javadoc
1   package fr.ifremer.quadrige2.core.service.persistence;
2   
3   /*-
4    * #%L
5    * Quadrige2 Core :: Quadrige2 Client Core
6    * $Id:$
7    * $HeadURL:$
8    * %%
9    * Copyright (C) 2017 Ifremer
10   * %%
11   * This program is free software: you can redistribute it and/or modify
12   * it under the terms of the GNU Affero General Public License as published by
13   * the Free Software Foundation, either version 3 of the License, or
14   * (at your option) any later version.
15   * 
16   * This program is distributed in the hope that it will be useful,
17   * but WITHOUT ANY WARRANTY; without even the implied warranty of
18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   * GNU General Public License for more details.
20   * 
21   * You should have received a copy of the GNU Affero General Public License
22   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23   * #L%
24   */
25  
26  import com.google.common.base.Preconditions;
27  import fr.ifremer.quadrige2.core.exception.Quadrige2BusinessException;
28  import fr.ifremer.quadrige2.core.exception.Quadrige2TechnicalException;
29  import fr.ifremer.quadrige2.core.config.Quadrige2Configuration;
30  import fr.ifremer.quadrige2.core.config.Quadrige2CoreConfiguration;
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  import org.apache.commons.vfs2.AllFileSelector;
34  import org.apache.commons.vfs2.FileName;
35  import org.apache.commons.vfs2.FileObject;
36  import org.apache.commons.vfs2.FileType;
37  import org.joda.time.DateTime;
38  import org.joda.time.format.DateTimeFormat;
39  import org.joda.time.format.DateTimeFormatter;
40  import org.nuiton.jaxx.application.ApplicationIOUtil;
41  
42  import java.io.File;
43  
44  import static org.nuiton.i18n.I18n.t;
45  
46  /**
47   * <p>PersistenceServiceHelper class.</p>
48   *
49   * @author Ludovic Pecquot <ludovic.pecquot@e-is.pro>
50   */
51  public class PersistenceServiceHelper {
52  
53      private static final Log LOG = LogFactory.getLog(PersistenceServiceHelper.class);
54  
55      public enum ImportStructureType {
56  
57          NORMAL, // normal structure with a db directory + optional meas_file directory
58          INLINE // has only one root directory, with database files (.script, .properties) inside
59      }
60  
61      /** Constant <code>EXPORT_DATE_FORMAT</code> */
62      public static final DateTimeFormatter EXPORT_DATE_FORMAT = DateTimeFormat.forPattern("yyyy-MM-dd");
63  
64      public static final String EXPORT_DIRECTORY_FORMAT = "%s-%s-%s";
65  
66      /**
67       * Export db as a zip archive (including the attachments and protocols).
68       *
69       * @param file archive file where to store
70       * @since 1.0.2
71       */
72      public static void exportDb(File file) {
73  
74          // let the hsqldb database to close properly
75          try {
76              Thread.sleep(2000);
77          } catch (InterruptedException e) {
78              throw new Quadrige2TechnicalException(e);
79          }
80  
81          Quadrige2CoreConfiguration config = Quadrige2CoreConfiguration.getInstance();
82          Preconditions.checkNotNull(config);
83  
84          // need a file to export
85          Preconditions.checkNotNull(file);
86  
87          // create zip structure
88          String directoryName = String.format(
89                  EXPORT_DIRECTORY_FORMAT,
90                  config.getApplicationName(),
91                  config.getVersion(),
92                  EXPORT_DATE_FORMAT.print(new DateTime()));
93  
94          File structureDirectory = new File(config.newTempFile("exportdb"),
95              directoryName);
96  
97          try {
98              ApplicationIOUtil.forceMkdir(structureDirectory,
99                  t("quadrige2.error.create.directory", structureDirectory));
100 
101             if (LOG.isDebugEnabled()) {
102                 LOG.debug("Export directory: " + structureDirectory);
103             }
104 
105             ApplicationIOUtil.copyDirectory(config.getDbDirectory(),
106                 new File(structureDirectory, "db"),
107                 t("quadrige2.service.persistence.copyDirectory.db.error"));
108 
109             ApplicationIOUtil.copyDirectory(config.getDbAttachmentDirectory(),
110                 new File(structureDirectory, "meas_files"),
111                 t("quadrige2.service.persistence.copyDirectory.attachment.error"));
112 
113             // create zip
114             ApplicationIOUtil.zip(structureDirectory, file,
115                 t("quadrige2.service.persistence.exportDb.zip.error", file));
116 
117         } finally {
118 
119             // delete temp files
120             ApplicationIOUtil.forceDeleteOnExit(
121                 structureDirectory,
122                 t("quadrige2.service.persistence.exportDb.deleteTempDir.error", structureDirectory));
123         }
124 
125     }
126 
127     /**
128      * Import a db from a zip archive (including the attachments and protocols).
129      *
130      * @param importStructureType type of structure of import
131      * @param file archive file where to store
132      * @since 1.0.2
133      */
134     public static void importDb(ImportStructureType importStructureType, File file) {
135 
136         // can not do this operation on a opened db
137 //        Preconditions.checkState(!isDbLoaded());
138         
139         Quadrige2Configuration config = Quadrige2Configuration.getInstance();
140         Preconditions.checkNotNull(config);
141 
142         // need a file to export
143         Preconditions.checkNotNull(file);
144 
145         File target = config.getDataDirectory();
146 
147         if (LOG.isInfoEnabled()) {
148             LOG.info("Import db to " + target);
149         }
150         FileObject fileObject = ApplicationIOUtil.resolveFile(
151             "zip:" + file.getAbsolutePath(),
152             t("quadrige2.service.persistence.getArchive.error", file));
153 
154         FileObject[] children = ApplicationIOUtil.getChildren(
155             fileObject,
156             t("quadrige2.service.persistence.openArchive.error", file));
157 
158         fileObject = children[0];
159 
160         switch (importStructureType) {
161 
162             case NORMAL:
163                 // nothing special to do
164                 break;
165 
166             case INLINE:
167                 target = new File(target, "db");
168                 ApplicationIOUtil.forceMkdir(
169                     target,
170                     t("quadrige2.service.persistence.createDbDirectory.error", file));
171 
172                 break;
173             
174             default:
175             	break;
176         }
177 
178         ApplicationIOUtil.explode(fileObject,
179             target,
180             new AllFileSelector(),
181             t("quadrige2.service.persistence.extractArchive.error", file));
182 
183     }
184 
185     /**
186      * <p>checkImportStructure.</p>
187      *
188      * @param file a {@link File} object.
189      * @return a {@link ImportStructureType} object.
190      */
191     public static ImportStructureType checkImportStructure(File file) {
192         if (!file.exists()) {
193             throw new Quadrige2BusinessException(t("quadrige2.service.persistence.checkImportStructure.fileNotExist", file));
194         }
195 
196         // check zip structure
197         FileObject fileObject = ApplicationIOUtil.resolveFile(
198                 "zip:" + file.getAbsolutePath(), t("quadrige2.service.persistence.getArchive.error", file));
199 
200         // Make sure only one root directory
201         FileObject[] children = ApplicationIOUtil.getChildren(fileObject, t("quadrige2.service.persistence.openArchive.error", file));
202         if (children.length != 1) {
203             throw new Quadrige2BusinessException(t("quadrige2.service.persistence.checkImportStructure.tooManyChildren", file));
204         }
205         fileObject = children[0];
206 
207         ImportStructureType result;
208         children = ApplicationIOUtil.getChildren(fileObject, t("quadrige2.service.persistence.openArchive.error", file));
209 
210         // try to detect a db directory
211         boolean dbDirectoyDetected = false;
212         for (FileObject child : children) {
213             FileName name = child.getName();
214             if ("db".equals(name.getBaseName())) {
215                 dbDirectoyDetected = true;
216                 break;
217             }
218         }
219 
220         if (dbDirectoyDetected) {
221 
222             // NORMAL type (two directories)
223             result = ImportStructureType.NORMAL;
224 
225             checkArchiveDb(file, fileObject, "db", true);
226             checkArchiveDb(file, fileObject, "meas_files", false);
227         } else {
228 
229             // INLINE type
230             result = ImportStructureType.INLINE;
231 
232             // Make sure the .script file exists
233             boolean hasScriptFile = false;
234             for (FileObject child : children) {
235                 FileType type = ApplicationIOUtil.getType(child, "Could not get type of " + child);
236                 if (FileType.FILE.equals(type)
237                         && "script".equalsIgnoreCase(child.getName().getExtension())) {
238                     hasScriptFile = true;
239                     break;
240                 }
241             }
242             if (!hasScriptFile) {
243                 throw new Quadrige2BusinessException(t("quadrige2.service.persistence.checkImportStructure.inlineNoScriptFile"));
244             }
245         }
246 
247         if (LOG.isDebugEnabled()) {
248             LOG.debug("Database import type: " + result);
249         }
250 
251         return result;
252     }
253 
254     /**
255      * <p>checkArchiveDb.</p>
256      *
257      * @param file a {@link File} object.
258      * @param fileObject a {@link FileObject} object.
259      * @param dir a {@link String} object.
260      * @param required a boolean.
261      */
262     protected static void checkArchiveDb(File file,
263         FileObject fileObject,
264         String dir,
265         boolean required) {
266         FileObject directory = ApplicationIOUtil.getChild(fileObject, dir, t("quadrige2.service.persistence.getChild.error", dir));
267         if (directory == null) {
268 
269             String message = t("quadrige2.service.persistence.checkArchiveDb.error", file, dir);
270             if (required) {
271                 throw new Quadrige2BusinessException(message);
272             }
273 
274             if (LOG.isWarnEnabled()) {
275                 LOG.warn(message);
276             }
277         }
278     }
279 
280 }