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

com.anaptecs.jeaf.xfun.api.XFun Maven / Gradle / Ivy

The newest version!
/**
 * Copyright 2004 - 2019 anaptecs GmbH, Burgstr. 96, 72764 Reutlingen, Germany
 *
 * All rights reserved.
 */
package com.anaptecs.jeaf.xfun.api;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.anaptecs.jeaf.xfun.annotations.XFunConfig;
import com.anaptecs.jeaf.xfun.api.checks.Verifier;
import com.anaptecs.jeaf.xfun.api.checks.VerifierFactory;
import com.anaptecs.jeaf.xfun.api.config.ConfigurationProvider;
import com.anaptecs.jeaf.xfun.api.config.ConfigurationProviderFactory;
import com.anaptecs.jeaf.xfun.api.datatypeconverter.DatatypeConverterRegistry;
import com.anaptecs.jeaf.xfun.api.datatypeconverter.DatatypeConverterRegistryFactory;
import com.anaptecs.jeaf.xfun.api.info.ApplicationInfo;
import com.anaptecs.jeaf.xfun.api.info.InfoProvider;
import com.anaptecs.jeaf.xfun.api.info.InfoProviderFactory;
import com.anaptecs.jeaf.xfun.api.info.VersionInfo;
import com.anaptecs.jeaf.xfun.api.locale.LocaleProvider;
import com.anaptecs.jeaf.xfun.api.locale.LocaleProviderFactory;
import com.anaptecs.jeaf.xfun.api.messages.MessageRepository;
import com.anaptecs.jeaf.xfun.api.messages.MessageRepositoryFactory;
import com.anaptecs.jeaf.xfun.api.principal.PrincipalProvider;
import com.anaptecs.jeaf.xfun.api.principal.PrincipalProviderFactory;
import com.anaptecs.jeaf.xfun.api.trace.StartupInfoConfiguration;
import com.anaptecs.jeaf.xfun.api.trace.StartupInfoEventCollector;
import com.anaptecs.jeaf.xfun.api.trace.StartupInfoEventHandler;
import com.anaptecs.jeaf.xfun.api.trace.StartupInfoWriter;
import com.anaptecs.jeaf.xfun.api.trace.Trace;
import com.anaptecs.jeaf.xfun.api.trace.TraceLevel;
import com.anaptecs.jeaf.xfun.api.trace.TraceProvider;
import com.anaptecs.jeaf.xfun.api.trace.TraceProviderFactory;
import com.anaptecs.jeaf.xfun.fallback.checks.FallbackVerifierImpl;
import com.anaptecs.jeaf.xfun.fallback.trace.FallbackTraceProviderFactoryImpl;
import com.anaptecs.jeaf.xfun.fallback.trace.FallbackTraceProviderImpl;

public final class XFun implements StartupInfoWriter, StartupInfoEventHandler {
  /**
   * Name of the base path under which all X-Fun configuration files are located.
   */
  public static final String X_FUN_BASE_PATH = "META-INF/JEAF/XFun";

  /**
   * Name of the class that contains the default configuration annotation.
   */
  public static final String DEFAULT_CONFIGURATION_CLASS = "com.anaptecs.jeaf.xfun.impl.DefaultXFunConfiguration";

  /**
   * Constant for the name of the system property that can be used to overwrite the default resource name
   * {@link XFunConfig#X_FUN_CONFIG_RESOURCE_NAME}
   */
  public static final String X_FUN_CONFIG_RESOURCE_NAME = "xfun.config.resourceName";

  /**
   * Constant for the name of the system property that can be used to overwrite the default resource path
   * {@link XFun#X_FUN_BASE_PATH}
   */
  public static final String X_FUN_CONFIG_RESOURCE_PATH = "xfun.config.resourcePath";

  /**
   * Constant for the system property that can be used to overwrite default exception handling when loading JEAF
   * configuration. By default no exception will be thrown.
   */
  public static final String X_FUN_CONFIG_EXCEPTION_ON_ERROR = "xfun.config.execptionOnError";

  /**
   * Fallback verifier is used at least during startup as at that time configured implementation may not be available
   * already.
   */
  private static final Verifier FALLBACK_VERIFIER = new FallbackVerifierImpl();

