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

com.ibm.jbatch.container.servicesmanager.ServicesManagerImpl Maven / Gradle / Ivy

The newest version!
/*
 * 
 * Copyright 2012,2013 International Business Machines Corp.
 * 
 * See the NOTICE file distributed with this work for additional information
 * regarding copyright ownership. Licensed under the Apache License, 
 * Version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.ibm.jbatch.container.servicesmanager;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.ibm.jbatch.container.callback.IJobEndCallbackService;
import com.ibm.jbatch.container.exception.BatchContainerServiceException;
import com.ibm.jbatch.container.exception.PersistenceException;
import com.ibm.jbatch.container.impl.BatchConfigImpl;
import com.ibm.jbatch.container.services.IBatchKernelService;
import com.ibm.jbatch.container.services.IJobStatusManagerService;
import com.ibm.jbatch.container.services.IPersistenceManagerService;
import com.ibm.jbatch.container.servicesmanager.ServiceTypes.Name;
import com.ibm.jbatch.container.util.BatchContainerConstants;
import com.ibm.jbatch.spi.BatchSPIManager;
import com.ibm.jbatch.spi.BatchSPIManager.PlatformMode;
import com.ibm.jbatch.spi.DatabaseConfigurationBean;
import com.ibm.jbatch.spi.ServiceRegistry;
import com.ibm.jbatch.spi.services.IBatchArtifactFactory;
import com.ibm.jbatch.spi.services.IBatchServiceBase;
import com.ibm.jbatch.spi.services.IBatchThreadPoolService;
import com.ibm.jbatch.spi.services.IJobXMLLoaderService;
import com.ibm.jbatch.spi.services.ITransactionManagementService;


/**
 * Note a call to any of the getter methods besides getInstance() will perform the initialization routine and thereby
 * 'harden' the config.
 */
public class ServicesManagerImpl implements BatchContainerConstants, ServicesManager {

	private final static String sourceClass = ServicesManagerImpl.class.getName();
	private final static Logger logger = Logger.getLogger(sourceClass);
	
	private ServicesManagerImpl() { } 
	
	// Lazily-loaded singleton.
	private static class ServicesManagerImplHolder {
		private static final ServicesManagerImpl INSTANCE = new ServicesManagerImpl();
	}

	public static ServicesManager getInstance() {
		return ServicesManagerImplHolder.INSTANCE;
	}
	
	// Declared 'volatile' to allow use in double-checked locking.  This 'isInited'
	// refers to whether the configuration has been hardened and possibly the
	// first service impl loaded, not whether the instance has merely been instantiated.
	private final byte[] isInitedLock = new byte[0];
	private volatile Boolean isInited = Boolean.FALSE;

	private DatabaseConfigurationBean databaseConfigBean = null;
	private BatchConfigImpl batchConfigImpl;
	private Properties batchContainerProps = null;

	private	Map serviceImplClassNames = ServiceTypes.getServiceImplClassNames();
	private Map propertyNameTable = ServiceTypes.getServicePropertyNames();

	// Registry of all current services
	private final ConcurrentHashMap serviceRegistry = new ConcurrentHashMap();
	private PlatformMode platformMode = null;
	
	/**
	 * Init doesn't actually load the service impls, which are still loaded lazily.   What it does is it
	 * hardens the config.  This is necessary since the batch runtime by and large is not dynamically
	 * configurable, (e.g. via MBeans).  Things like the database config used by the batch runtime's
	 * persistent store are hardened then, as are the names of the service impls to use.
	 */
	private void initIfNecessary() {
		if (logger.isLoggable(Level.FINER)) {
			logger.config("In initIfNecessary().");
		}
		// Use double-checked locking with volatile.
		if (!isInited) {
			synchronized (isInitedLock) {
				if (!isInited) {
					logger.config("--- Initializing ServicesManagerImpl ---");
					batchConfigImpl = new BatchConfigImpl();

					// Read config
					readConfigFromPropertiesFiles();
					readConfigFromSPI();
					readConfigFromSystemProperties();

					// Set config in memory
					initBatchConfigImpl();
					initServiceImplOverrides();
					initDatabaseConfig();
					initPlatformSEorEE();

					isInited = Boolean.TRUE;
					
					logger.config("--- Completed initialization of ServicesManagerImpl ---");
				}
			}
		}

		logger.config("Exiting initIfNecessary()");
	}

