All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jodd.joy.core.DefaultAppCore Maven / Gradle / Ivy

Go to download

Jodd Joy is set of Jodd extensions that makes development of web applications even easier.

There is a newer version: 5.3.0
Show newest version
// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

package jodd.joy.core;

import jodd.db.DbManager;
import jodd.db.DbSessionProvider;
import jodd.db.connection.ConnectionProvider;
import jodd.db.oom.DbOomManager;
import jodd.db.oom.config.AutomagicDbOomConfigurator;
import jodd.joy.exception.AppException;
import jodd.joy.jtx.meta.ReadWriteTransaction;
import jodd.jtx.JtxTransactionManager;
import jodd.db.jtx.DbJtxSessionProvider;
import jodd.db.jtx.DbJtxTransactionManager;
import jodd.jtx.meta.Transaction;
import jodd.jtx.proxy.AnnotationTxAdvice;
import jodd.jtx.proxy.AnnotationTxAdviceManager;
import jodd.jtx.proxy.AnnotationTxAdviceSupport;
import jodd.petite.PetiteContainer;
import jodd.petite.config.AutomagicPetiteConfigurator;
import jodd.petite.proxetta.ProxettaAwarePetiteContainer;
import jodd.petite.scope.SessionScope;
import jodd.petite.scope.SingletonScope;
import jodd.props.Props;
import jodd.props.PropsUtil;
import jodd.proxetta.MethodInfo;
import jodd.proxetta.ProxyAspect;
import jodd.proxetta.impl.ProxyProxetta;
import jodd.proxetta.pointcuts.MethodAnnotationPointcut;
import jodd.util.ClassLoaderUtil;
import jodd.util.SystemUtil;
import jodd.log.Logger;
import jodd.log.LoggerFactory;

import java.lang.annotation.Annotation;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;

/**
 * Default application core. Contains init points to
 * all application frameworks and layers.
 */
public abstract class DefaultAppCore {

	/**
	 * Application system property - application folder.
	 */
	public static final String APP_DIR = "app.dir";
	/**
	 * Application system property - flag if web application is detected..
	 */
	public static final String APP_WEB = "app.web";

	/**
	 * Petite bean name for AppCore (this instance).
	 */
	public static final String PETITE_CORE = "core";
	/**
	 * Petite bean name for database pool.
	 */
	public static final String PETITE_DBPOOL = "dbpool";
	/**
	 * Petite bean name for DbManager instance.
	 */
	public static final String PETITE_DB = "db";
	/**
	 * Petite bean name for DbOomManager instance.
	 */
	public static final String PETITE_DBOOM = "dboom";
	/**
	 * Petite bean name for {@link AppInit} bean.
	 */
	public static final String PETITE_INIT = "init";
	/**
	 * Petite bean name for application props.
	 */
	public static final String PETITE_PROPS = "props";
	/**
	 * Petite bean name for {@link AppScanner} bean.
	 */
	public static final String PETITE_SCAN = "scan";


	/**
	 * Logger. Resolved during {@link #initLogger() initialization}.
	 */
	protected static Logger log;

	/**
	 * App dir. Resolved during initialization.
	 */
	protected String appDir;

	/**
	 * Is web application. Resolved during initialization.
	 */
	protected boolean isWebApplication;

	/**
	 * Props profiles. If set, overrides any default profile set
	 * in the props files.
	 */
	protected String[] appPropsProfiles;

	/**
	 * Default constructor.
	 */
	protected DefaultAppCore() {
	}

	// ---------------------------------------------------------------- init
	
	/**
	 * Returns true if application is started as a part of web application.
	 */
	public boolean isWebApplication() {
		return isWebApplication;
	}

	/**
	 * Returns application directory.
	 */
	public String getAppDir() {
		return appDir;
	}

	/**
	 * Initializes application core, invoked very first!
	 * Important: logging is not yet available in this method!
	 */
	@SuppressWarnings("unchecked")
	public void initCore() {
		if (appPropsName == null) {
			appPropsName = "app.props";
		}

		if (appPropsNamePattern == null) {
			int index = appPropsName.lastIndexOf('.');

			appPropsNamePattern = '/' + appPropsName.substring(0, index) + "*.prop*";
		}

		if (jtxAnnotations == null) {
			jtxAnnotations = new Class[] {Transaction.class, ReadWriteTransaction.class};
		}

		if (jtxScopePattern == null) {
			jtxScopePattern = "$class";
		}

		if (appDir == null) {
			resolveAppDir(appPropsName);		// app directory is resolved from location of 'app.props'.
		}

		System.setProperty(APP_DIR, appDir);
		System.setProperty(APP_WEB, Boolean.toString(isWebApplication));
	}

	/**
	 * Initializes the logger, after the log path is {@link #init() defined}.
	 */
	protected void initLogger() {
		if (log != null) {
			return;
		}

		log = LoggerFactory.getLogger(DefaultAppCore.class);
		log.info("app dir: " + appDir);
	}

