View Javadoc
1   package fr.ifremer.quadrige3.ui.swing.content.db;
2   
3   /*-
4    * #%L
5    * Quadrige3 Core :: Quadrige3 UI Common
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  
27  import fr.ifremer.quadrige3.core.exception.Exceptions;
28  import fr.ifremer.quadrige3.core.service.ClientServiceLocator;
29  import fr.ifremer.quadrige3.core.service.persistence.PersistenceService;
30  import fr.ifremer.quadrige3.ui.swing.DialogHelper;
31  import fr.ifremer.quadrige3.ui.swing.Screen;
32  import fr.ifremer.quadrige3.ui.swing.action.AbstractChangeScreenAction;
33  import fr.ifremer.quadrige3.ui.swing.content.AbstractMainUIHandler;
34  import fr.ifremer.quadrige3.ui.swing.content.login.AuthenticationAction;
35  import fr.ifremer.quadrige3.ui.swing.synchro.action.ImportReferentialSynchroAtOnceAction;
36  import org.apache.commons.logging.Log;
37  import org.apache.commons.logging.LogFactory;
38  import org.nuiton.jaxx.application.ApplicationBusinessException;
39  import org.nuiton.jaxx.application.swing.action.ApplicationUIAction;
40  import org.nuiton.util.DateUtil;
41  import org.nuiton.version.Version;
42  import org.springframework.dao.DataIntegrityViolationException;
43  
44  import javax.swing.*;
45  import java.io.File;
46  import java.util.Date;
47  
48  import static org.nuiton.i18n.I18n.n;
49  import static org.nuiton.i18n.I18n.t;
50  
51  /**
52   * To open existing db.
53   *
54   * @since 1.0
55   */
56  public class OpenDbAction extends AbstractChangeScreenAction {
57  
58      /**
59       * Logger.
60       */
61      private static final Log LOG = LogFactory.getLog(OpenDbAction.class);
62  
63      static {
64          n("quadrige3.dbManager.action.restoreDb.step.open");
65          n("quadrige3.dbManager.action.openDb.step.open");
66          n("quadrige3.dbManager.action.restoreDb.couldNotOpen");
67          n("quadrige3.dbManager.action.openDb.couldNotOpen");
68          n("quadrige3.dbManager.action.restoreDb.step.checkSchemaVersion");
69          n("quadrige3.dbManager.action.openDb.step.checkSchemaVersion");
70          n("quadrige3.dbManager.action.restoreDb.step.close");
71          n("quadrige3.dbManager.action.openDb.step.close");
72          n("quadrige3.dbManager.action.restoreDb.step.will.migrateSchema");
73          n("quadrige3.dbManager.action.openDb.step.will.migrateSchema");
74      }
75  
76      private Version dbVersion;
77  
78      private Version applicationVersion;
79  
80      private File file;
81  
82      private boolean updateSchema;
83  
84      private boolean closeDb;
85  
86      private String jdbcUrl;
87  
88      /**
89       * indicate this open action is called by import action
90       */
91      private boolean isAfterImportDb;
92  
93      /**
94       * <p>Constructor for OpenDbAction.</p>
95       *
96       * @param handler a {@link AbstractMainUIHandler} object.
97       */
98      public OpenDbAction(AbstractMainUIHandler handler) {
99          super(handler, true, Screen.HOME);
100 
101         setActionDescription(t("quadrige3.dbManager.action.openDb.tip"));
102     }
103 
104     /**
105      * <p>setAfterImportDb.</p>
106      *
107      * @param isAfterImportDb a boolean.
108      */
109     public void setAfterImportDb(boolean isAfterImportDb) {
110         this.isAfterImportDb = isAfterImportDb;
111     }
112 
113     /** {@inheritDoc} */
114     @Override
115     public boolean prepareAction() throws Exception {
116 
117         jdbcUrl = null;
118         dbVersion = applicationVersion = null;
119         file = null;
120         closeDb = updateSchema = false;
121 
122         return super.prepareAction();
123     }
124 
125     /** {@inheritDoc} */
126     @Override
127     public void doAction() throws Exception {
128 
129         jdbcUrl = getConfig().getJdbcUrl();
130 
131         if (LOG.isDebugEnabled()) {
132             LOG.debug("Will open db: " + jdbcUrl);
133         }
134 
135         String actionName = isAfterImportDb ? "restoreDb" : "openDb";
136 
137         // at the beginning 3 steps (open db + check version + check db context)
138         createProgressionUIModel(3);
139 
140         // ------------------------------------------------------------------ //
141         // --- open db                                                        //
142         // ------------------------------------------------------------------ //
143         PersistenceService persistenceService;
144 
145         getProgressionUIModel().increments(t("quadrige3.dbManager.action." + actionName + ".step.open", jdbcUrl));
146         try {
147             getContext().setDbExist(true);
148             getContext().openPersistenceService(isAfterImportDb);
149             persistenceService = ClientServiceLocator.instance().getPersistenceService();
150 
151         } catch (Exception e) {
152 
153             if (LOG.isErrorEnabled()) {
154                 LOG.error("Could not open db", e);
155             }
156             // no more db
157             getContext().closePersistenceService();
158 
159             // could not load db
160             throw new ApplicationBusinessException(t("quadrige3.dbManager.action." + actionName + ".couldNotOpen"), e);
161         }
162 
163 
164         // ------------------------------------------------------------------ //
165         // --- check schema version                                           //
166         // ------------------------------------------------------------------ //
167         getProgressionUIModel().increments(t("quadrige3.dbManager.action." + actionName + ".step.checkSchemaVersion"));
168 
169         dbVersion = persistenceService.getDbVersion();
170         if (LOG.isDebugEnabled()) {
171             LOG.debug("Detected database version: " + (dbVersion == null ? "no version" : dbVersion));
172         }
173         applicationVersion = persistenceService.getApplicationVersion();
174 
175         if (LOG.isDebugEnabled()) {
176             LOG.debug("Detected schema version need for application: " + (applicationVersion == null ? "no version" : applicationVersion));
177         }
178 
179         if (dbVersion == null) {
180 
181             // no database version filled (can not migrate schema)
182             displayWarningMessage(t("quadrige3.dbManager.title.schema.toUpdate"), t("quadrige3.dbManager.action.upgradeDb.schema.version.not.found"));
183             closeDb = true;
184 
185         } else if (dbVersion.equals(applicationVersion)) {
186 
187             // database schema is up to date
188             if (LOG.isInfoEnabled()) {
189                 LOG.info("Database schema is up-to-date at version: " + dbVersion);
190             }
191         } else if (dbVersion.compareTo(applicationVersion) < 0) {
192 
193             // database schema need to migrate
194             // no need to ask (Mantis #46199)
195             getContext().getDialogHelper().showMessageDialog(
196                     t("quadrige3.dbManager.action.upgradeDb.schema.to.update.message", dbVersion, applicationVersion),
197                     t("quadrige3.dbManager.title.schema.toUpdate"),
198                     DialogHelper.UPDATE_DB_OPTION);
199 
200             if (isAfterImportDb || getContext().isDbJustInstalled()) {
201 
202                 // will migrate from import
203                 updateSchema = true;
204             } else {
205 
206                 // ask user where to backup db
207                 String date = DateUtil.formatDate(new Date(), "yyyy-MM-dd");
208 
209                 // ask user file where to backup db
210                 file = saveFile(
211                         getConfig().getDbBackupDirectory(),
212                         String.format("%s-db-%s", getConfig().getApplicationName(), date),
213                         "zip",
214                         t("quadrige3.dbManager.title.choose.dbBackupFile"),
215                         t("quadrige3.dbManager.action.chooseDbBackupFile"),
216                         "^.*\\.zip", t("quadrige3.common.file.zip")
217                 );
218 
219                 if (file == null) {
220 
221                     // won't migrate db
222                     closeDb = true;
223 
224                     displayWarningMessage(
225                             t("quadrige3.dbManager.title.choose.dbBackupFile"),
226                             t("quadrige3.dbManager.action.upgradeDb.no.backup.db.choosen"));
227                 } else {
228 
229                     updateSchema = true;
230                 }
231             }
232         } else {
233 
234             // database schema version is higher than application one
235             if (isAfterImportDb) {
236 
237                 int i = getContext().getDialogHelper().showConfirmDialog(
238                         t("quadrige3.dbManager.action.upgradeDb.schema.too.high", dbVersion, applicationVersion),
239                         t("quadrige3.dbManager.title.schema.toUpdate"),
240                         DialogHelper.LOAD_DB_CANCEL_OPTION);
241                 boolean continueAction = i == JOptionPane.OK_OPTION;
242 
243                 if (!continueAction) {
244                     //close db
245                     closeDb = true;
246                 }
247             } else {
248                 displayWarningMessage(
249                         t("quadrige3.dbManager.title.schema.toUpdate"),
250                         t("quadrige3.dbManager.action.upgradeDb.schema.not.update.message", dbVersion, applicationVersion)
251                 );
252             }
253         }
254         if (closeDb) {
255 
256             // ------------------------------------------------------------------ //
257             // --- close current db                                               //
258             // ------------------------------------------------------------------ //
259             getProgressionUIModel().increments(t("quadrige3.dbManager.action." + actionName + ".step.close"));
260             getActionEngine().runInternalAction(getHandler(), CloseDbAction.class);
261 
262             if (!isAfterImportDb) {
263                 setScreen(Screen.MANAGE_DB);
264                 super.doAction();
265             }
266             return;
267         }
268 
269         if (updateSchema) {
270 
271             // need to export db + migrate schema)
272             getProgressionUIModel().adaptTotal(getProgressionUIModel().getTotal() + 2);
273 
274             if (!isAfterImportDb && file != null) {
275                 // ------------------------------------------------------------------ //
276                 // --- backup current db                                              //
277                 // ------------------------------------------------------------------ //
278                 ApplicationUIAction<BackupDbAction> backupAction = getActionFactory().createUIAction(getHandler(), BackupDbAction.class);
279                 backupAction.getLogicAction().setBackupFile(file);
280                 getActionEngine().runInternalAction(backupAction.getLogicAction());
281             }
282 
283             // ------------------------------------------------------------------ //
284             // --- update schema                                                  //
285             // ------------------------------------------------------------------ //
286             String message = t("quadrige3.dbManager.action." + actionName + ".step.will.migrateSchema", dbVersion, applicationVersion);
287             getProgressionUIModel().increments(message);
288             sendMessage(message);
289 
290             try {
291                 ClientServiceLocator.instance().getPersistenceService().updateSchema();
292             } catch (Exception e) {
293               if (Exceptions.hasCause(e, DataIntegrityViolationException.class)) {
294                   DataIntegrityViolationException integrityViolationException = (DataIntegrityViolationException) Exceptions.getCause(e, DataIntegrityViolationException.class);
295                   if (integrityViolationException != null && integrityViolationException.getMessage().contains("CHECKPOINT DEFRAG")) {
296                       // Need db repair (Mantis #58885)
297                       message = t("quadrige3.dbManager.action.repairDb");
298                       getProgressionUIModel().increments(message);
299                       sendMessage(message);
300                       // Close db with compact (=will repair)
301                       getContext().closePersistenceService(true, false);
302                       getContext().clearDbContext();
303                       // Re-open db
304                       getContext().openPersistenceService(isAfterImportDb);
305                   }
306               }
307             }
308 
309 
310             sendMessage(t("quadrige3.flash.info.db.schema.updated", dbVersion, applicationVersion));
311         }
312 
313         // ------------------------------------------------------------------ //
314         // --- Authentication                                                 //
315         // ------------------------------------------------------------------ //
316         if (getContext().isPersistenceLoaded() && !getContext().isAuthenticated()) {
317 
318             getProgressionUIModel().increments(t("quadrige3.dbManager.action.openDb.step.authenticate"));
319 
320             AuthenticationAction action = new AuthenticationAction(getHandler());
321             action.setSkipScreenReload(true);
322             getActionEngine().runInternalAction(action);
323 
324         }
325 
326         // ----------------------------------------------------------------------------- //
327         // --- Perform referential updates before checkDbContext (see mantis #30764) --- //
328         // ----------------------------------------------------------------------------- //
329         if (getContext().isSynchroEnabled() && getContext().isDbJustInstalled()) {
330             ImportReferentialSynchroAtOnceAction importAction = getContext().getActionFactory().createLogicAction(getHandler(), ImportReferentialSynchroAtOnceAction.class);
331             getContext().getActionEngine().runInternalAction(importAction);
332         }
333 
334         // ------------------------------------------------------------------ //
335         // --- check db context                                               //
336         // ------------------------------------------------------------------ //
337         getProgressionUIModel().increments(t("quadrige3.dbManager.action.openDb.step.check.dbContext", dbVersion, applicationVersion));
338 
339         if (LOG.isDebugEnabled()) {
340             LOG.debug("Check db context");
341         }
342 
343         getContext().setDbJustImportedFromFile(isAfterImportDb);
344 
345         // Prepare the persistence layer
346         getContext().checkDbContext(getProgressionUIModel());
347 
348         super.doAction();
349     }
350 
351     /** {@inheritDoc} */
352     @Override
353     public void postSuccessAction() {
354 
355         getHandler().reloadDbManagerText();
356 
357         // make sure title is reloaded
358         getHandler().changeTitle();
359 
360         if (!isAfterImportDb) {
361             if (closeDb) {
362                 sendMessage(t("quadrige3.flash.info.db.not.opened", jdbcUrl));
363             } else {
364                 sendMessage(t("quadrige3.flash.info.db.opened", jdbcUrl));
365             }
366         }
367     }
368 
369     /** {@inheritDoc} */
370     @Override
371     public void postFailedAction(Throwable error) {
372 
373         getHandler().reloadDbManagerText();
374 
375         super.postFailedAction(error);
376     }
377 }