	private void readConfigFromPropertiesFiles() {

		Properties serviceIntegratorProps = new Properties();
		InputStream batchServicesListInputStream = this.getClass()
				.getResourceAsStream("/META-INF/services/" + BATCH_INTEGRATOR_CONFIG_FILE);

		if (batchServicesListInputStream != null) {
			try {
				logger.config("Batch Integrator Config File exists! loading it..");
				serviceIntegratorProps.load(batchServicesListInputStream);
				batchServicesListInputStream.close();
			} catch (IOException e) {
				logger.config("Error loading " + "/META-INF/services/" + BATCH_INTEGRATOR_CONFIG_FILE + " IOException=" + e.toString());
			} catch (Exception e) {
				logger.config("Error loading " + "/META-INF/services/" + BATCH_INTEGRATOR_CONFIG_FILE + " Exception=" + e.toString());
			}
		} else {
			logger.config("Could not find batch integrator config file: " + "/META-INF/services/" + BATCH_INTEGRATOR_CONFIG_FILE);
		}

		// See if any do not map to service impls.

		Set removeThese = new HashSet();
		for (Object key : serviceIntegratorProps.keySet()) {
			String keyStr = (String) key;
			if (!propertyNameTable.containsKey(keyStr)) {
				logger.warning("Found property named: " + keyStr
						+ " with value: " + serviceIntegratorProps.get(keyStr)
						+ " in " + BATCH_INTEGRATOR_CONFIG_FILE + " , but did not find a corresponding service type "
						+ "in the internal table of service types.\n Ignoring this property then.   Maybe this should have been set in batch-config.properties instead.");
				removeThese.add(keyStr);
			}
		}
		for (String s : removeThese) {
			serviceIntegratorProps.remove(s);
		}

		Properties adminProps = new Properties();
		InputStream batchAdminConfigListInputStream = this.getClass().getResourceAsStream("/META-INF/services/" + BATCH_ADMIN_CONFIG_FILE);

		if (batchServicesListInputStream != null) {
			try {
				logger.config("Batch Admin Config File exists! loading it..");
				adminProps.load(batchAdminConfigListInputStream);
				batchAdminConfigListInputStream.close();
			} catch (IOException e) {
				logger.config("Error loading " + "/META-INF/services/" + BATCH_ADMIN_CONFIG_FILE + " IOException=" + e.toString());
			} catch (Exception e) {
				logger.config("Error loading " + "/META-INF/services/" + BATCH_ADMIN_CONFIG_FILE + " Exception=" + e.toString());
			}
		} else {
			logger.config("Could not find batch admin config file: " + "/META-INF/services/" + BATCH_ADMIN_CONFIG_FILE);
		}

		// See if any DO map to service impls, which would be a mistake
		Set removeTheseToo = new HashSet();
		for (Object key : adminProps.keySet()) {
			String keyStr = (String) key;
			if (propertyNameTable.containsKey(keyStr)) {
				logger.warning("Found property named: " + keyStr + " with value: " + adminProps.get(keyStr) + " in "
						+ BATCH_ADMIN_CONFIG_FILE + " , but this is a batch runtime service configuration.\n"
						+ "Ignoring this property then, since this should have been set in batch-services.properties instead.");
				removeThese.add(keyStr);
			}
		}
		for (String s : removeTheseToo) {
			adminProps.remove(s);
		}

		// Merge the two into 'batchContainerProps'
		batchContainerProps = new Properties();
		batchContainerProps.putAll(adminProps);
		batchContainerProps.putAll(serviceIntegratorProps);

	}
	
	private void readConfigFromSPI() {
		//Merge in overrides from the SPI so the container can change properties
		batchContainerProps.putAll(BatchSPIManager.getInstance().getBatchContainerOverrideProperties());

		// Don't cache in the 'platformMode' field variable just yet, wait until config is complete for consistency.
		PlatformMode mode = BatchSPIManager.getInstance().getPlatformMode();
		if (mode != null) {
			if (mode.equals(PlatformMode.EE)) {
				logger.config("SPI configured platform selection of EE");
				batchContainerProps.setProperty(ServiceTypes.J2SE_MODE, "false");
			} else if (mode.equals(PlatformMode.SE)) {
				logger.config("SPI configured platform selection of SE");
				batchContainerProps.setProperty(ServiceTypes.J2SE_MODE, "true");
			}
		}
	}
	
	private void readConfigFromSystemProperties() {
		batchContainerProps.putAll(ServiceRegistry.getSystemPropertyOverrides());
	}