  /**
   * Constant for format string that is used when tracing names of used implementations.
   */
  public static final String IMPL_INFO_FORMAT_STRING = "    %1$-40s%2$s";

  /**
   * Current Instance of this class. Existing instances can be replaced by calling
   */
  private static XFun instance;

  /**
   * Attribute marks if tracing is initialized or not. Attribute must only be used in cases where {@link XFun#instance}
   * is null.
   */
  private static boolean tracingInitialized = false;

  /**
   * Lock object to avoid multiple initializations of tracing in multi-threaded cases.
   */
  private static final Object TRACE_INIT_LOCK = new Object();

  /**
   * Timestamp when the XFun instance was created.
   */
  private final long startTimeStamp;

  /**
   * Reference to used X-Fun configuration.
   */
  private final XFunConfiguration configuration;

  /**
   * Reference to configured configuration provider.
   */
  private final ConfigurationProviderFactory configurationProviderFactory;

  /**
   * Reference to configured message repository factory.
   */
  private final MessageRepositoryFactory messageRepositoryFactory;

  /**
   * Reference to configured verifier factory.
   */
  private final VerifierFactory verifierFactory;

  /**
   * Reference to configured locale provider factory.
   */
  private final LocaleProviderFactory localeProviderFactory;

  /**
   * Reference to configured principal provider factory.
   */
  private final PrincipalProviderFactory principalProviderFactory;

  /**
   * Reference to configured info provider factory.
   */
  private final InfoProviderFactory infoProviderFactory;

  /**
   * Reference to configured datatype converter registry factory.
   */
  private final DatatypeConverterRegistryFactory datatypeConverterRegistryFactory;

  /**
   * Reference to configured trace provider factory. In order to avoid problems during startup it will be lazy loaded.
   */
  private TraceProviderFactory traceProviderFactory;

  /**
   * Reference to configuration for startup info.
   */
  private final StartupInfoConfiguration startupInfoConfiguration;

  /**
   * Map contains all {@link StartupInfoWriter} that are configured.
   */
  private final Map, List> startupInfoWriters = new HashMap<>();

  /**
   * Method resolved the configuration resource name that contains the JEAF configuration. This name can be configured
   * using the system property {@value #X_FUN_CONFIG_RESOURCE_NAME}. If the system property is not set then the default
   * configuration file (see {@link XFunConfig#X_FUN_CONFIG_RESOURCE_NAME}) will be used.
   * 
   * @return {@link String} Name of the configuration file. The method never returns null.
   */
  private static String getConfigurationResourceName( ) {
    return System.getProperty(X_FUN_CONFIG_RESOURCE_NAME, XFunConfig.X_FUN_CONFIG_RESOURCE_NAME);
  }

  /**
   * Method resolved the configuration path that contains the JEAF configuration. This path can be configured using the
   * system property {@value #X_FUN_CONFIG_RESOURCE_PATH}. If the system property is not set then the default
   * configuration file (see {@link XFun#X_FUN_BASE_PATH}) will be used.
   * 
   * @return {@link String} Path of the configuration file. The method never returns null.
   */
  private static String getConfigurationBasePackagePath( ) {
    return System.getProperty(X_FUN_CONFIG_RESOURCE_PATH, XFun.X_FUN_BASE_PATH);
  }

  /**
   * Method determines if an exception should be thrown in case of configuration problems. This value can be configured
   * through system property {@value #X_FUN_CONFIG_EXCEPTION_ON_ERROR}. By default JEAF tries to continue loading
   * without throwing an exception.
   * 
   * @return boolean Method returns true if an exception should be thrown and false otherwise.
   */
  private static boolean getExceptionOnError( ) {
    return Boolean.getBoolean(X_FUN_CONFIG_EXCEPTION_ON_ERROR);
  }

  /**
   * Initialize single instance of this class.
   */
  static {
    try {
      instance = new XFun();
      StartupInfoEventCollector.startupCompleted(XFun.class);
      StartupInfoEventCollector.registerEventHandler(instance);
    }
    catch (RuntimeException e) {
      Trace lTrace = FallbackTraceProviderImpl.EMERGENCY_TRACE;
      lTrace.fatal(
          "Caught exception during JEAF X-Fun initialization. Most likely this is caused by some missing configuration file or problems with your classpath.");
      lTrace.fatal(e);
      throw e;
    }
  }

