Line 21... |
Line 21... |
21 |
import org.openconcerto.erp.modules.ModuleTableModel.ModuleRow;
|
21 |
import org.openconcerto.erp.modules.ModuleTableModel.ModuleRow;
|
22 |
import org.openconcerto.sql.Configuration;
|
22 |
import org.openconcerto.sql.Configuration;
|
23 |
import org.openconcerto.sql.TM;
|
23 |
import org.openconcerto.sql.TM;
|
24 |
import org.openconcerto.sql.element.SQLElement;
|
24 |
import org.openconcerto.sql.element.SQLElement;
|
25 |
import org.openconcerto.sql.element.SQLElementDirectory;
|
25 |
import org.openconcerto.sql.element.SQLElementDirectory;
|
- |
|
26 |
import org.openconcerto.sql.element.SQLElementNamesFromXML;
|
26 |
import org.openconcerto.sql.model.AliasedTable;
|
27 |
import org.openconcerto.sql.model.AliasedTable;
|
27 |
import org.openconcerto.sql.model.ConnectionHandlerNoSetup;
|
28 |
import org.openconcerto.sql.model.ConnectionHandlerNoSetup;
|
28 |
import org.openconcerto.sql.model.DBFileCache;
|
29 |
import org.openconcerto.sql.model.DBFileCache;
|
29 |
import org.openconcerto.sql.model.DBItemFileCache;
|
30 |
import org.openconcerto.sql.model.DBItemFileCache;
|
30 |
import org.openconcerto.sql.model.DBRoot;
|
31 |
import org.openconcerto.sql.model.DBRoot;
|
Line 55... |
Line 56... |
55 |
import org.openconcerto.sql.view.list.IListeAction;
|
56 |
import org.openconcerto.sql.view.list.IListeAction;
|
56 |
import org.openconcerto.ui.SwingThreadUtils;
|
57 |
import org.openconcerto.ui.SwingThreadUtils;
|
57 |
import org.openconcerto.utils.CollectionMap2.Mode;
|
58 |
import org.openconcerto.utils.CollectionMap2.Mode;
|
58 |
import org.openconcerto.utils.CollectionUtils;
|
59 |
import org.openconcerto.utils.CollectionUtils;
|
59 |
import org.openconcerto.utils.ExceptionHandler;
|
60 |
import org.openconcerto.utils.ExceptionHandler;
|
- |
|
61 |
import org.openconcerto.utils.ExceptionUtils;
|
60 |
import org.openconcerto.utils.FileUtils;
|
62 |
import org.openconcerto.utils.FileUtils;
|
61 |
import org.openconcerto.utils.SetMap;
|
63 |
import org.openconcerto.utils.SetMap;
|
62 |
import org.openconcerto.utils.StringUtils;
|
64 |
import org.openconcerto.utils.StringUtils;
|
63 |
import org.openconcerto.utils.ThreadFactory;
|
65 |
import org.openconcerto.utils.ThreadFactory;
|
64 |
import org.openconcerto.utils.Tuple2;
|
66 |
import org.openconcerto.utils.Tuple2;
|
Line 75... |
Line 77... |
75 |
import java.io.FileFilter;
|
77 |
import java.io.FileFilter;
|
76 |
import java.io.FileInputStream;
|
78 |
import java.io.FileInputStream;
|
77 |
import java.io.FileOutputStream;
|
79 |
import java.io.FileOutputStream;
|
78 |
import java.io.IOException;
|
80 |
import java.io.IOException;
|
79 |
import java.io.InputStream;
|
81 |
import java.io.InputStream;
|
- |
|
82 |
import java.nio.charset.StandardCharsets;
|
- |
|
83 |
import java.nio.file.Files;
|
- |
|
84 |
import java.nio.file.Path;
|
- |
|
85 |
import java.nio.file.StandardCopyOption;
|
80 |
import java.sql.SQLException;
|
86 |
import java.sql.SQLException;
|
81 |
import java.util.ArrayList;
|
87 |
import java.util.ArrayList;
|
82 |
import java.util.Arrays;
|
88 |
import java.util.Arrays;
|
83 |
import java.util.Collection;
|
89 |
import java.util.Collection;
|
84 |
import java.util.Collections;
|
90 |
import java.util.Collections;
|
Line 133... |
Line 139... |
133 |
@GuardedBy("ModuleManager.class")
|
139 |
@GuardedBy("ModuleManager.class")
|
134 |
private static ExecutorService exec = null;
|
140 |
private static ExecutorService exec = null;
|
135 |
|
141 |
|
136 |
private static synchronized final Executor getExec() {
|
142 |
private static synchronized final Executor getExec() {
|
137 |
if (exec == null)
|
143 |
if (exec == null)
|
138 |
exec = new ThreadPoolExecutor(0, 2, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
|
144 |
exec = new ThreadPoolExecutor(0, 2, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new ThreadFactory(ModuleManager.class.getSimpleName()
|
139 |
new ThreadFactory(ModuleManager.class.getSimpleName()
|
- |
|
140 |
// not daemon since install() is not atomic
|
145 |
// not daemon since install() is not atomic
|
141 |
+ " executor thread ", false));
|
146 |
+ " executor thread ", false));
|
142 |
return exec;
|
147 |
return exec;
|
143 |
}
|
148 |
}
|
144 |
|
149 |
|
Line 170... |
Line 175... |
170 |
private static final String NEEDED_MODULE_COLNAME = "ID_MODULE_NEEDED";
|
175 |
private static final String NEEDED_MODULE_COLNAME = "ID_MODULE_NEEDED";
|
171 |
private static final String fileMutex = new String("modules");
|
176 |
private static final String fileMutex = new String("modules");
|
172 |
|
177 |
|
173 |
private static final Integer TO_INSTALL_VERSION = 1;
|
178 |
private static final Integer TO_INSTALL_VERSION = 1;
|
174 |
|
179 |
|
175 |
@GuardedBy("ModuleManager.class")
|
- |
|
176 |
private static ModuleManager instance = null;
|
180 |
private static final String OLD_BACKUP_DIR_SUFFIX = ".backup";
|
177 |
|
- |
|
178 |
public static synchronized ModuleManager getInstance() {
|
181 |
private static final String OLD_FAILED_DIR_SUFFIX = ".failed";
|
179 |
if (instance == null)
|
- |
|
180 |
instance = new ModuleManager();
|
- |
|
181 |
return instance;
|
- |
|
182 |
}
|
- |
|
183 |
|
- |
|
184 |
static synchronized void resetInstance() {
|
182 |
private static final String BACKUP_DIR = "Install backup";
|
185 |
if (instance != null) {
|
- |
|
186 |
for (final String id : instance.getRunningModules().keySet()) {
|
183 |
private static final String FAILED_DIR = "Install failed";
|
187 |
instance.stopModuleRecursively(id);
|
- |
|
188 |
}
|
- |
|
189 |
instance = null;
|
- |
|
190 |
}
|
- |
|
191 |
}
|
- |
|
192 |
|
184 |
|
193 |
// return true if the MainFrame is not displayable (or if there's none)
|
185 |
// return true if the MainFrame is not displayable (or if there's none)
|
194 |
static private boolean noDisplayableFrame() {
|
186 |
static private boolean noDisplayableFrame() {
|
195 |
final MainFrame mf = MainFrame.getInstance();
|
187 |
final MainFrame mf = MainFrame.getInstance();
|
196 |
if (mf == null)
|
188 |
if (mf == null)
|
Line 503... |
Line 495... |
503 |
ModuleState toInstallTargetState = ModuleState.NOT_CREATED;
|
495 |
ModuleState toInstallTargetState = ModuleState.NOT_CREATED;
|
504 |
if (toInstallFile.exists()) {
|
496 |
if (toInstallFile.exists()) {
|
505 |
if (!toInstallFile.canRead() || !toInstallFile.isFile()) {
|
497 |
if (!toInstallFile.canRead() || !toInstallFile.isFile()) {
|
506 |
L.warning("Couldn't read " + toInstallFile);
|
498 |
L.warning("Couldn't read " + toInstallFile);
|
507 |
} else {
|
499 |
} else {
|
508 |
final XMLDecoder dec = new XMLDecoder(new FileInputStream(toInstallFile));
|
500 |
try (final XMLDecoder dec = new XMLDecoder(new FileInputStream(toInstallFile))) {
|
509 |
try {
|
- |
|
510 |
final Number version = (Number) dec.readObject();
|
501 |
final Number version = (Number) dec.readObject();
|
511 |
if (version.intValue() != TO_INSTALL_VERSION.intValue())
|
502 |
if (version.intValue() != TO_INSTALL_VERSION.intValue())
|
512 |
throw new Exception("Version mismatch, expected " + TO_INSTALL_VERSION + " found " + version);
|
503 |
throw new Exception("Version mismatch, expected " + TO_INSTALL_VERSION + " found " + version);
|
513 |
final Date fileDate = (Date) dec.readObject();
|
504 |
final Date fileDate = (Date) dec.readObject();
|
514 |
@SuppressWarnings("unchecked")
|
505 |
@SuppressWarnings("unchecked")
|
Line 543... |
Line 534... |
543 |
final File errorFile = FileUtils.addSuffix(toInstallFile, ".error");
|
534 |
final File errorFile = FileUtils.addSuffix(toInstallFile, ".error");
|
544 |
errorFile.delete();
|
535 |
errorFile.delete();
|
545 |
final boolean renamed = toInstallFile.renameTo(errorFile);
|
536 |
final boolean renamed = toInstallFile.renameTo(errorFile);
|
546 |
throw new Exception("Couldn't parse " + toInstallFile + " ; renamed : " + renamed, e);
|
537 |
throw new Exception("Couldn't parse " + toInstallFile + " ; renamed : " + renamed, e);
|
547 |
} finally {
|
538 |
} finally {
|
- |
|
539 |
// Either the installation will succeed and we don't want to execute it again,
|
- |
|
540 |
// or the installation will fail and we don't want to be caught in an endless
|
- |
|
541 |
// loop (since there's no API to remove this file).
|
548 |
dec.close();
|
542 |
if (toInstallFile.exists() && !toInstallFile.delete())
|
- |
|
543 |
throw new IOException("Couldn't delete " + toInstallFile);
|
549 |
}
|
544 |
}
|
550 |
}
|
545 |
}
|
551 |
}
|
546 |
}
|
552 |
// handle upgrades : the old version was not uninstalled and thus still in
|
547 |
// handle upgrades : the old version was not uninstalled and thus still in
|
553 |
// dbRequiredModules, and the new version is in toInstall, so the DepSolver will error out
|
548 |
// dbRequiredModules, and the new version is in toInstall, so the DepSolver will error out
|
Line 577... |
Line 572... |
577 |
} else {
|
572 |
} else {
|
578 |
// NO_CHANGE since they were just installed above
|
573 |
// NO_CHANGE since they were just installed above
|
579 |
this.createModules(userReferencesToInstall, NoChoicePredicate.NO_CHANGE, ModuleState.STARTED, persistent);
|
574 |
this.createModules(userReferencesToInstall, NoChoicePredicate.NO_CHANGE, ModuleState.STARTED, persistent);
|
580 |
}
|
575 |
}
|
581 |
}
|
576 |
}
|
582 |
if (toInstallFile.exists() && !toInstallFile.delete())
|
- |
|
583 |
throw new IOException("Couldn't delete " + toInstallFile);
|
- |
|
584 |
this.inited = true;
|
577 |
this.inited = true;
|
585 |
}
|
578 |
}
|
586 |
|
579 |
|
587 |
// whether module removal can exit the VM
|
580 |
// whether module removal can exit the VM
|
588 |
final void setExitAllowed(boolean exitAllowed) {
|
581 |
final void setExitAllowed(boolean exitAllowed) {
|
Line 645... |
Line 638... |
645 |
private final Preferences getRequiredIDsPrefs() {
|
638 |
private final Preferences getRequiredIDsPrefs() {
|
646 |
return getDBPrefs().node("required");
|
639 |
return getDBPrefs().node("required");
|
647 |
}
|
640 |
}
|
648 |
|
641 |
|
649 |
protected final boolean isModuleInstalledLocally(String id) {
|
642 |
protected final boolean isModuleInstalledLocally(String id) {
|
- |
|
643 |
final File versionFile = getLocalVersionFile(id);
|
650 |
return getLocalVersionFile(id).exists();
|
644 |
return versionFile != null && versionFile.exists();
|
651 |
}
|
645 |
}
|
652 |
|
646 |
|
653 |
protected final ModuleVersion getModuleVersionInstalledLocally(String id) {
|
647 |
protected final ModuleVersion getModuleVersionInstalledLocally(String id) {
|
654 |
synchronized (fileMutex) {
|
648 |
synchronized (fileMutex) {
|
655 |
final File versionFile = getLocalVersionFile(id);
|
649 |
final File versionFile = getLocalVersionFile(id);
|
656 |
if (versionFile.exists()) {
|
650 |
if (versionFile != null && versionFile.exists()) {
|
657 |
try {
|
651 |
try {
|
658 |
return new ModuleVersion(Long.valueOf(FileUtils.read(versionFile)));
|
652 |
return new ModuleVersion(Long.valueOf(FileUtils.read(versionFile)));
|
659 |
} catch (IOException e) {
|
653 |
} catch (IOException e) {
|
660 |
throw new IllegalStateException("Couldn't get installed version of " + id, e);
|
654 |
throw new IllegalStateException("Couldn't get installed version of " + id, e);
|
661 |
}
|
655 |
}
|
Line 777... |
Line 771... |
777 |
|
771 |
|
778 |
private SQLElementDirectory getDirectory() {
|
772 |
private SQLElementDirectory getDirectory() {
|
779 |
return getConf().getDirectory();
|
773 |
return getConf().getDirectory();
|
780 |
}
|
774 |
}
|
781 |
|
775 |
|
782 |
final File getLocalDirectory() {
|
776 |
public final File getLocalDirectory() {
|
783 |
return new File(this.getConf().getConfDir(getRoot()), "modules");
|
777 |
return new File(this.getConf().getConfDir(getRoot()), "modules");
|
784 |
}
|
778 |
}
|
785 |
|
779 |
|
- |
|
780 |
public final Set<String> migrateOldTransientDirs() {
|
- |
|
781 |
Set<String> res = Collections.emptySet();
|
- |
|
782 |
final File[] listFiles = this.getLocalDirectory().listFiles();
|
- |
|
783 |
if (listFiles != null) {
|
- |
|
784 |
for (final File f : listFiles) {
|
- |
|
785 |
if (f.isDirectory()) {
|
- |
|
786 |
res = migrateOldDir(res, f, OLD_BACKUP_DIR_SUFFIX);
|
- |
|
787 |
res = migrateOldDir(res, f, OLD_FAILED_DIR_SUFFIX);
|
- |
|
788 |
}
|
- |
|
789 |
}
|
- |
|
790 |
}
|
- |
|
791 |
return res;
|
- |
|
792 |
}
|
- |
|
793 |
|
- |
|
794 |
private final String getOldID(final File f, final String oldSuffix) {
|
- |
|
795 |
if (f.getName().endsWith(oldSuffix)) {
|
- |
|
796 |
final String id = f.getName().substring(0, f.getName().length() - oldSuffix.length());
|
- |
|
797 |
return ModuleReference.checkID(id, false);
|
- |
|
798 |
}
|
- |
|
799 |
return null;
|
- |
|
800 |
}
|
- |
|
801 |
|
- |
|
802 |
private final Set<String> migrateOldDir(Set<String> res, final File f, final String oldSuffix) {
|
- |
|
803 |
final String id = getOldID(f, oldSuffix);
|
- |
|
804 |
if (id != null) {
|
- |
|
805 |
final Path localFailedDir = getLocalFailedDirectory(id);
|
- |
|
806 |
try {
|
- |
|
807 |
if (Files.exists(localFailedDir)) {
|
- |
|
808 |
// newer already exists, remove old one
|
- |
|
809 |
FileUtils.rm_R(f);
|
- |
|
810 |
} else {
|
- |
|
811 |
Files.createDirectories(localFailedDir.getParent());
|
- |
|
812 |
Files.move(f.toPath(), localFailedDir);
|
- |
|
813 |
}
|
- |
|
814 |
} catch (IOException e) {
|
- |
|
815 |
L.log(Level.CONFIG, "Couldn't migrate " + f, e);
|
- |
|
816 |
if (res.isEmpty())
|
- |
|
817 |
res = new HashSet<>();
|
- |
|
818 |
res.add(id);
|
- |
|
819 |
}
|
- |
|
820 |
}
|
- |
|
821 |
return res;
|
- |
|
822 |
}
|
- |
|
823 |
|
786 |
// file specifying which module (and only those, dependencies won't be installed automatically)
|
824 |
// file specifying which module (and only those, dependencies won't be installed automatically)
|
787 |
// to install during the next application launch.
|
825 |
// to install during the next application launch.
|
788 |
private final File getToInstallFile() {
|
826 |
private final File getToInstallFile() {
|
789 |
return new File(getLocalDirectory(), "toInstall");
|
827 |
return new File(getLocalDirectory(), "toInstall");
|
790 |
}
|
828 |
}
|
791 |
|
829 |
|
- |
|
830 |
private final File getLocalDataSubDir(final File f) {
|
- |
|
831 |
// TODO return "Local Data" and a migration method
|
- |
|
832 |
return f;
|
- |
|
833 |
}
|
- |
|
834 |
|
- |
|
835 |
// => create "internal state" and "local data" folders inside getLocalDirectory() (i.e.
|
- |
|
836 |
// module.id/local data and not local data/module.id so that we can easily copy to
|
- |
|
837 |
// getLocalBackupDirectory() or uninstall a module)
|
- |
|
838 |
|
- |
|
839 |
private final Path getInternalStateSubDir(final Path f) {
|
- |
|
840 |
// TODO return "Internal State" and a migration method
|
- |
|
841 |
return f;
|
- |
|
842 |
}
|
- |
|
843 |
|
792 |
protected final File getLocalDirectory(final String id) {
|
844 |
protected final File getLocalDataDirectory(final String id) {
|
- |
|
845 |
return getLocalDataSubDir(this.getLocalDirectory(id));
|
- |
|
846 |
}
|
- |
|
847 |
|
- |
|
848 |
private final Path getInternalStateDirectory(final String id) {
|
- |
|
849 |
final File l = this.getLocalDirectory(id);
|
- |
|
850 |
return l == null ? null : getInternalStateSubDir(l.toPath());
|
- |
|
851 |
}
|
- |
|
852 |
|
- |
|
853 |
private final File getLocalDirectory(final String id) {
|
- |
|
854 |
if (ModuleReference.checkID(id, false) == null)
|
- |
|
855 |
return null;
|
793 |
return new File(this.getLocalDirectory(), id);
|
856 |
return new File(this.getLocalDirectory(), id);
|
794 |
}
|
857 |
}
|
795 |
|
858 |
|
- |
|
859 |
// contains a copy of local module data during install
|
- |
|
860 |
protected final Path getLocalBackupDirectory(final String id) {
|
796 |
// TODO module might remove it since it's in getLocalDirectory()
|
861 |
// will be ignored by getModulesVersionInstalledLocally()
|
- |
|
862 |
assert ModuleReference.checkID(BACKUP_DIR, false) == null;
|
- |
|
863 |
return this.getLocalDirectory().toPath().resolve(BACKUP_DIR).resolve(id);
|
- |
|
864 |
}
|
- |
|
865 |
|
- |
|
866 |
// contains local module data of the last failed install
|
- |
|
867 |
protected final Path getLocalFailedDirectory(final String id) {
|
- |
|
868 |
// will be ignored by getModulesVersionInstalledLocally()
|
- |
|
869 |
assert ModuleReference.checkID(FAILED_DIR, false) == null;
|
- |
|
870 |
return this.getLocalDirectory().toPath().resolve(FAILED_DIR).resolve(id);
|
- |
|
871 |
}
|
- |
|
872 |
|
797 |
private final File getLocalVersionFile(final String id) {
|
873 |
private final File getLocalVersionFile(final String id) {
|
- |
|
874 |
final Path dir = this.getInternalStateDirectory(id);
|
- |
|
875 |
if (dir == null)
|
- |
|
876 |
return null;
|
798 |
return new File(this.getLocalDirectory(id), "version");
|
877 |
return new File(dir.toFile(), "version");
|
799 |
}
|
878 |
}
|
800 |
|
879 |
|
801 |
public final ModuleVersion getDBInstalledModuleVersion(final String id) throws SQLException {
|
880 |
public final ModuleVersion getDBInstalledModuleVersion(final String id) throws SQLException {
|
802 |
return getDBInstalledModules(id).get(id);
|
881 |
return getDBInstalledModules(id).get(id);
|
803 |
}
|
882 |
}
|
Line 1020... |
Line 1099... |
1020 |
// rename it failed
|
1099 |
// rename it failed
|
1021 |
// 2. copy dir to a backup, pass dir to DBContext, then either remove backup or rename
|
1100 |
// 2. copy dir to a backup, pass dir to DBContext, then either remove backup or rename
|
1022 |
// it to dir
|
1101 |
// it to dir
|
1023 |
// Choice 2 is simpler since the module deals with the same directory in both install()
|
1102 |
// Choice 2 is simpler since the module deals with the same directory in both install()
|
1024 |
// and start()
|
1103 |
// and start()
|
1025 |
final File backupDir;
|
1104 |
final Path backupDir;
|
1026 |
// check if we need a backup
|
1105 |
// check if we need a backup
|
1027 |
if (localDir.exists()) {
|
1106 |
if (localDir.exists()) {
|
1028 |
backupDir = FileUtils.addSuffix(localDir, ".backup");
|
1107 |
backupDir = getLocalBackupDirectory(factory.getID());
|
1029 |
FileUtils.rm_R(backupDir);
|
1108 |
FileUtils.rm_R(backupDir, false);
|
1030 |
FileUtils.copyDirectory(localDir, backupDir);
|
1109 |
Files.createDirectories(backupDir.getParent());
|
- |
|
1110 |
FileUtils.copyDirectory(localDir.toPath(), backupDir, false, StandardCopyOption.COPY_ATTRIBUTES);
|
1031 |
} else {
|
1111 |
} else {
|
1032 |
backupDir = null;
|
1112 |
backupDir = null;
|
1033 |
FileUtils.mkdir_p(localDir);
|
1113 |
FileUtils.mkdir_p(localDir);
|
1034 |
}
|
1114 |
}
|
1035 |
assert localDir.exists();
|
1115 |
assert localDir.exists();
|
1036 |
try {
|
1116 |
try {
|
1037 |
SQLUtils.executeAtomic(getDS(), new ConnectionHandlerNoSetup<Object, IOException>() {
|
1117 |
SQLUtils.executeAtomic(getDS(), new ConnectionHandlerNoSetup<Object, IOException>() {
|
1038 |
@Override
|
1118 |
@Override
|
1039 |
public Object handle(SQLDataSource ds) throws SQLException, IOException {
|
1119 |
public Object handle(SQLDataSource ds) throws SQLException, IOException {
|
1040 |
final Tuple2<Set<String>, Set<SQLName>> alreadyCreatedItems = getCreatedItems(factory.getID());
|
1120 |
final Tuple2<Set<String>, Set<SQLName>> alreadyCreatedItems = getCreatedItems(factory.getID());
|
1041 |
final DBContext ctxt = new DBContext(localDir, localVersion, getRoot(), lastInstalledVersion, alreadyCreatedItems.get0(), alreadyCreatedItems.get1(), getDirectory());
|
1121 |
final DBContext ctxt = new DBContext(getLocalDataSubDir(localDir), localVersion, getRoot(), lastInstalledVersion, alreadyCreatedItems.get0(), alreadyCreatedItems.get1(),
|
- |
|
1122 |
getDirectory());
|
1042 |
// install local (i.e. ctxt stores the actions to carry on the DB)
|
1123 |
// install local (i.e. ctxt stores the actions to carry on the DB)
|
1043 |
// TODO pass a data source with no rights to modify the data definition (or
|
1124 |
// TODO pass a data source with no rights to modify the data definition (or
|
1044 |
// even no rights to modify the data if DB version is up to date)
|
1125 |
// even no rights to modify the data if DB version is up to date)
|
1045 |
module.install(ctxt);
|
1126 |
module.install(ctxt);
|
1046 |
if (!localDir.exists())
|
1127 |
if (!localDir.exists())
|
Line 1054... |
Line 1135... |
1054 |
} catch (Exception e) {
|
1135 |
} catch (Exception e) {
|
1055 |
// install did not complete successfully
|
1136 |
// install did not complete successfully
|
1056 |
if (getRoot().getServer().getSQLSystem() == SQLSystem.MYSQL)
|
1137 |
if (getRoot().getServer().getSQLSystem() == SQLSystem.MYSQL)
|
1057 |
L.warning("MySQL cannot rollback DDL statements");
|
1138 |
L.warning("MySQL cannot rollback DDL statements");
|
1058 |
// keep failed install files and restore previous files
|
1139 |
// keep failed install files and restore previous files
|
1059 |
final File failed = FileUtils.addSuffix(localDir, ".failed");
|
1140 |
final Path failed = getLocalFailedDirectory(factory.getID());
|
- |
|
1141 |
boolean moved = false;
|
- |
|
1142 |
try {
|
1060 |
if (failed.exists() && !FileUtils.rmR(failed))
|
1143 |
FileUtils.rm_R(failed, false);
|
1061 |
L.warning("Couldn't remove " + failed);
|
1144 |
Files.createDirectories(failed.getParent());
|
1062 |
if (!localDir.renameTo(failed)) {
|
1145 |
Files.move(localDir.toPath(), failed);
|
1063 |
L.warning("Couldn't move " + localDir + " to " + failed);
|
1146 |
final String errorMsg = "Couldn't install " + module + " :\n" + ExceptionUtils.getStackTrace(e);
|
- |
|
1147 |
// TODO as in getLocalVersionFile(), separate internal state (i.e. version and
|
- |
|
1148 |
// error) from module local data
|
- |
|
1149 |
Files.write(getInternalStateSubDir(failed).resolve("Install error.txt"), errorMsg.getBytes(StandardCharsets.UTF_8));
|
1064 |
} else {
|
1150 |
moved = true;
|
1065 |
assert !localDir.exists();
|
1151 |
} catch (Exception e1) {
|
- |
|
1152 |
L.log(Level.WARNING, "Couldn't move " + localDir + " to " + failed, e1);
|
- |
|
1153 |
}
|
1066 |
// restore if needed
|
1154 |
// restore if needed
|
- |
|
1155 |
if (moved && backupDir != null) {
|
- |
|
1156 |
assert !localDir.exists();
|
- |
|
1157 |
try {
|
1067 |
if (backupDir != null && !backupDir.renameTo(localDir))
|
1158 |
Files.move(backupDir, localDir.toPath());
|
- |
|
1159 |
} catch (Exception e1) {
|
1068 |
L.warning("Couldn't restore " + backupDir + " to " + localDir);
|
1160 |
L.log(Level.WARNING, "Couldn't restore " + backupDir + " to " + localDir, e1);
|
- |
|
1161 |
}
|
1069 |
}
|
1162 |
}
|
1070 |
throw e;
|
1163 |
throw e;
|
1071 |
}
|
1164 |
}
|
1072 |
// DB transaction was committed, remove backup files
|
1165 |
// DB transaction was committed, remove backup files
|
1073 |
assert localDir.exists();
|
1166 |
assert localDir.exists();
|
Line 1081... |
Line 1174... |
1081 |
private void registerSQLElements(final AbstractModule module, Map<SQLTable, SQLElement> beforeElements) throws IOException {
|
1174 |
private void registerSQLElements(final AbstractModule module, Map<SQLTable, SQLElement> beforeElements) throws IOException {
|
1082 |
final ModuleReference id = module.getFactory().getReference();
|
1175 |
final ModuleReference id = module.getFactory().getReference();
|
1083 |
synchronized (this.modulesElements) {
|
1176 |
synchronized (this.modulesElements) {
|
1084 |
// perhaps check that no other version of the module has been registered
|
1177 |
// perhaps check that no other version of the module has been registered
|
1085 |
if (!this.modulesElements.containsKey(id)) {
|
1178 |
if (!this.modulesElements.containsKey(id)) {
|
1086 |
final String mdVariant = getMDVariant(module.getFactory());
|
- |
|
1087 |
// load now so that it's available to ModuleElement in setupElements()
|
- |
|
1088 |
final Set<SQLTable> tablesWithMD = loadTranslations(getConf().getTranslator(), module, mdVariant);
|
- |
|
1089 |
|
- |
|
1090 |
final SQLElementDirectory dir = getDirectory();
|
1179 |
final SQLElementDirectory dir = getDirectory();
|
1091 |
module.setupElements(dir);
|
1180 |
module.setupElements(dir);
|
1092 |
final IdentityHashMap<SQLElement, SQLElement> elements = new IdentityHashMap<SQLElement, SQLElement>();
|
1181 |
final IdentityHashMap<SQLElement, SQLElement> elements = new IdentityHashMap<SQLElement, SQLElement>();
|
1093 |
// use IdentitySet so as not to call equals() since it triggers initFF()
|
1182 |
// use IdentitySet so as not to call equals() since it triggers initFF()
|
1094 |
final IdentitySet<SQLElement> beforeElementsSet = new IdentityHashSet<SQLElement>(beforeElements.values());
|
1183 |
final IdentitySet<SQLElement> beforeElementsSet = new IdentityHashSet<SQLElement>(beforeElements.values());
|
Line 1123... |
Line 1212... |
1123 |
elements.put(elem, null);
|
1212 |
elements.put(elem, null);
|
1124 |
}
|
1213 |
}
|
1125 |
}
|
1214 |
}
|
1126 |
}
|
1215 |
}
|
1127 |
|
1216 |
|
- |
|
1217 |
// Load translations after registering elements since SQLFieldTranslator needs them.
|
- |
|
1218 |
// If setupElements() needs translations then perhaps we should store translations
|
- |
|
1219 |
// with the element code, without needing the element.
|
- |
|
1220 |
final String mdVariant = getMDVariant(module.getFactory());
|
- |
|
1221 |
final Set<SQLTable> tablesWithMD = loadTranslations(getConf().getTranslator(), module, mdVariant);
|
- |
|
1222 |
|
1128 |
// insert just loaded labels into the search path
|
1223 |
// insert just loaded labels into the search path
|
1129 |
for (final SQLTable tableWithDoc : tablesWithMD) {
|
1224 |
for (final SQLTable tableWithDoc : tablesWithMD) {
|
1130 |
final SQLElement sqlElem = this.getDirectory().getElement(tableWithDoc);
|
1225 |
final SQLElement sqlElem = this.getDirectory().getElement(tableWithDoc);
|
1131 |
if (sqlElem == null)
|
1226 |
if (sqlElem == null)
|
1132 |
throw new IllegalStateException("Missing element for table with metadata : " + tableWithDoc);
|
1227 |
throw new IllegalStateException("Missing element for table with metadata : " + tableWithDoc);
|
Line 1544... |
Line 1639... |
1544 |
}
|
1639 |
}
|
1545 |
|
1640 |
|
1546 |
// call it before stopping/uninstalling
|
1641 |
// call it before stopping/uninstalling
|
1547 |
final boolean exit = this.isExitAllowed() && this.needExit(change);
|
1642 |
final boolean exit = this.isExitAllowed() && this.needExit(change);
|
1548 |
|
1643 |
|
- |
|
1644 |
final DepSolverGraph graph = change.getGraph();
|
1549 |
final Set<ModuleReference> toRemove = change.getReferencesToRemove();
|
1645 |
final Set<ModuleReference> toRemove = change.getReferencesToRemove();
|
1550 |
final Set<ModuleReference> removed;
|
1646 |
final Set<ModuleReference> removed;
|
1551 |
if (toRemove.size() > 0) {
|
1647 |
if (toRemove.size() > 0) {
|
1552 |
final Set<String> idsToInstall = change.getIDsToInstall();
|
1648 |
final Set<String> idsToInstall = change.getIDsToInstall();
|
1553 |
|
1649 |
|
1554 |
// limit the number of requests
|
- |
|
1555 |
final Map<String, ModuleVersion> dbVersions = this.getDBInstalledModules();
|
- |
|
1556 |
removed = new HashSet<ModuleReference>();
|
1650 |
removed = new HashSet<ModuleReference>();
|
1557 |
for (final ModuleReference ref : toRemove) {
|
1651 |
for (final ModuleReference ref : toRemove) {
|
1558 |
// don't uninstall modules to upgrade but since this loop might uninstall modules
|
1652 |
// don't uninstall modules to upgrade but since this loop might uninstall modules
|
1559 |
// needed by ref, at least stop it like uninstallUnsafe() does
|
1653 |
// needed by ref, at least stop it like uninstallUnsafe() does
|
1560 |
if (idsToInstall.contains(ref.getID()))
|
1654 |
if (idsToInstall.contains(ref.getID()))
|
1561 |
this.stopModule(ref.getID(), false);
|
1655 |
this.stopModule(ref.getID(), false);
|
1562 |
else if (this.uninstallUnsafe(ref, !change.forceRemove(), dbVersions))
|
1656 |
else if (this.uninstallUnsafe(ref, !change.forceRemove(), change.getInstallState()))
|
1563 |
removed.add(ref);
|
1657 |
removed.add(ref);
|
1564 |
}
|
1658 |
}
|
1565 |
} else {
|
1659 |
} else {
|
1566 |
removed = Collections.emptySet();
|
1660 |
removed = Collections.emptySet();
|
1567 |
}
|
1661 |
}
|
Line 1579... |
Line 1673... |
1579 |
// don't use only getReferencesToInstall() as even if no modules need installing, their
|
1673 |
// don't use only getReferencesToInstall() as even if no modules need installing, their
|
1580 |
// state might need to change (e.g. start)
|
1674 |
// state might need to change (e.g. start)
|
1581 |
if (toInstall.size() > 0 || (targetState.compareTo(ModuleState.INSTALLED) > 0 && change.getUserReferencesToInstall().size() > 0)) {
|
1675 |
if (toInstall.size() > 0 || (targetState.compareTo(ModuleState.INSTALLED) > 0 && change.getUserReferencesToInstall().size() > 0)) {
|
1582 |
// record current time and actions
|
1676 |
// record current time and actions
|
1583 |
final File f = getToInstallFile();
|
1677 |
final File f = getToInstallFile();
|
1584 |
final XMLEncoder xmlEncoder = new XMLEncoder(new FileOutputStream(f));
|
1678 |
try (final XMLEncoder xmlEncoder = new XMLEncoder(new FileOutputStream(f))) {
|
1585 |
try {
|
- |
|
1586 |
xmlEncoder.setExceptionListener(XMLCodecUtils.EXCEPTION_LISTENER);
|
1679 |
xmlEncoder.setExceptionListener(XMLCodecUtils.EXCEPTION_LISTENER);
|
1587 |
xmlEncoder.setPersistenceDelegate(ModuleVersion.class, ModuleVersion.PERSIST_DELEGATE);
|
1680 |
xmlEncoder.setPersistenceDelegate(ModuleVersion.class, ModuleVersion.PERSIST_DELEGATE);
|
1588 |
xmlEncoder.setPersistenceDelegate(ModuleReference.class, ModuleReference.PERSIST_DELEGATE);
|
1681 |
xmlEncoder.setPersistenceDelegate(ModuleReference.class, ModuleReference.PERSIST_DELEGATE);
|
1589 |
xmlEncoder.writeObject(TO_INSTALL_VERSION);
|
1682 |
xmlEncoder.writeObject(TO_INSTALL_VERSION);
|
1590 |
xmlEncoder.writeObject(new Date());
|
1683 |
xmlEncoder.writeObject(new Date());
|
1591 |
xmlEncoder.writeObject(toInstall);
|
1684 |
xmlEncoder.writeObject(toInstall);
|
1592 |
xmlEncoder.writeObject(change.getUserReferencesToInstall());
|
1685 |
xmlEncoder.writeObject(change.getUserReferencesToInstall());
|
1593 |
xmlEncoder.writeObject(targetState);
|
1686 |
xmlEncoder.writeObject(targetState);
|
1594 |
xmlEncoder.writeObject(startPersistent);
|
1687 |
xmlEncoder.writeObject(startPersistent);
|
1595 |
xmlEncoder.close();
|
- |
|
1596 |
} catch (Exception e) {
|
1688 |
} catch (Exception e) {
|
1597 |
// try to delete invalid file before throwing exception
|
1689 |
// try to delete invalid file before throwing exception
|
1598 |
try {
|
- |
|
1599 |
xmlEncoder.close();
|
- |
|
1600 |
} catch (Exception e1) {
|
1690 |
// "any catch or finally block is run after the resources have been closed."
|
1601 |
e1.printStackTrace();
|
- |
|
1602 |
}
|
- |
|
1603 |
f.delete();
|
1691 |
f.delete();
|
1604 |
throw e;
|
1692 |
throw e;
|
1605 |
}
|
1693 |
}
|
1606 |
}
|
1694 |
}
|
1607 |
return ModulesStateChangeResult.onlyRemoved(removed);
|
1695 |
return new ModulesStateChangeResult(removed, change.getReferencesToInstall(), graph, Collections.emptyMap());
|
1608 |
}
|
1696 |
}
|
1609 |
|
1697 |
|
1610 |
// don't use getReferencesToInstall() as even if no modules need installing, their state
|
1698 |
// don't use getReferencesToInstall() as even if no modules need installing, their state
|
1611 |
// might need to change (e.g. start)
|
1699 |
// might need to change (e.g. start)
|
1612 |
if (targetState.compareTo(ModuleState.CREATED) < 0)
|
1700 |
if (targetState.compareTo(ModuleState.CREATED) < 0)
|
1613 |
return ModulesStateChangeResult.onlyRemoved(removed);
|
1701 |
return ModulesStateChangeResult.onlyRemoved(removed);
|
1614 |
|
1702 |
|
1615 |
final DepSolverGraph graph = change.getGraph();
|
- |
|
1616 |
if (graph == null)
|
1703 |
if (graph == null)
|
1617 |
throw new IllegalArgumentException("target state is " + targetState + " but no graph was provided");
|
1704 |
throw new IllegalArgumentException("target state is " + targetState + " but no graph was provided by " + change);
|
1618 |
|
1705 |
|
1619 |
// modules created by this method
|
1706 |
// modules created by this method
|
1620 |
final Map<ModuleReference, AbstractModule> modules = new LinkedHashMap<ModuleReference, AbstractModule>(graph.getFactories().size());
|
1707 |
final Map<ModuleReference, AbstractModule> modules = new LinkedHashMap<ModuleReference, AbstractModule>(graph.getFactories().size());
|
1621 |
// MAYBE try to continue even if some modules couldn't be created
|
1708 |
// MAYBE try to continue even if some modules couldn't be created
|
1622 |
final Set<ModuleReference> cannotCreate = Collections.emptySet();
|
1709 |
final Set<ModuleReference> cannotCreate = Collections.emptySet();
|
Line 1632... |
Line 1719... |
1632 |
for (final Entry<Object, ModuleFactory> e : dependenciesFactory.entrySet()) {
|
1719 |
for (final Entry<Object, ModuleFactory> e : dependenciesFactory.entrySet()) {
|
1633 |
final AbstractModule module = this.createdModules.get(e.getValue());
|
1720 |
final AbstractModule module = this.createdModules.get(e.getValue());
|
1634 |
assert module != null;
|
1721 |
assert module != null;
|
1635 |
dependenciesModule.put(e.getKey(), module);
|
1722 |
dependenciesModule.put(e.getKey(), module);
|
1636 |
}
|
1723 |
}
|
1637 |
final AbstractModule createdModule = useableFactory.createModule(this.getLocalDirectory(id), Collections.unmodifiableMap(dependenciesModule));
|
1724 |
final AbstractModule createdModule = useableFactory.createModule(this.getLocalDataDirectory(id), Collections.unmodifiableMap(dependenciesModule));
|
1638 |
modules.put(useableFactory.getReference(), createdModule);
|
1725 |
modules.put(useableFactory.getReference(), createdModule);
|
1639 |
this.createdModules.put(useableFactory, createdModule);
|
1726 |
this.createdModules.put(useableFactory, createdModule);
|
1640 |
|
1727 |
|
1641 |
// update graph
|
1728 |
// update graph
|
1642 |
final boolean added = this.dependencyGraph.addVertex(useableFactory);
|
1729 |
final boolean added = this.dependencyGraph.addVertex(useableFactory);
|
Line 1755... |
Line 1842... |
1755 |
final List<Locale> langs = cntrl.getCandidateLocales(baseName, targetLocale);
|
1842 |
final List<Locale> langs = cntrl.getCandidateLocales(baseName, targetLocale);
|
1756 |
// SQLFieldTranslator overwrite, so we need to load from general to specific
|
1843 |
// SQLFieldTranslator overwrite, so we need to load from general to specific
|
1757 |
final ListIterator<Locale> listIterator = CollectionUtils.getListIterator(langs, true);
|
1844 |
final ListIterator<Locale> listIterator = CollectionUtils.getListIterator(langs, true);
|
1758 |
while (listIterator.hasNext()) {
|
1845 |
while (listIterator.hasNext()) {
|
1759 |
final Locale lang = listIterator.next();
|
1846 |
final Locale lang = listIterator.next();
|
- |
|
1847 |
final SQLElementNamesFromXML elemNames = new SQLElementNamesFromXML(lang);
|
- |
|
1848 |
final String bundleName = cntrl.toBundleName(baseName, lang);
|
1760 |
final String resourceName = cntrl.toResourceName(cntrl.toBundleName(baseName, lang), "xml");
|
1849 |
final String resourceName = cntrl.toResourceName(bundleName, "xml");
|
1761 |
final InputStream ins = module.getClass().getResourceAsStream(resourceName);
|
1850 |
final InputStream ins = module.getClass().getResourceAsStream(resourceName);
|
1762 |
// do not force to have one mapping for each locale
|
1851 |
// do not force to have one mapping for each locale
|
1763 |
if (ins != null) {
|
1852 |
if (ins != null) {
|
1764 |
L.config("module " + module.getName() + " loading translation from " + resourceName);
|
1853 |
L.config("module " + module.getName() + " loading translation from " + resourceName);
|
1765 |
final Set<SQLTable> loadedTables;
|
1854 |
final Set<SQLTable> loadedTables;
|
1766 |
try {
|
1855 |
try {
|
1767 |
loadedTables = trns.load(getRoot(), mdVariant, ins).get0();
|
1856 |
loadedTables = trns.load(getRoot(), mdVariant, ins, elemNames).get0();
|
1768 |
} finally {
|
1857 |
} finally {
|
1769 |
ins.close();
|
1858 |
ins.close();
|
1770 |
}
|
1859 |
}
|
1771 |
if (loadedTables.size() > 0) {
|
1860 |
if (loadedTables.size() > 0) {
|
1772 |
res.addAll(loadedTables);
|
1861 |
res.addAll(loadedTables);
|
1773 |
found |= true;
|
1862 |
found |= true;
|
1774 |
}
|
1863 |
}
|
- |
|
1864 |
|
- |
|
1865 |
// As in PropsConfiguration.loadTranslations(), perhaps load the class at
|
- |
|
1866 |
// module.getClass().getPackage().getName() + '.' + bundleName
|
- |
|
1867 |
// to allow more flexibility. Perhaps pass a ModuleSQLFieldTranslator to
|
- |
|
1868 |
// restrict what a module can do (and have SQLElementNamesFromXML, mdVariant).
|
1775 |
}
|
1869 |
}
|
1776 |
}
|
1870 |
}
|
1777 |
}
|
1871 |
}
|
1778 |
return res;
|
1872 |
return res;
|
1779 |
}
|
1873 |
}
|
Line 1857... |
Line 1951... |
1857 |
res.add(f.getReference());
|
1951 |
res.add(f.getReference());
|
1858 |
}
|
1952 |
}
|
1859 |
return res;
|
1953 |
return res;
|
1860 |
}
|
1954 |
}
|
1861 |
|
1955 |
|
- |
|
1956 |
public final Set<ModuleReference> stopAllModules() {
|
- |
|
1957 |
// this method is not synchronized, so don't just return getRunningModules()
|
- |
|
1958 |
final Set<ModuleReference> res = new HashSet<>();
|
- |
|
1959 |
for (final String id : this.getRunningModules().keySet()) {
|
- |
|
1960 |
res.addAll(this.stopModuleRecursively(id));
|
- |
|
1961 |
}
|
- |
|
1962 |
return res;
|
- |
|
1963 |
}
|
- |
|
1964 |
|
1862 |
public synchronized final void stopModuleRecursively(final String id) {
|
1965 |
public synchronized final List<ModuleReference> stopModuleRecursively(final String id) {
|
1863 |
for (final ModuleReference ref : getRunningDependentModulesRecursively(id)) {
|
1966 |
final List<ModuleReference> res = getRunningDependentModulesRecursively(id);
|
- |
|
1967 |
for (final ModuleReference ref : res) {
|
1864 |
this.stopModule(ref.getID());
|
1968 |
this.stopModule(ref.getID());
|
1865 |
}
|
1969 |
}
|
- |
|
1970 |
return res;
|
1866 |
}
|
1971 |
}
|
1867 |
|
1972 |
|
1868 |
public final void stopModule(final String id) {
|
1973 |
public final boolean stopModule(final String id) {
|
1869 |
this.stopModule(id, true);
|
1974 |
return this.stopModule(id, true);
|
1870 |
}
|
1975 |
}
|
1871 |
|
1976 |
|
1872 |
// TODO pass ModuleReference instead of ID (need to change this.runningModules)
|
1977 |
// TODO pass ModuleReference instead of ID (need to change this.runningModules)
|
1873 |
public synchronized final void stopModule(final String id, final boolean persistent) {
|
1978 |
public synchronized final boolean stopModule(final String id, final boolean persistent) {
|
1874 |
if (!this.isModuleRunning(id))
|
1979 |
if (!this.isModuleRunning(id))
|
1875 |
return;
|
1980 |
return false;
|
1876 |
|
1981 |
|
1877 |
final ModuleFactory f = this.runningModules.get(id).getFactory();
|
1982 |
final ModuleFactory f = this.runningModules.get(id).getFactory();
|
1878 |
if (this.isAdminRequired(f.getReference()) && !currentUserIsAdmin())
|
1983 |
if (this.isAdminRequired(f.getReference()) && !currentUserIsAdmin())
|
1879 |
throw new IllegalStateException("Not allowed to stop a module required by the administrator " + f);
|
1984 |
throw new IllegalStateException("Not allowed to stop a module required by the administrator " + f);
|
1880 |
final Set<DepLink> deps = this.dependencyGraph.incomingEdgesOf(f);
|
1985 |
final Set<DepLink> deps = this.dependencyGraph.incomingEdgesOf(f);
|
Line 1905... |
Line 2010... |
1905 |
// we can't undo what the module has done, so just start from the base menu and re-apply all
|
2010 |
// we can't undo what the module has done, so just start from the base menu and re-apply all
|
1906 |
// modifications
|
2011 |
// modifications
|
1907 |
final MenuAndActions menuAndActions = MenuManager.getInstance().createBaseMenuAndActions();
|
2012 |
final MenuAndActions menuAndActions = MenuManager.getInstance().createBaseMenuAndActions();
|
1908 |
final ArrayList<AbstractModule> modules = new ArrayList<AbstractModule>(this.runningModules.values());
|
2013 |
final ArrayList<AbstractModule> modules = new ArrayList<AbstractModule>(this.runningModules.values());
|
1909 |
SwingThreadUtils.invoke(new Runnable() {
|
2014 |
SwingThreadUtils.invoke(new Runnable() {
|
- |
|
2015 |
|
1910 |
@Override
|
2016 |
@Override
|
1911 |
public void run() {
|
2017 |
public void run() {
|
1912 |
for (final AbstractModule m : modules) {
|
2018 |
for (final AbstractModule m : modules) {
|
1913 |
setupMenu(m, menuAndActions);
|
2019 |
setupMenu(m, menuAndActions);
|
1914 |
}
|
2020 |
}
|
1915 |
MenuManager.getInstance().setMenuAndActions(menuAndActions);
|
2021 |
MenuManager.getInstance().setMenuAndActions(menuAndActions);
|
1916 |
}
|
2022 |
}
|
- |
|
2023 |
|
1917 |
});
|
2024 |
});
|
1918 |
|
2025 |
|
1919 |
if (persistent)
|
2026 |
if (persistent)
|
1920 |
getRunningIDsPrefs().remove(m.getFactory().getID());
|
2027 |
getRunningIDsPrefs().remove(m.getFactory().getID());
|
1921 |
assert !this.isModuleRunning(id);
|
2028 |
assert !this.isModuleRunning(id);
|
- |
|
2029 |
return true;
|
1922 |
}
|
2030 |
}
|
1923 |
|
2031 |
|
1924 |
private final void stopModule(final AbstractModule m) {
|
2032 |
private final void stopModule(final AbstractModule m) {
|
1925 |
// this must not attempt to lock this monitor, see uninstallUnsafe()
|
2033 |
// this must not attempt to lock this monitor, see uninstallUnsafe()
|
1926 |
assert SwingUtilities.isEventDispatchThread();
|
2034 |
assert SwingUtilities.isEventDispatchThread();
|
Line 2129... |
Line 2237... |
2129 |
|
2237 |
|
2130 |
@Override
|
2238 |
@Override
|
2131 |
public DepSolverGraph getGraph() {
|
2239 |
public DepSolverGraph getGraph() {
|
2132 |
return null;
|
2240 |
return null;
|
2133 |
}
|
2241 |
}
|
- |
|
2242 |
|
- |
|
2243 |
@Override
|
- |
|
2244 |
public String toString() {
|
- |
|
2245 |
return "Uninstall solution for " + this.getReferencesToRemove();
|
- |
|
2246 |
}
|
2134 |
};
|
2247 |
};
|
2135 |
}
|
2248 |
}
|
2136 |
|
2249 |
|
2137 |
public final void uninstall(final ModuleReference ref) throws Exception {
|
2250 |
public final void uninstall(final ModuleReference ref) throws Exception {
|
2138 |
this.uninstall(ref, false);
|
2251 |
this.uninstall(ref, false);
|
Line 2144... |
Line 2257... |
2144 |
|
2257 |
|
2145 |
public synchronized final Set<ModuleReference> uninstall(final ModuleReference id, final boolean recurse, final boolean force) throws Exception {
|
2258 |
public synchronized final Set<ModuleReference> uninstall(final ModuleReference id, final boolean recurse, final boolean force) throws Exception {
|
2146 |
return this.uninstall(Collections.singleton(id), recurse, force);
|
2259 |
return this.uninstall(Collections.singleton(id), recurse, force);
|
2147 |
}
|
2260 |
}
|
2148 |
|
2261 |
|
2149 |
// return vers if it matches ref
|
2262 |
// return the version in installed that matches ref
|
2150 |
private final ModuleVersion filter(final ModuleVersion vers, final ModuleReference ref) {
|
2263 |
private final ModuleReference filter(final Set<ModuleReference> installed, final ModuleReference ref) {
|
- |
|
2264 |
for (final ModuleReference installedRef : installed) {
|
2151 |
return ref.getVersion() == null || vers != null && vers.equals(ref.getVersion()) ? vers : null;
|
2265 |
if (installedRef.getID().equals(ref.getID()) && (ref.getVersion() == null || installedRef.getVersion().equals(ref.getVersion())))
|
- |
|
2266 |
return installedRef;
|
- |
|
2267 |
}
|
- |
|
2268 |
return null;
|
2152 |
}
|
2269 |
}
|
2153 |
|
2270 |
|
2154 |
// unsafe because this method doesn't check dependents
|
2271 |
// unsafe because this method doesn't check dependents
|
2155 |
// dbVersions parameter to avoid requests to the DB
|
2272 |
// dbVersions parameter to avoid requests to the DB
|
2156 |
// return true if the mref was actually uninstalled (i.e. it was installed locally or remotely)
|
2273 |
// return true if the mref was actually uninstalled (i.e. it was installed locally or remotely)
|
2157 |
private boolean uninstallUnsafe(final ModuleReference mref, final boolean requireModule, Map<String, ModuleVersion> dbVersions) throws SQLException, Exception {
|
2274 |
private boolean uninstallUnsafe(final ModuleReference mref, final boolean requireModule, final InstallationState installState) throws SQLException, Exception {
|
2158 |
assert Thread.holdsLock(this);
|
2275 |
assert Thread.holdsLock(this);
|
2159 |
final String id = mref.getID();
|
2276 |
final String id = mref.getID();
|
2160 |
if (dbVersions == null)
|
- |
|
2161 |
dbVersions = this.getDBInstalledModules();
|
- |
|
2162 |
// versions to uninstall
|
2277 |
// versions to uninstall
|
2163 |
final ModuleVersion localVersion = filter(this.getModuleVersionInstalledLocally(id), mref);
|
2278 |
final ModuleReference localRef = filter(installState.getLocal(), mref);
|
2164 |
final ModuleVersion dbVersion = filter(dbVersions.get(id), mref);
|
2279 |
final ModuleReference dbRef = filter(installState.getRemote(), mref);
|
- |
|
2280 |
final ModuleVersion dbVersion = dbRef == null ? null : dbRef.getVersion();
|
2165 |
|
2281 |
|
2166 |
// otherwise it will get re-installed the next launch
|
2282 |
// otherwise it will get re-installed the next launch
|
2167 |
getRunningIDsPrefs().remove(id);
|
2283 |
getRunningIDsPrefs().remove(id);
|
2168 |
final Set<ModuleReference> refs = new HashSet<ModuleReference>(2);
|
2284 |
final Set<ModuleReference> refs = new HashSet<ModuleReference>(2);
|
2169 |
if (localVersion != null)
|
2285 |
if (localRef != null)
|
2170 |
refs.add(new ModuleReference(id, localVersion));
|
2286 |
refs.add(localRef);
|
2171 |
if (dbVersion != null)
|
2287 |
if (dbRef != null)
|
2172 |
refs.add(new ModuleReference(id, dbVersion));
|
2288 |
refs.add(dbRef);
|
2173 |
setAdminRequiredModules(refs, false);
|
2289 |
setAdminRequiredModules(refs, false);
|
2174 |
|
2290 |
|
2175 |
// only return after having cleared required, so that we don't need to install just to
|
2291 |
// only return after having cleared required, so that we don't need to install just to
|
2176 |
// not require
|
2292 |
// not require
|
2177 |
if (localVersion == null && dbVersion == null)
|
2293 |
if (localRef == null && dbRef == null)
|
2178 |
return false;
|
2294 |
return false;
|
2179 |
|
2295 |
|
2180 |
if (dbVersion != null && !currentUserIsAdmin())
|
2296 |
if (dbRef != null && !currentUserIsAdmin())
|
2181 |
throw new IllegalStateException("Not allowed to uninstall " + id + " from the database");
|
2297 |
throw new IllegalStateException("Not allowed to uninstall " + id + " from the database");
|
2182 |
|
2298 |
|
2183 |
// DB module
|
2299 |
// DB module
|
2184 |
final AbstractModule module;
|
2300 |
final AbstractModule module;
|
2185 |
if (!this.isModuleRunning(id)) {
|
2301 |
if (!this.isModuleRunning(id)) {
|
2186 |
if (dbVersion == null) {
|
2302 |
if (dbRef == null) {
|
2187 |
assert localVersion != null;
|
2303 |
assert localRef != null;
|
2188 |
// only installed locally
|
2304 |
// only installed locally
|
2189 |
module = null;
|
2305 |
module = null;
|
2190 |
} else {
|
2306 |
} else {
|
2191 |
final SortedMap<ModuleVersion, ModuleFactory> available = this.factories.getVersions(id);
|
2307 |
final SortedMap<ModuleVersion, ModuleFactory> available = this.factories.getVersions(id);
|
2192 |
final ModuleReference ref;
|
2308 |
final ModuleReference ref;
|
2193 |
if (available.containsKey(dbVersion)) {
|
2309 |
if (available.containsKey(dbVersion)) {
|
2194 |
ref = new ModuleReference(id, dbVersion);
|
2310 |
ref = dbRef;
|
2195 |
} else {
|
2311 |
} else {
|
2196 |
// perhaps modules should specify which versions they can uninstall
|
2312 |
// perhaps modules should specify which versions they can uninstall
|
2197 |
final SortedMap<ModuleVersion, ModuleFactory> moreRecent = available.headMap(dbVersion);
|
2313 |
final SortedMap<ModuleVersion, ModuleFactory> moreRecent = available.headMap(dbVersion);
|
2198 |
if (moreRecent.size() == 0) {
|
2314 |
if (moreRecent.size() == 0) {
|
2199 |
ref = null;
|
2315 |
ref = null;
|
Line 2206... |
Line 2322... |
2206 |
assert ref.getVersion().compareTo(dbVersion) >= 0;
|
2322 |
assert ref.getVersion().compareTo(dbVersion) >= 0;
|
2207 |
final ModuleFactory f = available.get(ref.getVersion());
|
2323 |
final ModuleFactory f = available.get(ref.getVersion());
|
2208 |
assert f != null;
|
2324 |
assert f != null;
|
2209 |
// only call expensive method if necessary
|
2325 |
// only call expensive method if necessary
|
2210 |
if (!this.createdModules.containsKey(f)) {
|
2326 |
if (!this.createdModules.containsKey(f)) {
|
2211 |
// don't use the result, instead use this.createdModules since the module
|
2327 |
// * Don't use the result, instead use this.createdModules since the module
|
2212 |
// might have been created before
|
2328 |
// might have been created before.
|
- |
|
2329 |
// * Cannot call directly applyChange(), we need DepSolver to create modules
|
- |
|
2330 |
// that ref depends on, as they might be required by
|
- |
|
2331 |
// AbstractModule.uninstall().
|
- |
|
2332 |
// * Cannot pass NoChoicePredicate.NO_CHANGE as ref won't be created if not
|
- |
|
2333 |
// already installed both locally and remotely. No installation will occur
|
- |
|
2334 |
// since we pass ModuleState.CREATED.
|
2213 |
this.createModules(Collections.singleton(ref), NoChoicePredicate.NO_CHANGE, ModuleState.CREATED);
|
2335 |
this.createModules(Collections.singleton(ref), NoChoicePredicate.ONLY_INSTALL, ModuleState.CREATED);
|
2214 |
}
|
2336 |
}
|
2215 |
module = this.createdModules.get(f);
|
2337 |
module = this.createdModules.get(f);
|
2216 |
} else {
|
2338 |
} else {
|
2217 |
module = null;
|
2339 |
module = null;
|
2218 |
}
|
2340 |
}
|
Line 2226... |
Line 2348... |
2226 |
}
|
2348 |
}
|
2227 |
throw new IllegalStateException("Couldn't get module " + id + " : " + reason);
|
2349 |
throw new IllegalStateException("Couldn't get module " + id + " : " + reason);
|
2228 |
}
|
2350 |
}
|
2229 |
}
|
2351 |
}
|
2230 |
} else {
|
2352 |
} else {
|
- |
|
2353 |
final ModuleVersion localVersion = localRef.getVersion();
|
2231 |
if (!localVersion.equals(dbVersion))
|
2354 |
if (!localVersion.equals(dbVersion))
|
2232 |
L.warning("Someone else has changed the database version while we were running :" + localVersion + " != " + dbVersion);
|
2355 |
L.warning("Someone else has changed the database version while we were running :" + localVersion + " != " + dbVersion);
|
2233 |
module = this.runningModules.get(id);
|
2356 |
module = this.runningModules.get(id);
|
2234 |
assert localVersion.equals(module.getFactory().getVersion());
|
2357 |
assert localVersion.equals(module.getFactory().getVersion());
|
2235 |
this.stopModule(id, false);
|
2358 |
this.stopModule(id, false);
|
Line 2237... |
Line 2360... |
2237 |
// ATTN we hold this monitor, so stop() should never try to acquire it in the EDT
|
2360 |
// ATTN we hold this monitor, so stop() should never try to acquire it in the EDT
|
2238 |
if (!SwingUtilities.isEventDispatchThread()) {
|
2361 |
if (!SwingUtilities.isEventDispatchThread()) {
|
2239 |
SwingUtilities.invokeAndWait(EMPTY_RUNNABLE);
|
2362 |
SwingUtilities.invokeAndWait(EMPTY_RUNNABLE);
|
2240 |
}
|
2363 |
}
|
2241 |
}
|
2364 |
}
|
2242 |
assert (module == null) == (!requireModule || dbVersion == null);
|
2365 |
assert (module == null) == (!requireModule || dbRef == null);
|
2243 |
|
2366 |
|
2244 |
SQLUtils.executeAtomic(getDS(), new SQLFactory<Object>() {
|
2367 |
SQLUtils.executeAtomic(getDS(), new SQLFactory<Object>() {
|
2245 |
@Override
|
2368 |
@Override
|
2246 |
public Object create() throws SQLException {
|
2369 |
public Object create() throws SQLException {
|
2247 |
final DBRoot root = getRoot();
|
2370 |
final DBRoot root = getRoot();
|
2248 |
if (module != null) {
|
2371 |
if (module != null) {
|
2249 |
module.uninstall(root);
|
2372 |
module.uninstall(root);
|
2250 |
unregisterSQLElements(module);
|
2373 |
unregisterSQLElements(module);
|
2251 |
}
|
2374 |
}
|
2252 |
if (localVersion != null)
|
2375 |
if (localRef != null)
|
2253 |
setModuleInstalledLocally(new ModuleReference(id, localVersion), false);
|
2376 |
setModuleInstalledLocally(localRef, false);
|
2254 |
|
2377 |
|
2255 |
// uninstall from DB
|
2378 |
// uninstall from DB
|
2256 |
if (dbVersion != null) {
|
2379 |
if (dbRef != null) {
|
2257 |
final Tuple2<Set<String>, Set<SQLName>> createdItems = getCreatedItems(id);
|
2380 |
final Tuple2<Set<String>, Set<SQLName>> createdItems = getCreatedItems(id);
|
2258 |
final List<ChangeTable<?>> l = new ArrayList<ChangeTable<?>>();
|
2381 |
final List<ChangeTable<?>> l = new ArrayList<ChangeTable<?>>();
|
2259 |
final Set<String> tableNames = createdItems.get0();
|
2382 |
final Set<String> tableNames = createdItems.get0();
|
2260 |
for (final SQLName field : createdItems.get1()) {
|
2383 |
for (final SQLName field : createdItems.get1()) {
|
2261 |
final SQLField f = root.getDesc(field, SQLField.class);
|
2384 |
final SQLField f = root.getDesc(field, SQLField.class);
|
Line 2274... |
Line 2397... |
2274 |
root.getDBSystemRoot().getDataSource().execute(s);
|
2397 |
root.getDBSystemRoot().getDataSource().execute(s);
|
2275 |
root.getSchema().updateVersion();
|
2398 |
root.getSchema().updateVersion();
|
2276 |
root.refetch();
|
2399 |
root.refetch();
|
2277 |
}
|
2400 |
}
|
2278 |
|
2401 |
|
2279 |
removeModuleFields(new ModuleReference(id, dbVersion));
|
2402 |
removeModuleFields(dbRef);
|
2280 |
}
|
2403 |
}
|
2281 |
return null;
|
2404 |
return null;
|
2282 |
}
|
2405 |
}
|
2283 |
});
|
2406 |
});
|
2284 |
return true;
|
2407 |
return true;
|