	private void initBatchConfigImpl() {
		logger.fine("Dumping contents of batchContainerProps after reading properties files and calling SPI.");
		for (Object key : batchContainerProps.keySet()) {
			logger.config("key = " + key);
			logger.config("value = " + batchContainerProps.get(key));
		}
		
		// Set this on the config. 
		// 
		// WARNING:  This sets us up for collisions since this is just a single holder of properties
		// potentially used by any service impl.
		batchConfigImpl.setConfigProperties(batchContainerProps);
	}

	/**
	 * The method name reflects the fact that we have default service impl classnames baked into
	 * the runtime that will be used in the absence of any config property.   The config is only
	 * necessary to 'override' one of these default impls.
	 */
	private void initServiceImplOverrides() {
		
		// For each property we care about (i.e that defines one of our service impls)
		for (String propKey : propertyNameTable.keySet()) {
			// If the property is defined
			String value = batchContainerProps.getProperty(propKey);
			if (value != null) {
				// Get the corresponding serviceType enum and store the value of
				// the key/value property pair in the table where we store the service impl classnames.
				Name serviceType = propertyNameTable.get(propKey);
				String defaultServiceImplClassName = serviceImplClassNames.get(serviceType); // For logging.
				serviceImplClassNames.put(serviceType, value.trim());
				logger.config("Overriding serviceType: " + serviceType + ", replacing default impl classname: " + 
							defaultServiceImplClassName + " with override impl class name: " + value.trim());
			}
		}
	}

	private void initDatabaseConfig() {
		if (databaseConfigBean == null) { 
			logger.config("First try to load 'suggested config' from BatchSPIManager");
			databaseConfigBean = BatchSPIManager.getInstance().getFinalDatabaseConfiguration();
			if (databaseConfigBean == null) { 
				logger.config("Loading database config from configuration properties file.");
				// Initialize database-related properties
				databaseConfigBean = new DatabaseConfigurationBean();
				databaseConfigBean.setJndiName(batchContainerProps.getProperty(JNDI_NAME, DEFAULT_JDBC_JNDI_NAME));
				databaseConfigBean.setJdbcDriver(batchContainerProps.getProperty(JDBC_DRIVER, DEFAULT_JDBC_DRIVER));
				databaseConfigBean.setJdbcUrl(batchContainerProps.getProperty(JDBC_URL, DEFAULT_JDBC_URL));
				databaseConfigBean.setDbUser(batchContainerProps.getProperty(DB_USER));
				databaseConfigBean.setDbPassword(batchContainerProps.getProperty(DB_PASSWORD));
				databaseConfigBean.setSchema(batchContainerProps.getProperty(DB_SCHEMA, DEFAULT_DB_SCHEMA));
			}
		}  else {
			// Currently we do not expected this path to be used by Glassfish
			logger.config("Database config has been set directly from SPI, do NOT load from properties file.");
		}
		// In either case, set this bean on the main config bean
		batchConfigImpl.setDatabaseConfigurationBean(databaseConfigBean);
	}


	// Push hardened config value onto batchConfigImpl and cache the value in a field.
	private void initPlatformSEorEE() {
		String seMode = serviceImplClassNames.get(Name.JAVA_EDITION_IS_SE_DUMMY_SERVICE);
		if (seMode.equalsIgnoreCase("true")) {
			platformMode = PlatformMode.SE;
			batchConfigImpl.setJ2seMode(true);
		} else {
			platformMode = PlatformMode.EE;
			batchConfigImpl.setJ2seMode(false);
		}
	}

	// Look up registry and return requested service if exist
	// If not exist, create a new one, add to registry and return that one
	private IBatchServiceBase getService(Name serviceType) throws BatchContainerServiceException {
		String sourceMethod = "getService";
		logger.entering(sourceClass, sourceMethod + ", serviceType=" + serviceType);

		initIfNecessary();

		IBatchServiceBase service = new ServiceLoader(serviceType).getService();

		logger.exiting(sourceClass, sourceMethod);
		return service;
	}

	/*
	 * 	public enum Name {
		JAVA_EDITION_IS_SE_DUMMY_SERVICE, 
		TRANSACTION_SERVICE, 
		PERSISTENCE_MANAGEMENT_SERVICE, 
		JOB_STATUS_MANAGEMENT_SERVICE, 
		BATCH_THREADPOOL_SERVICE, 
		BATCH_KERNEL_SERVICE, 
		JOB_ID_MANAGEMENT_SERVICE, 
		CALLBACK_SERVICE, 
		JOBXML_LOADER_SERVICE,                // Preferred
		DELEGATING_JOBXML_LOADER_SERVICE,      // Delegating wrapper
		CONTAINER_ARTIFACT_FACTORY_SERVICE,   // Preferred
		DELEGATING_ARTIFACT_FACTORY_SERVICE  // Delegating wrapper
	 */