  /**
   * Initialize object. Which configuration class will be used can be defined through the system properties
   * {@value #X_FUN_CONFIG_RESOURCE_NAME}, {@value #X_FUN_CONFIG_RESOURCE_PATH} and
   * {@value #X_FUN_CONFIG_EXCEPTION_ON_ERROR}. If one of the parameter or all of them are not defined then their
   * default values will be used.
   * 
   * If nothing special is defined then the X-Fun configuration will be read from the class that is defined in file
   * {@link XFunConfig#XFUN_CONFIG_PATH}
   */
  private XFun( ) {
    // Start initialization
    startTimeStamp = System.currentTimeMillis();
    Trace lTrace = FallbackTraceProviderImpl.EMERGENCY_TRACE;
    lTrace.info("Starting JEAF X-Fun initialization.");

    // Trace classpath
    String lClasspath = System.getProperty("java.class.path");

    // Depending on the operating system classpath entries havew different separators
    String lPathSeparator = System.getProperty("path.separator");
    String[] lClasspathEntries = lClasspath.split(lPathSeparator);
    lTrace.info("Classpath:");
    for (String lNextEntry : lClasspathEntries) {
      lTrace.info("    " + lNextEntry);

    }

    // Trace OS information
    String lOSInfo = "Operating System: " + System.getProperty("os.name") + " (Version: "
        + System.getProperty("os.version") + ", Architecture: " + System.getProperty("os.arch") + ")";
    lTrace.info(lOSInfo);

    // Trace JVM information.
    String lJVMInfo = "Java Virtual Machine: " + System.getProperty("java.runtime.name") + " "
        + System.getProperty("java.version") + " (Vendor: " + System.getProperty("java.vm.vendor") + ")";
    lTrace.info(lJVMInfo);

    // Trace runtime information
    Runtime lRuntime = Runtime.getRuntime();
    int lProcessors = lRuntime.availableProcessors();
    long lFreeMemory = lRuntime.freeMemory() / (1024 * 1024);
    long lMaxMemory = lRuntime.maxMemory() / (1024 * 1024);
    long lTotalMemory = lRuntime.totalMemory() / (1024 * 1024);
    String lRuntimeInfo = "Runtime Environment: " + lProcessors + " Cores, JVM Memory: " + lTotalMemory
        + " MB available, " + lMaxMemory + " MB maximum, " + lFreeMemory + " MB free";

    lTrace.info(lRuntimeInfo);

    // Resolve XFun configuration.
    String lConfigurationResourceName = XFun.getConfigurationResourceName();
    String lConfigurationBasePackagePath = XFun.getConfigurationBasePackagePath();
    boolean lExceptionOnError = XFun.getExceptionOnError();
    configuration = new XFunConfiguration(lConfigurationResourceName, lConfigurationBasePackagePath, lExceptionOnError);

    // Load all configured factory classes except for tracing.
    messageRepositoryFactory = configuration.getMessageRepositoryFactory();
    configurationProviderFactory = configuration.getConfigurationProviderFactory();
    verifierFactory = configuration.getVerifierFactory();
    localeProviderFactory = configuration.getLocaleProviderFactory();
    principalProviderFactory = configuration.getPrincipalProviderFactory();
    infoProviderFactory = configuration.getInfoProviderFactory();
    datatypeConverterRegistryFactory = configuration.getDatatypeConverterRegistryFactory();

    // Load startup info writers
    startupInfoConfiguration = new StartupInfoConfiguration();
    List lStartupInfoWriters = startupInfoConfiguration.getStartupInfoWriters();
    lStartupInfoWriters.add(0, this);
    for (StartupInfoWriter lStartupInfoWriter : lStartupInfoWriters) {
      Class lEventSource = lStartupInfoWriter.getStartupCompletedEventSource();
      List lWritersList = startupInfoWriters.computeIfAbsent(lEventSource, p -> new ArrayList<>());
      lWritersList.add(lStartupInfoWriter);
    }

    // Trace provider factory will be loaded lazy to avoid trouble during startup.
  }