	/**
	 * Resolves application root folders.
	 * 

* If application is started as web application, app folder is one below the WEB-INF folder. * Otherwise, the root folder is equal to the working folder. */ protected void resolveAppDir(String classPathFileName) { URL url = ClassLoaderUtil.getResourceUrl(classPathFileName); if (url == null) { throw new AppException("Failed to resolve app dir, missing: " + classPathFileName); } String protocol = url.getProtocol(); if (!protocol.equals("file")) { try { url = new URL(url.getFile()); } catch (MalformedURLException ignore) { } } appDir = url.getFile(); int ndx = appDir.indexOf("WEB-INF"); isWebApplication = (ndx != -1); appDir = isWebApplication ? appDir.substring(0, ndx) : SystemUtil.workingFolder(); } // ---------------------------------------------------------------- ready /** * Called after the {@link #init() core initialization}, * during the {@link #start() application startup}. */ protected void ready() { } // ---------------------------------------------------------------- start protected boolean initialized; /** * Initializes application. * May be called several times, but the core * will be initialized just once. * Usually called manually when core needs to * be created before server is started * (e.g. in embedded environments) */ public void init() { if (initialized) { return; } initCore(); initLogger(); initProps(); initScanner(); initialized = true; } /** * Starts the application and performs all initialization. */ public void start() { init(); ready(); try { startProxetta(); startPetite(); startDb(); startApp(); log.info("app started"); } catch (RuntimeException rex) { if (log != null) { log.error(rex.toString(), rex); } else { System.out.println(rex.toString()); rex.printStackTrace(); } try { stop(); } catch (Exception ignore) { } throw rex; } } /** * Stops the application. */ public void stop() { if (log != null) { log.info("shutting down..."); } stopApp(); stopDb(); stopPetite(); if (log != null) { log.info("app stopped"); } } // ---------------------------------------------------------------- props /** * Main application props file name, must exist in class path. */ protected String appPropsName; /** * Application props file name pattern. */ protected String appPropsNamePattern; /** * Application props. */ protected Props appProps; /** * Returns applications properties loaded from props files. */ public Props getAppProps() { return appProps; } /** * Creates and loads application props. * It first loads system properties (registered as sys.*) * and then environment properties (registered as env.*). * Finally, props files are read from the classpath. All properties * are loaded using *

* If props have been already loaded, does nothing. */ protected void initProps() { if (appProps != null) { return; } appProps = createProps(); appProps.loadSystemProperties("sys"); appProps.loadEnvironment("env"); PropsUtil.loadFromClasspath(appProps, appPropsNamePattern); if (appPropsProfiles != null) { appProps.setActiveProfiles(appPropsProfiles); } } /** * Creates new Props. Empty props will be ignored, * and missing macros will be resolved as empty string. */ protected Props createProps() { Props props = new Props(); props.setSkipEmptyProps(true); props.setIgnoreMissingMacros(true); return props; } // ---------------------------------------------------------------- scanning protected AppScanner appScanner; /** * Returns scanner. */ public AppScanner getAppScanner() { return appScanner; } /** * Initializes {@link AppScanner}. */ protected void initScanner() { if (appScanner != null) { return; } appScanner = new AppScanner(this); } // ---------------------------------------------------------------- proxetta protected ProxyProxetta proxetta; /** * Returns proxetta. */ public ProxyProxetta getProxetta() { return proxetta; } /** * Creates Proxetta with all aspects. The following aspects are created: *

