Package jodd.joy.core

Source Code of jodd.joy.core.DefaultAppCore

// Copyright (c) 2003-2014, Jodd Team (jodd.org). All Rights Reserved.

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.db.pool.CoreConnectionPool;
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 <code>DbManager</code> instance.
   */
  public static final String PETITE_DB = "db";
  /**
   * Petite bean name for <code>DbOomManager</code> 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 <code>true</code> 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.
   * <p>
   * 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") == false) {
      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.getWorkingFolder();
  }

  // ---------------------------------------------------------------- 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 <code>sys.*</code>)
   * and then environment properties (registered as <code>env.*</code>).
   * Finally, props files are read from the classpath. All properties
   * are loaded using
   * <p>
   * 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:
   * <ul>
   * <li>Transaction proxy - applied on all classes that contains public top-level methods
   * annotated with <code>@Transaction</code> annotation. This is just one way how proxies
   * can be applied - since base configuration is in Java, everything is possible.</li>
   * </ul>
   */
  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 == false) {
      // 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<? extends Annotation>[] 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
    Class<? extends ConnectionProvider> connectionProviderClass = getConnectionProviderType();
    petite.registerPetiteBean(connectionProviderClass, PETITE_DBPOOL, null, null, false);
    connectionProvider = (ConnectionProvider) petite.getBean(PETITE_DBPOOL);
    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 <code>ConnectionProvider</code> implementation.
   */
  protected Class<? extends ConnectionProvider> getConnectionProviderType() {
    return CoreConnectionPool.class;
  }

  /**
   * 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 = (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();
    }
  }

}
TOP

Related Classes of jodd.joy.core.DefaultAppCore

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.