  /**
   * Method returns only instance of this class.
   * 
   * @return {@link XFun} Instance of this class. The method never returns null.
   */
  private static final XFun getInstance( ) {
    return instance;
  }

  /**
   * Method returns the configured message repository.
   * 
   * @return {@link MessageRepository} Message repository that is used. The method never returns null.
   */
  public static MessageRepository getMessageRepository( ) {
    return XFun.getInstance().messageRepositoryFactory.getMessageRepository();
  }

  /**
   * Method returns the configured verifier.
   * 
   * @return {@link Verifier} Verifier that is used. The method never returns null.
   */
  public static Verifier getVerifier( ) {
    // During initialization there may be cases where an instance of XFun does not exist yet.
    XFun lXFunInstance = XFun.getInstance();
    Verifier lVerifier;
    if (lXFunInstance != null) {
      lVerifier = lXFunInstance.verifierFactory.getVerifier();
      if (lVerifier == null) {
        lVerifier = FALLBACK_VERIFIER;
      }
    }
    // During initialization we will also use our fallback verifier.
    else {
      lVerifier = FALLBACK_VERIFIER;
    }
    return lVerifier;
  }

  /**
   * Method returns the configured configuration provider.
   * 
   * @return {@link ConfigurationProvider} Configuration provider that is used. The method never returns null.
   */
  public static ConfigurationProvider getConfigurationProvider( ) {
    return XFun.getInstance().configurationProviderFactory.getConfigurationProvider();
  }

  /**
   * Method returns the configured locale provider.
   * 
   * @return {@link LocaleProvider} Locale provider that is used. The method never returns null.
   */
  public static LocaleProvider getLocaleProvider( ) {
    return XFun.getInstance().localeProviderFactory.getLocaleProvider();
  }

  /**
   * Method returns the configured principal provider.
   * 
   * @return {@link PrincipalProvider} Principal provider that is used. The method never returns null.
   */
  public static PrincipalProvider getPrincipalProvider( ) {
    return XFun.getInstance().principalProviderFactory.getPrincipalProvider();
  }

  /**
   * Method returns the configured info provider.
   * 
   * @return {@link InfoProvider} Info provider that is used. The method never returns null.
   */
  public static InfoProvider getInfoProvider( ) {
    return XFun.getInstance().infoProviderFactory.getInfoProvider();
  }

  /**
   * Method returns version info of the current application. If no version info is available the a special unknown
   * version is returned.
   * 
   * @return {@link VersionInfo} Version info of the application. The method never returns null.
   */
  public static VersionInfo getVersionInfo( ) {
    return XFun.getInfoProvider().getApplicationInfo().getVersion();
  }

  /**
   * Method returns the configured trace provider.
   * 
   * @return {@link TraceProvider} Trace provider that is used. The method never returns null.
   */
  public static TraceProvider getTraceProvider( ) {
    // In order to avoid problems during initialization we load the trace factory only on request.
    TraceProvider lTraceProvider;
    if (instance == null) {
      // We are still during initialization so we better use our fallback trace provider
      lTraceProvider = new FallbackTraceProviderFactoryImpl().getTraceProvider();
    }
    // Tracing is not yet initialized.
    else if (instance.traceProviderFactory == null) {
      // Avoid race conditions due to multiple trace initializations.
      synchronized (TRACE_INIT_LOCK) {
        // Ensure that tracing is initialized only once. During initialization of tracing it may happen that tracing is
        // already requested from within the same thread.
        if (tracingInitialized == false) {
          // Mark at first that we are initializing
          tracingInitialized = true;

          // Use configured factory to resolve the trace provider that should be used.
          TraceProviderFactory lTraceProviderFactory = instance.configuration.getTraceProviderFactory();

          // Trace provider factory could be loaded.
          if (lTraceProviderFactory != null) {
            instance.traceProviderFactory = lTraceProviderFactory;
          }
          // In case of configuration problems we will not be able to resolve a trace provider factory. In this case we
          // will fallback to the fallback trace provider
          else {
            instance.traceProviderFactory = new FallbackTraceProviderFactoryImpl();
            FallbackTraceProviderImpl.EMERGENCY_TRACE.error(
                "Configuration problems prevent that the configured trace provider can be used. Using fallback tracing instead.");
          }
          // Resolve trace provider that will be used.
          lTraceProvider = instance.traceProviderFactory.getTraceProvider();
        }
        // As we want to avoid problems due to multiple initializations of trace providers. This may happen in cases
        // that during initialization of tracing traces should be written.
        else {
          lTraceProvider = new FallbackTraceProviderFactoryImpl().getTraceProvider();
        }
      }
    }
    // Trace initialization is fully completed
    else {
      lTraceProvider = instance.traceProviderFactory.getTraceProvider();
    }
    return lTraceProvider;
  }