	@Override
	public ITransactionManagementService getTransactionManagementService() {
		return (ITransactionManagementService)getService(Name.TRANSACTION_SERVICE);
	}

	@Override
	public IPersistenceManagerService getPersistenceManagerService() {
		return (IPersistenceManagerService)getService(Name.PERSISTENCE_MANAGEMENT_SERVICE);
	}

	@Override
	public IJobStatusManagerService getJobStatusManagerService() {
		return (IJobStatusManagerService)getService(Name.JOB_STATUS_MANAGEMENT_SERVICE);
	}

	@Override
	public IBatchThreadPoolService getThreadPoolService() {
		return (IBatchThreadPoolService)getService(Name.BATCH_THREADPOOL_SERVICE);
	}

	@Override
	public IBatchKernelService getBatchKernelService() {
		return (IBatchKernelService)getService(Name.BATCH_KERNEL_SERVICE);
	}

	@Override
	public IJobEndCallbackService getJobCallbackService() {
		return (IJobEndCallbackService)getService(Name.CALLBACK_SERVICE);
	}

	@Override
	public IJobXMLLoaderService getPreferredJobXMLLoaderService() {
		return (IJobXMLLoaderService)getService(Name.JOBXML_LOADER_SERVICE);
	}

	@Override
	public IJobXMLLoaderService getDelegatingJobXMLLoaderService() {
		return (IJobXMLLoaderService)getService(Name.DELEGATING_JOBXML_LOADER_SERVICE);
	}

	@Override
	public IBatchArtifactFactory getPreferredArtifactFactory() {
		return (IBatchArtifactFactory)getService(Name.CONTAINER_ARTIFACT_FACTORY_SERVICE);
	}
	
	@Override
	public IBatchArtifactFactory getDelegatingArtifactFactory() {
		return (IBatchArtifactFactory)getService(Name.DELEGATING_ARTIFACT_FACTORY_SERVICE);
	}

	/**
	 * Note this will always return a non-null platform mode, i.e. defaulting is
	 * taken care of.
	 * 
	 * @return mode signifying whether we are executing on an SE or EE platform.  
	 */
	@Override
	public PlatformMode getPlatformMode() {
		initIfNecessary();
		return platformMode;
	}
	
	private class ServiceLoader {
		
		volatile IBatchServiceBase service = null;
		private Name serviceType = null;

		private ServiceLoader(Name name) {
			this.serviceType = name;
		}

		private IBatchServiceBase getService() {
			service = serviceRegistry.get(serviceType);
			if (service == null) {
				// Probably don't want to be loading two on two different threads so lock the whole table.
				synchronized (serviceRegistry) {
					if (service == null) {
						service = _loadServiceHelper(serviceType);
						service.init(batchConfigImpl);
						serviceRegistry.putIfAbsent(serviceType, service);
					}
				}
			}
			return service;
		}
		/**
		 * Try to load the service impl given by the className.
		 */
		private IBatchServiceBase _loadServiceHelper(Name serviceType) {
			IBatchServiceBase service = null;

			String className = serviceImplClassNames.get(serviceType);
			try {
				if (className != null) {
					service = _loadService(className);
				}
			} catch (Throwable t) {
				logger.log(Level.SEVERE, "Could not instantiate service: " + className + " due to exception:" + t);
				throw new RuntimeException("Could not instantiate service " + className, t);
			}

			if (service == null) {
				throw new RuntimeException("Instantiate of service=: " + className + " for serviceType: " + serviceType + " returned null. Aborting...");
			}

			return service;
		}

		private IBatchServiceBase _loadService(String className) throws Exception {

			IBatchServiceBase service = null;
			Class cls;
			try {
				cls = Class.forName(className);
			} catch (ClassNotFoundException cnfe) {
				cls = Thread.currentThread().getContextClassLoader().loadClass(className);
			}

			if (cls != null) {
				Constructor ctor = cls.getConstructor();
				if (ctor != null) {
					service = (IBatchServiceBase) ctor.newInstance();
				} else {
					throw new Exception("Service class " + className + " should  have a default constructor defined");
				}
			} else {
				throw new Exception("Exception loading Service class " + className + " make sure it exists");
			}

			return service;
		}
	}
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy