package com.eas.client.application; import com.eas.client.*; import com.eas.client.cache.ApplicationSourceIndexer; import com.eas.client.cache.FormsDocuments; import com.eas.client.cache.ModelsDocuments; import com.eas.client.cache.ReportsConfigs; import com.eas.client.cache.ScriptsConfigs; import com.eas.client.login.AnonymousPlatypusPrincipal; import com.eas.client.login.ConnectionsSelector; import com.eas.client.login.Credentials; import com.eas.client.login.CredentialsSelector; import com.eas.client.login.PlatypusPrincipal; import com.eas.client.queries.LocalQueriesProxy; import com.eas.client.queries.QueriesProxy; import com.eas.client.resourcepool.DatasourcesArgsConsumer; import com.eas.client.resourcepool.GeneralResourceProvider; import com.eas.client.scripts.ScriptedResource; import com.eas.client.settings.ConnectionSettings; import com.eas.client.threetier.PlatypusClient; import com.eas.client.threetier.http.PlatypusHttpConnection; import com.eas.client.threetier.http.PlatypusHttpConstants; import com.eas.client.threetier.platypus.PlatypusPlatypusConnection; import com.eas.script.Scripts; import com.eas.util.args.ThreadsArgsConsumer; import java.awt.EventQueue; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashSet; import java.util.concurrent.Callable; import java.util.logging.*; import javax.swing.UIManager; /** * * @author pk, mg refactoring */ public class PlatypusClientApplication { public static final String CMD_SWITCHS_PREFIX = "-"; // command line switches public static final String APPELEMENT_CMD_SWITCH = "appelement"; public static final String URL_CMD_SWITCH = "url"; // container switches public static final String DEF_DATASOURCE_CONF_PARAM = "default-datasource"; public static final String SOURCE_PATH_CONF_PARAM = "source-path"; // login switchs public static final String USER_CMD_SWITCH = "user"; public static final String PASSWORD_CMD_SWITCH = "password"; public static final String MAX_LOGIN_ATTEMPTS_CMD_SWITCH = "max-login-attempts"; // error messages public static final String BAD_DEF_DATASOURCE_MSG = "default-datasource value not specified"; public static final String BAD_SOURCE_PATH_MSG = "source-path value not specified"; public static final String USER_HOME_ABSENTFILE_MSG = ClientConstants.USER_HOME_PROP_NAME + " property points to non-existent location"; public static final String USER_HOME_MISSING_MSG = ClientConstants.USER_HOME_PROP_NAME + " property missing. Please specify it with -D" + ClientConstants.USER_HOME_PROP_NAME + "=... command line switch"; public static final String USER_HOME_NOT_A_DIRECTORY_MSG = ClientConstants.USER_HOME_PROP_NAME + " property points to non-directory"; public static final String BAD_DB_CREDENTIALS_MSG = "Bad database credentials. May be bad db connection settings (url, dbuser, dbpassword)."; public static final String BAD_APP_CREDENTIALS_MSG = "Bad application credentials."; public static final String APP_ELEMENT_MISSING_MSG = "Script to be executed is missing. Nothing to do, so exit."; public static final String CLIENT_REQUIRED_AFTER_LOGIN_MSG = "After successfull login there must be a client."; public static final String MISSING_SUCH_APP_ELEMENT_MSG = "Application element with name specified (%s) is absent. Nothing to do, so exit."; public static final String NON_RUNNABLE_APP_ELEMENT_MSG = "Application element specified (%s) is of non-runnable type. Nothing to do, so exit."; public static final String SHOW_FUNC_MISSING_MSG = "'show' function missing in %s instance"; public static final String EXECUTE_FUNC_MISSING_MSG = "'execute' function missing in %s instance"; public static final String APPLICATION_ELEMENTS_LOCATION_MSG = "Application is located at: {0}"; // public static class Config { private String startScriptPath; private String userName; private char[] password; private int maximumAuthenticateAttempts = Integer.MAX_VALUE; private URL url; private String defDatasource; private String sourcePath; private final DatasourcesArgsConsumer datasourcesArgs = new DatasourcesArgsConsumer(); private final ThreadsArgsConsumer threadsArgs = new ThreadsArgsConsumer(); public String getStartScriptPath() { return startScriptPath; } public String getUserName() { return userName; } public URL getUrl() { return url; } public String getSourcePath() { return sourcePath; } public ThreadsArgsConsumer getThreadsArgs() { return threadsArgs; } public DatasourcesArgsConsumer getDatasourcesArgs() { return datasourcesArgs; } public String getDefDatasource() { return defDatasource; } public int getMaximumAuthenticateAttempts() { return maximumAuthenticateAttempts; } public static Config parse(String[] args) throws Exception { Config commonArgs = new Config(); int i = 0; while (i < args.length) { if ((CMD_SWITCHS_PREFIX + URL_CMD_SWITCH).equalsIgnoreCase(args[i])) { if (i < args.length - 1) { commonArgs.url = new URL(null, args[i + 1], new URLStreamHandler() { @Override protected URLConnection openConnection(URL u) throws IOException { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } }); i += 2; } else { throw new IllegalArgumentException("Url syntax: -url <value>"); } } else if ((CMD_SWITCHS_PREFIX + DEF_DATASOURCE_CONF_PARAM).equalsIgnoreCase(args[i])) { if (i < args.length - 1) { commonArgs.defDatasource = args[i + 1]; i += 2; } else { throw new IllegalArgumentException(BAD_DEF_DATASOURCE_MSG); } } else if ((CMD_SWITCHS_PREFIX + SOURCE_PATH_CONF_PARAM).equalsIgnoreCase(args[i])) { if (i < args.length - 1) { commonArgs.sourcePath = args[i + 1]; i += 2; } else { throw new IllegalArgumentException(BAD_SOURCE_PATH_MSG); } } else if ((CMD_SWITCHS_PREFIX + USER_CMD_SWITCH).equalsIgnoreCase(args[i])) { if (i < args.length - 1) { commonArgs.userName = args[i + 1]; i += 2; } else { throw new IllegalArgumentException("syntax: -user <value>"); } } else if ((CMD_SWITCHS_PREFIX + PASSWORD_CMD_SWITCH).equalsIgnoreCase(args[i])) { if (i < args.length - 1) { commonArgs.password = args[i + 1].toCharArray(); i += 2; } else { throw new IllegalArgumentException("syntax: -password <value>"); } } else if ((CMD_SWITCHS_PREFIX + MAX_LOGIN_ATTEMPTS_CMD_SWITCH).equalsIgnoreCase(args[i])) { if (i < args.length - 1) { commonArgs.maximumAuthenticateAttempts = Integer.valueOf(args[i + 1]); i += 2; } else { throw new IllegalArgumentException("syntax: -max-login-attempts <value>"); } } else if ((CMD_SWITCHS_PREFIX + APPELEMENT_CMD_SWITCH).equalsIgnoreCase(args[i])) { if (i < args.length - 1) { commonArgs.startScriptPath = args[i + 1]; i += 2; } else { throw new IllegalArgumentException("syntax: -appElement <application element id>"); } } else { int consumed = commonArgs.datasourcesArgs.consume(args, i); if (consumed > 0) { i += consumed; } else { consumed = commonArgs.threadsArgs.consume(args, i); if (consumed > 0) { i += consumed; } else { throw new IllegalArgumentException("unknown argument: " + args[i]); } } } } return commonArgs; } } public static class UIOnCredentials implements Callable<Credentials> { protected Config config; public UIOnCredentials(Config aConfig) { super(); config = aConfig; } @Override public Credentials call() throws Exception { if (config.userName != null) { return new Credentials(config.userName, new String(config.password)); } else { Callable<Credentials> selector = () -> { CredentialsSelector credentialsSelector = new CredentialsSelector(); credentialsSelector.setVisible(true); if (credentialsSelector.getReturnStatus() == CredentialsSelector.RET_OK) { return new Credentials(credentialsSelector.getUserName(), credentialsSelector.getPassword()); } else { return null; } }; if (EventQueue.isDispatchThread()) { Scripts.LocalContext context = Scripts.getContext(); try { return selector.call(); } finally { Scripts.setContext(context); } } else { Credentials res = new Credentials(null, null); EventQueue.invokeAndWait(() -> { try { Credentials cr = selector.call(); if (cr != null) { res.userName = cr.userName; res.password = cr.password; } } catch (Exception ex) { Logger.getLogger(PlatypusClientApplication.class.getName()).log(Level.SEVERE, null, ex); } }); return res.userName != null ? res : null; } } } } /** * @param args the command line arguments * @throws Exception */ public static void main(String[] args) throws Exception { try { UIManager.put("swing.boldMetal", Boolean.FALSE); System.setProperty("java.awt.Window.locationByPlatform", "true"); Config config = Config.parse(args); checkUrl(config); init(config); run(""); } catch (Throwable t) { Logger.getLogger(PlatypusClientApplication.class.getName()).log(Level.SEVERE, null, t); System.exit(0xff); } } public static void run(String aModuleName) { Scripts.getSpace().process(() -> { try { ScriptedResource._require(new String[]{aModuleName}, null, Scripts.getSpace(), new HashSet<>(), (Void v) -> { Logger.getLogger(PlatypusClientApplication.class.getName()).log(Level.INFO, "Platypus application started."); }, (Exception ex) -> { Logger.getLogger(PlatypusClientApplication.class.getName()).log(Level.SEVERE, null, ex); }); } catch (Exception ex) { Logger.getLogger(PlatypusClientApplication.class.getName()).log(Level.SEVERE, null, ex); } }); } public static void init(Config config) throws Exception { if (config.url != null) { checkUserHome(); GeneralResourceProvider.registerDrivers(); config.datasourcesArgs.registerDatasources(); Scripts.initBIO(config.threadsArgs.getMaxServicesTreads()); Scripts.initTasks((Runnable aTask) -> { EventQueue.invokeLater(aTask); }); Application app; PlatypusPrincipal.setClientSpacePrincipal(new AnonymousPlatypusPrincipal()); Path apiFolder = ScriptedResource.lookupPlatypusJs(); Logger.getLogger(PlatypusClientApplication.class.getName()).log(Level.INFO, "Application is located at: {0}", config.url); if (config.url.getProtocol().equalsIgnoreCase(PlatypusHttpConstants.PROTOCOL_HTTP)) { app = new PlatypusClient(new PlatypusHttpConnection(config.url, config.sourcePath, new UIOnCredentials(config), config.maximumAuthenticateAttempts, config.threadsArgs.getMaxHttpTreads())); } else if (config.url.getProtocol().equalsIgnoreCase(PlatypusHttpConstants.PROTOCOL_HTTPS)) { app = new PlatypusClient(new PlatypusHttpConnection(config.url, config.sourcePath, new UIOnCredentials(config), config.maximumAuthenticateAttempts, config.threadsArgs.getMaxHttpTreads())); } else if (config.url.getProtocol().equalsIgnoreCase("platypus")) { app = new PlatypusClient(new PlatypusPlatypusConnection(config.url, new UIOnCredentials(config), config.maximumAuthenticateAttempts, (Runnable aTask) -> { EventQueue.invokeLater(aTask); }, config.threadsArgs.getMaxPlatypusConnections(), true)); } else if (config.url.getProtocol().equalsIgnoreCase("file")) { File f = new File(config.url.toURI()); if (f.exists() && f.isDirectory()) { ModelsDocuments models = new ModelsDocuments(); ScriptsConfigs scriptsConfigs = new ScriptsConfigs(); ValidatorsScanner validatorsScanner = new ValidatorsScanner(); Path projectRoot = Paths.get(f.toURI()); Path appFolder = config.sourcePath != null ? projectRoot.resolve(config.sourcePath) : projectRoot; ApplicationSourceIndexer indexer = new ApplicationSourceIndexer(appFolder, apiFolder, scriptsConfigs, validatorsScanner); // TODO: add command line argument "watch" after watcher refactoring //indexer.watch(); ScriptedDatabasesClient twoTierCore = new ScriptedDatabasesClient(config.defDatasource, indexer, true, validatorsScanner.getValidators(), config.threadsArgs.getMaxJdbcTreads()); QueriesProxy qp = new LocalQueriesProxy(twoTierCore, indexer); ModulesProxy mp = new LocalModulesProxy(indexer, models, config.startScriptPath); twoTierCore.setQueries(qp); app = new Application() { protected FormsDocuments forms = new FormsDocuments(); protected ReportsConfigs reports = new ReportsConfigs(); @Override public Application.Type getType() { return Type.CLIENT; } @Override public QueriesProxy getQueries() { return qp; } @Override public ModulesProxy getModules() { return mp; } @Override public ServerModulesProxy getServerModules() { throw new UnsupportedOperationException("Application.getServerModules() is not supported in two-tier architecture."); } @Override public ModelsDocuments getModels() { return models; } @Override public FormsDocuments getForms() { return forms; } @Override public ReportsConfigs getReports() { return reports; } @Override public ScriptsConfigs getScriptsConfigs() { return scriptsConfigs; } }; } else { throw new IllegalArgumentException("applicationUrl: " + config.url + " doesn't point to existent directory or JNDI resource."); } } else { throw new Exception("Unknown protocol in url: " + config.url); } ScriptedResource.init(app, apiFolder, false); Scripts.setOnlySpace(Scripts.createSpace()); } else { throw new IllegalArgumentException("Application url is missing. url is a required parameter."); } } protected static void checkUrl(Config config) throws InvocationTargetException, InterruptedException { if (config.url == null) { Callable<URL> urlSelector = () -> { ConnectionsSelector connectionsSelector = new ConnectionsSelector(null); connectionsSelector.setVisible(true); if (connectionsSelector.getReturnStatus() == ConnectionsSelector.RET_OK) { ConnectionSettings settings = ConnectionsSelector.getDefaultSettings(); return new URL(null, settings.getUrl(), new URLStreamHandler() { @Override protected URLConnection openConnection(URL u) throws IOException { throw new UnsupportedOperationException("Not supported yet."); } }); } return null; }; EventQueue.invokeAndWait(() -> { try { config.url = urlSelector.call(); } catch (Exception ex) { Logger.getLogger(PlatypusClientApplication.class.getName()).log(Level.SEVERE, null, ex); } }); } } protected PlatypusClientApplication() { super(); } public static void checkUserHome() { String home = System.getProperty(ClientConstants.USER_HOME_PROP_NAME); if (home == null || home.isEmpty()) { throw new IllegalArgumentException(USER_HOME_MISSING_MSG); } if (!(new File(home)).exists()) { throw new IllegalArgumentException(USER_HOME_ABSENTFILE_MSG); } if (!(new File(home)).isDirectory()) { throw new IllegalArgumentException(USER_HOME_NOT_A_DIRECTORY_MSG); } } }