  /**
   * Method returns the configured datatype converter registry.
   * 
   * @return DatatypeConverterRegistry Registry that is used. The method never returns null.
   */
  public static DatatypeConverterRegistry getDatatypeConverterRegistry( ) {
    return XFun.getInstance().datatypeConverterRegistryFactory.getDatatypeConverterRegistry();
  }

  /**
   * Methods returns the trace object that should be used in the current context.
   * 
   * Remark: As the trace object depends on the current context it's not recommended to cache the result of this
   * call.
   * 
   * @return {@link Trace} Trace object for the current context. The method never returns null.
   */
  public static Trace getTrace( ) {
    return XFun.getTraceProvider().getCurrentTrace();
  }

  /**
   * Method returns the X-Fun configuration that is currently used.
   * 
   * @return {@link XFunConfiguration} Configuration that is currently used. The method never returns null.
   */
  public static XFunConfiguration getConfiguration( ) {
    return instance.configuration;
  }

  /**
   * Method resets the current X-Fun instance. This means that JEAF X-Fun will be reinitialized completely. This method
   * is mainly intended to be used for test purposes.
   */
  public static void reload( ) {
    // Create new X-Fun instance add trace startup info.
    XFun.getTrace().warn("Reloading JEAF X-Fun");
    XFun lNewInstance = new XFun();
    lNewInstance.traceStartupInfo(XFun.class);

    // Replace existing instance with new one.
    XFun.getTrace().warn("Existing instance of JEAF X-Fun replaced by new one.");
    instance = lNewInstance;
  }

  @Override
  public void handleStartupInfoEvent( Class pClass ) {
    this.traceStartupInfo(pClass);
  }

  /**
   * Method traces startup info about the current X-Fun configuration.
   */
  private void traceStartupInfo( Class pClass ) {
    boolean lTraceStartupInfo = startupInfoConfiguration.traceStartupInfo();
    if (lTraceStartupInfo == true) {
      Trace lTrace;
      try {
        lTrace = XFun.getTraceProvider().getCurrentTrace();
      }
      catch (Throwable e) {
        lTrace = FallbackTraceProviderImpl.EMERGENCY_TRACE;
        lTrace.writeInitInfo(
            "Unable to load configured trace provider. Using fallback to emergency trace implementation. "
                + e.getMessage(),
            TraceLevel.ERROR);
        lTrace.error(e);
      }
      if (lTrace.isInfoEnabled()) {
        // Try to resolve application info

        // Use configured trace level.
        TraceLevel lTraceLevel = startupInfoConfiguration.getStartupInfoTraceLevel();

        // Call all configured startup info writers.
        List lStartupInfoWriters = startupInfoWriters.get(pClass);
        if (lStartupInfoWriters != null) {
          for (StartupInfoWriter lWriter : lStartupInfoWriters) {
            lWriter.traceStartupInfo(lTrace, lTraceLevel);
          }
        }
      }
    }
  }

  @Override
  public Class getStartupCompletedEventSource( ) {
    return XFun.class;
  }