    *
  • Transaction proxy - applied on all classes that contains public top-level methods * annotated with @Transaction annotation. This is just one way how proxies * can be applied - since base configuration is in Java, everything is possible.
  • *
*/ protected void startProxetta() { log.info("proxetta initialization"); proxetta = ProxyProxetta.withAspects(createAppAspects()); } /** * Creates all application aspects. By default it creates just * {@link #createTxProxyAspects() transactional aspect}. */ protected ProxyAspect[] createAppAspects() { return new ProxyAspect[] {createTxProxyAspects()}; } /** * Creates TX aspect that will be applied on all classes * having at least one public top-level method annotated * with {@link #jtxAnnotations registered JTX annotations}. */ protected ProxyAspect createTxProxyAspects() { return new ProxyAspect( AnnotationTxAdvice.class, new MethodAnnotationPointcut(jtxAnnotations) { @Override public boolean apply(MethodInfo methodInfo) { return isPublic(methodInfo) && isTopLevelMethod(methodInfo) && super.apply(methodInfo); } }); } // ---------------------------------------------------------------- petite protected PetiteContainer petite; /** * Returns application container (Petite). */ public PetiteContainer getPetite() { return petite; } /** * Creates and initializes Petite container. * It will be auto-magically configured by scanning the classpath. * Also, all 'app*.prop*' will be loaded and values will * be injected in the matched beans. At the end it registers * this instance of core into the container. */ protected void startPetite() { log.info("petite initialization"); petite = createPetiteContainer(); log.info("app in web: " + Boolean.valueOf(isWebApplication)); if (!isWebApplication) { // make session scope to act as singleton scope // if this is not a web application (and http session is not available). petite.registerScope(SessionScope.class, new SingletonScope()); } // load parameters from properties files petite.defineParameters(appProps); // adds a scanner bean, so it can be immediately configured from props petite.addBean(PETITE_SCAN, appScanner); // automagic configuration registerPetiteContainerBeans(petite); // add AppCore instance to Petite petite.addBean(PETITE_CORE, this); petite.addBean(PETITE_PROPS, appProps); } /** * Configures Petite container. By default scans the class path * for petite beans and registers them automagically. */ protected void registerPetiteContainerBeans(PetiteContainer petiteContainer) { AutomagicPetiteConfigurator pcfg = new AutomagicPetiteConfigurator(); appScanner.configure(pcfg); pcfg.configure(petiteContainer); } /** * Creates Petite container. By default, it creates * {@link jodd.petite.proxetta.ProxettaAwarePetiteContainer proxetta aware petite container}. */ protected PetiteContainer createPetiteContainer() { return new ProxettaAwarePetiteContainer(proxetta); } /** * Stops Petite container. */ protected void stopPetite() { if (petite != null) { petite.shutdown(); } } // ---------------------------------------------------------------- database protected boolean useDatabase = true; /** * JTX manager. */ protected JtxTransactionManager jtxManager; /** * Returns JTX transaction manager. */ public JtxTransactionManager getJtxManager() { return jtxManager; } /** * Database connection provider. */ protected ConnectionProvider connectionProvider; /** * JTX annotations. */ protected Class[] jtxAnnotations; /** * JTX scope pattern. * @see AnnotationTxAdviceManager */ protected String jtxScopePattern; /** * Initializes database. First, creates connection pool. * and transaction manager. Then, Jodds DbOomManager is * configured. It is also configured automagically, by scanning * the class path for entities. */ @SuppressWarnings("unchecked") protected void startDb() { if (!useDatabase) { log.info("database is not used"); return; } log.info("database initialization"); // connection pool connectionProvider = createConnectionProvider(); petite.addBean(PETITE_DBPOOL, connectionProvider); connectionProvider.init(); checkConnectionProvider(); // transactions manager jtxManager = createJtxTransactionManager(connectionProvider); jtxManager.setValidateExistingTransaction(true); AnnotationTxAdviceManager annTxAdviceManager = new AnnotationTxAdviceManager(jtxManager, jtxScopePattern); annTxAdviceManager.registerAnnotations(jtxAnnotations); AnnotationTxAdviceSupport.manager = annTxAdviceManager; DbSessionProvider sessionProvider = new DbJtxSessionProvider(jtxManager); // global settings DbManager dbManager = DbManager.getInstance(); dbManager.setConnectionProvider(connectionProvider); dbManager.setSessionProvider(sessionProvider); petite.addBean(PETITE_DB, dbManager); DbOomManager dbOomManager = DbOomManager.getInstance(); petite.addBean(PETITE_DBOOM, dbOomManager); // automatic configuration registerDbEntities(dbOomManager); } /** * Registers DbOom entities. By default, scans the * class path and register entities automagically. */ protected void registerDbEntities(DbOomManager dbOomManager) { AutomagicDbOomConfigurator dbcfg = new AutomagicDbOomConfigurator(); appScanner.configure(dbcfg); dbcfg.configure(dbOomManager); } /** * Creates JTX transaction manager. */ protected JtxTransactionManager createJtxTransactionManager(ConnectionProvider connectionProvider) { return new DbJtxTransactionManager(connectionProvider); } /** * Returns ConnectionProvider instance. */ protected ConnectionProvider createConnectionProvider() { throw new UnsupportedOperationException("Please provide ConnectionProvider implementation."); } /** * Checks if connection provider can return a connection. */ protected void checkConnectionProvider() { Connection connection = connectionProvider.getConnection(); try { DatabaseMetaData databaseMetaData = connection.getMetaData(); String name = databaseMetaData.getDatabaseProductName(); String version = databaseMetaData.getDatabaseProductVersion(); if (log.isInfoEnabled()) { log.info("Connected to database: " + name + " v" + version); } } catch (SQLException sex) { log.error("DB connection failed: ", sex); } finally { connectionProvider.closeConnection(connection); } } /** * Closes database resources at the end. */ protected void stopDb() { if (!useDatabase) { return; } if (log != null) { log.info("database shutdown"); } if (jtxManager != null) { jtxManager.close(); } if (connectionProvider != null) { connectionProvider.close(); } } // ---------------------------------------------------------------- init protected AppInit appInit; /** * Initializes business part of the application. * Simply delegates to {@link AppInit#init()}. */ protected void startApp() { appInit = petite.getBean(PETITE_INIT); if (appInit != null) { appInit.init(); } } /** * Stops business part of the application. * Simply delegates to {@link AppInit#stop()}. */ protected void stopApp() { if (appInit != null) { appInit.stop(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy