package ilarkesto.di.app; import ilarkesto.base.Str; import ilarkesto.base.Sys; import ilarkesto.base.Tm; import ilarkesto.base.Utl; import ilarkesto.concurrent.ATask; import ilarkesto.concurrent.TaskManager; import ilarkesto.core.logging.Log; import ilarkesto.di.Context; import ilarkesto.io.ExclusiveFileLock; import ilarkesto.io.ExclusiveFileLock.FileLockedException; import ilarkesto.io.IO; import ilarkesto.logging.DefaultLogDataHandler; import ilarkesto.persistence.DaoListener; import ilarkesto.persistence.DaoService; import ilarkesto.persistence.EntityStore; import ilarkesto.persistence.FileEntityStore; import ilarkesto.persistence.Serializer; import ilarkesto.persistence.TransactionService; import ilarkesto.persistence.xstream.XStreamSerializer; import ilarkesto.properties.FilePropertiesStore; import java.io.File; import java.util.Properties; import java.util.Set; /** * Base class of a custom application * * @author Witoslaw Koczewski */ public abstract class AApplication { private static Log log = Log.get(AApplication.class); private ExclusiveFileLock exclusiveFileLock; protected abstract void onStart(); protected abstract void onShutdown(); protected abstract void scheduleTasks(TaskManager tm); protected Context context; private String[] arguments = new String[0]; public void ensureIntegrity() { log.info("Ensuring application integrity"); getDaoService().ensureIntegrity(); getTransactionService().commit(); } protected boolean isSingleton() { return true; } public final void start() { if (instance != null) { throw new RuntimeException("An Application already started: " + instance); } instance = this; log.info("\n\n DATA DIR:", getApplicationDataDir(), "\n"); context = Context.createRootContext("app:" + getApplicationName()); context.addBeanProvider(this); if (isSingleton()) { File lockFile = new File(getApplicationDataDir() + "/.lock"); for (int i = 0; i < 10; i++) { try { exclusiveFileLock = new ExclusiveFileLock(lockFile); break; } catch (FileLockedException ex) { log.info("Application already running. Lock file locked: " + lockFile.getAbsolutePath()); } Utl.sleep(1000); } if (exclusiveFileLock == null) { log.fatal("Application startup failed. Another instance is running. Lock file: " + lockFile.getAbsolutePath()); shutdown(); return; } } try { ensureIntegrity(); onStart(); } catch (Throwable ex) { APPLICATION_LOCK = null; throw new RuntimeException("Application startup failed.", ex); } scheduleTasks(getTaskManager()); } public final void shutdown() { new Thread(new Runnable() { @Override public void run() { synchronized (getApplicationLock()) { if (instance == null) throw new RuntimeException("Application not started yet."); log.info("Shutdown initiated:", getApplicationName()); getTaskManager().shutdown(10000); Set<ATask> tasks = getTaskManager().getRunningTasks(); if (!tasks.isEmpty()) { log.warn("Aborting tasks on shutdown failed:", tasks); } if (exclusiveFileLock != null) exclusiveFileLock.release(); Log.flush(); onShutdown(); DefaultLogDataHandler.stopLogging(); } } }).start(); } public final <T> T autowire(T bean) { return context.autowire(bean); } public void setArguments(String[] arguments) { this.arguments = arguments; } public String[] getArguments() { return arguments; } private static AApplication instance; public static AApplication get() { if (instance == null) throw new RuntimeException("No application started yet"); return instance; } public static boolean isStarted() { return instance != null; } private static String APPLICATION_LOCK = "APPLICATION_LOCK"; public Object getApplicationLock() { return APPLICATION_LOCK; } public final String getApplicationPackageName() { return getClass().getPackage().getName(); } public String getApplicationLabel() { return getApplicationName(); } public AApplication getApplication() { return this; } private String applicationName; public String getApplicationName() { if (applicationName == null) { applicationName = getClass().getSimpleName(); applicationName = Str.lowercaseFirstLetter(applicationName); applicationName = Str.removeSuffix(applicationName, "Application"); } return applicationName; } private String applicationDataDir; public String getApplicationDataDir() { if (applicationDataDir == null) { applicationDataDir = isDevelopmentMode() ? getDevelopmentModeApplicationDataDir() : getProductionModeApplicationDataDir(); } return applicationDataDir; } protected String getDevelopmentModeApplicationDataDir() { return new File("runtimedata").getAbsolutePath(); } protected String getProductionModeApplicationDataDir() { return new File(Sys.getUsersHomePath() + "/." + getApplicationName()).getAbsolutePath(); } private String applicationTempDir; public String getApplicationTempDir() { if (applicationTempDir == null) { applicationTempDir = getApplicationDataDir() + "/tmp"; } return applicationTempDir; } private FilePropertiesStore applicationConfig; public FilePropertiesStore getApplicationConfig() { if (applicationConfig == null) { applicationConfig = new FilePropertiesStore(getApplicationDataDir() + "/config.properties", false); } return applicationConfig; } private String releaseLabel; public String getReleaseLabel() { if (releaseLabel == null) { releaseLabel = getBuildProperties().getProperty("release.label"); if (releaseLabel == null || releaseLabel.equals("@release-label@")) releaseLabel = "dev"; } return releaseLabel; } private Properties buildProperties; public Properties getBuildProperties() { if (buildProperties == null) { if (isDevelopmentMode()) { buildProperties = new Properties(); } else { try { buildProperties = IO.loadProperties(IO.getResource(getApplicationName() + ".build.properties"), IO.UTF_8); } catch (Throwable t) { log.error(t); buildProperties = new Properties(); } } } return buildProperties; } public void deleteOldBackupFiles(String backupDir) { log.info("Deleting old backup files from:", backupDir); final long deadline = System.currentTimeMillis() - Tm.DAY * 3; IO.FileProcessor processor = new IO.FileProcessor() { public boolean isAbortRequested() { return false; } public void onFile(File file) { if (file.lastModified() < deadline) IO.delete(file); } public boolean onFolderBegin(File folder) { return true; } public void onFolderEnd(File folder) { folder.delete(); } }; IO.process(backupDir, processor); } public boolean isDevelopmentMode() { return Sys.isDevelopmentMode(); } public final boolean isProductionMode() { return !isDevelopmentMode(); } @Override public final String toString() { return getApplicationName(); } // --- beans / services --- private TaskManager taskManager; public TaskManager getTaskManager() { if (taskManager == null) taskManager = Context.get().autowire(new TaskManager()); return taskManager; } private FileEntityStore entityStore; public final EntityStore getEntityStore() { if (entityStore == null) { entityStore = new FileEntityStore(); entityStore.setDir(getApplicationDataDir() + "/entities"); entityStore.setBackupDir(getApplicationDataDir() + "/backup/entities"); Context.get().autowire(entityStore); } return entityStore; } private XStreamSerializer beanSerializer; public final Serializer getBeanSerializer() { if (beanSerializer == null) { beanSerializer = new XStreamSerializer(); Context.get().autowire(beanSerializer); } return beanSerializer; } private DaoService daoService; public final DaoService getDaoService() { if (daoService == null) { daoService = new DaoService(); Context.get().autowire(daoService); daoService.initialize(context); for (DaoListener listener : Context.get().getBeansByType(DaoListener.class)) daoService.addListener(listener); } return daoService; } private TransactionService transactionService; public final TransactionService getTransactionService() { if (transactionService == null) { transactionService = new TransactionService(); Context.get().autowire(transactionService); } return transactionService; } }