  @Override
  public void traceStartupInfo( Trace pTrace, TraceLevel pTraceLevel ) {
    // Trace application info
    ApplicationInfo lApplicationInfo;
    try {
      lApplicationInfo = XFun.getInfoProvider().getApplicationInfo();
      pTrace.writeInitInfo(lApplicationInfo.toString(), pTraceLevel);
    }
    catch (Throwable e) {
      lApplicationInfo = ApplicationInfo.UNKNOWN_APPLICATION;
      pTrace.writeInitInfo(lApplicationInfo.toString(), pTraceLevel.getLevelWithHigherPriority(TraceLevel.WARN));
    }

    // Trace version info.
    VersionInfo lVersion = lApplicationInfo.getVersion();
    if (lVersion.isUnknownVersion() == true) {
      pTrace.writeInitInfo("Using generic version info as fallback. Please see log above for further details.",
          pTraceLevel.getLevelWithHigherPriority(TraceLevel.WARN));
    }

    // Trace information about used implementations.
    pTrace.writeInitInfo("JEAF X-Fun configuration read from class: " + configuration.getConfigurationClass().getName(),
        pTraceLevel);
    pTrace.writeInitInfo("JEAF X-Fun uses the following implementations:", pTraceLevel);

    // Configuration provider
    String lConfigurationProvider =
        configurationProviderFactory != null ? configurationProviderFactory.getClass().getName() : "null";
    pTrace.writeInitInfo(String.format(IMPL_INFO_FORMAT_STRING,
        ConfigurationProviderFactory.class.getSimpleName() + ":", lConfigurationProvider), pTraceLevel);

    // Message repository
    String lMessageRepositoryFactory =
        messageRepositoryFactory != null ? messageRepositoryFactory.getClass().getName() : "null";
    pTrace.writeInitInfo(String.format(IMPL_INFO_FORMAT_STRING, MessageRepositoryFactory.class.getSimpleName() + ":",
        lMessageRepositoryFactory), pTraceLevel);

    // Verifier
    String lVerifierFactory = verifierFactory != null ? verifierFactory.getClass().getName() : "null";
    pTrace.writeInitInfo(
        String.format(IMPL_INFO_FORMAT_STRING, VerifierFactory.class.getSimpleName() + ":", lVerifierFactory),
        pTraceLevel);

    // Locale provider
    String lLocaleProviderFactory = localeProviderFactory != null ? localeProviderFactory.getClass().getName() : "null";
    pTrace.writeInitInfo(String.format(IMPL_INFO_FORMAT_STRING, LocaleProviderFactory.class.getSimpleName() + ":",
        lLocaleProviderFactory), pTraceLevel);

    // Principal provider
    String lPrincipalProviderFactory =
        principalProviderFactory != null ? principalProviderFactory.getClass().getName() : "null";
    pTrace.writeInitInfo(String.format(IMPL_INFO_FORMAT_STRING, PrincipalProviderFactory.class.getSimpleName() + ":",
        lPrincipalProviderFactory), pTraceLevel);

    // Info provider
    String lInfoProviderFactory = infoProviderFactory != null ? infoProviderFactory.getClass().getName() : "null";
    pTrace.writeInitInfo(
        String.format(IMPL_INFO_FORMAT_STRING, InfoProviderFactory.class.getSimpleName() + ":", lInfoProviderFactory),
        pTraceLevel);

    // Trace provider
    String lTraceProviderFactory = traceProviderFactory != null ? traceProviderFactory.getClass().getName()
        : "not yet loaded due to lazy initialization";
    pTrace.writeInitInfo(
        String.format(IMPL_INFO_FORMAT_STRING, TraceProviderFactory.class.getSimpleName() + ":", lTraceProviderFactory),
        pTraceLevel);

    // Datatype converter
    String lDatatypeConverterRegistryFactory =
        datatypeConverterRegistryFactory != null ? datatypeConverterRegistryFactory.getClass().getName() : "null";
    pTrace.writeInitInfo(String.format(IMPL_INFO_FORMAT_STRING,
        DatatypeConverterRegistryFactory.class.getSimpleName() + ":", lDatatypeConverterRegistryFactory), pTraceLevel);

    // Trace info about end of initialization.
    long lNow = System.currentTimeMillis();
    StringBuilder lBuilder = new StringBuilder();
    lBuilder.append("JEAF X-Fun initialization completed in ");
    lBuilder.append(lNow - startTimeStamp);
    lBuilder.append("ms.");
    pTrace.writeInitInfo(lBuilder.toString(), TraceLevel.INFO);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy