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

com.ibm.jbatch.container.services.impl.JSEBatchArtifactFactoryImpl Maven / Gradle / Ivy

There is a newer version: 1.0
Show newest version
/*
 * Copyright 2012 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.services.impl;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamReader;

import com.ibm.jbatch.container.exception.BatchContainerRuntimeException;
import com.ibm.jbatch.container.exception.BatchContainerServiceException;
import com.ibm.jbatch.spi.services.IBatchArtifactFactory;
import com.ibm.jbatch.spi.services.IBatchConfig;

public class JSEBatchArtifactFactoryImpl implements IBatchArtifactFactory, XMLStreamConstants {

	private final static Logger logger = Logger.getLogger(JSEBatchArtifactFactoryImpl.class.getName());
	private final static String CLASSNAME = JSEBatchArtifactFactoryImpl.class.getName();

	// TODO - surface constants
	private final static String BATCH_XML = "META-INF/batch.xml";
	private final static QName BATCH_ROOT_ELEM = new QName("http://xmlns.jcp.org/xml/ns/javaee", "batch-artifacts");

	// TODO - synchronize appropriately once we learn more about usage
	private boolean loaded = false;
	private volatile ArtifactMap artifactMap = null;

	// Uses TCCL
	@Override
	public Object load(String batchId) {
		String methodName = "load";

		if (logger.isLoggable(Level.FINER)) {
			logger.entering(CLASSNAME, methodName, "Loading batch artifact id = " + batchId);
		}

		ClassLoader tccl = Thread.currentThread().getContextClassLoader();

		if (logger.isLoggable(Level.FINE)) {
			logger.fine("TCCL = " + tccl);
		}

		initArtifactMapFromClassLoader(tccl);

		Object loadedArtifact = artifactMap.getArtifactById(batchId);

		if (loadedArtifact == null) {
			throw new IllegalArgumentException("Could not load any artifacts with batch id=" + batchId);
		}

		if (logger.isLoggable(Level.FINER)) {
			logger.exiting(CLASSNAME, methodName, "For batch artifact id = " + batchId + ", loaded artifact instance: " +
					loadedArtifact + " of type: " + loadedArtifact.getClass().getCanonicalName());
		}
		return loadedArtifact;
	}

	private void initArtifactMapFromClassLoader(ClassLoader loader) {
		/*
		 * Following pattern in:
		 *   http://en.wikipedia.org/wiki/Double-checked_locking
		 */
		ArtifactMap tempMap = artifactMap;
		if (tempMap == null) {
			synchronized(this) {
				tempMap = artifactMap;
				if (tempMap == null) {
					tempMap = new ArtifactMap();                    
					InputStream is = getBatchXMLStreamFromClassLoader(loader);
					artifactMap = populateArtifactMapFromStream(tempMap, is);					
				}
			}
		}
	}

	protected InputStream getBatchXMLStreamFromClassLoader(ClassLoader loader) {
		InputStream is = loader.getResourceAsStream(BATCH_XML);

		if (is == null) {
			throw new IllegalStateException("Unable to load batch.xml");
		}

		return is;
	}

	/*
	 * Non-validating (e.g. that the artifact type is correct) load
	 * 
	 * TODO - add some logging to the parsing
	 */
	protected ArtifactMap populateArtifactMapFromStream(ArtifactMap tempMap, InputStream is) {
		XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();


		try {
			XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(is);

			if (logger.isLoggable(Level.FINEST)) {
				logger.finest("Loaded XMLStreamReader = " + xmlStreamReader);
			}

			boolean processedRoot = false;

			// We are going to take advantage of the simplified structure of a
			// line
			// E.g.:
			// 
			//   
			//   ..
			// 
			//
			// and have much simpler logic than general-purpose parsing would
			// require.
			while (xmlStreamReader.hasNext()) {
				int event = xmlStreamReader.next();

				// Until we reach end of document
				if (event == END_DOCUMENT) {
					break;
				}

				// At this point we have either:
				//    A) just passed START_DOCUMENT, and are at START_ELEMENT for the root, 
				//       , or 
				//    B) we have just passed END_ELEMENT for one of the artifacts which is a child of
				//       .
				//   
				//  Only handle START_ELEMENT now so we can skip whitespace CHARACTERS events.
				//
				if (event == START_ELEMENT) {
					if (!processedRoot) {
						QName rootQName = xmlStreamReader.getName();
						if (!rootQName.equals(BATCH_ROOT_ELEM)) {
							throw new IllegalStateException("Expecting document with root element QName: " + BATCH_ROOT_ELEM
									+ ", but found root element with QName: " + rootQName);
						} else {
							processedRoot = true;
						}
					} else {

						// Should only need localName
						String annotationShortName = xmlStreamReader.getLocalName();
						String id = xmlStreamReader.getAttributeValue(null, "id");
						String className = xmlStreamReader.getAttributeValue(null, "class");
						tempMap.addEntry(annotationShortName, id, className);

						// Ignore anything else (text/whitespace) within this
						// element
						while (event != END_ELEMENT) {
							event = xmlStreamReader.next();
						}
					}
				}
			}
			xmlStreamReader.close();
			is.close();
			return tempMap;

		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	private class ArtifactMap {

		private Map idToArtifactClassMap = new HashMap();

		// Maps to a list of types not a single type since there's no reason a single artifact couldn't be annotated
		// with >1 batch artifact annotation type.
		private Map> idToArtifactTypeListMap = new HashMap>();

		/*
		 * Init already synchronized, so no need to synch further
		 */
		private void addEntry(String batchTypeName, String id, String className) {
			try {
				if (!idToArtifactClassMap.containsKey(id)) {
					Class artifactClass = Thread.currentThread().getContextClassLoader().loadClass(className);

					idToArtifactClassMap.put(id, artifactClass);
					List typeList = new ArrayList();
					typeList.add(batchTypeName);                    
					idToArtifactTypeListMap.put(id, typeList);                    
				} else {

					Class artifactClass = Thread.currentThread().getContextClassLoader().loadClass(className);

					// Already contains entry for this 'id', let's make sure it's the same Class
					// which thus must implement >1 batch artifact "type" (i.e. contains >1 batch artifact annotation).
					if (!idToArtifactClassMap.get(id).equals(artifactClass)) {
						if (logger.isLoggable(Level.SEVERE)) {
							Class alreadyLoaded = idToArtifactClassMap.get(id); 
							logger.severe("Attempted to load batch artifact with id: " + id + ", and className: " + className + 
									".   Found: " + artifactClass + ", however the artifact id: " + id + 
									" is already associated with: " + alreadyLoaded + ", of className: " +
									alreadyLoaded.getCanonicalName());

						}
						throw new IllegalArgumentException("Already loaded a different class for id = " + id);
					}
					List typeList = idToArtifactTypeListMap.get(id);
					typeList.add(batchTypeName);
				}
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}

		private Object getArtifactById(String id) {

			Object artifactInstance = null;

			try {
				Class clazz = idToArtifactClassMap.get(id);
				if (clazz != null) {
					artifactInstance = (idToArtifactClassMap.get(id)).newInstance();	
				}
			} catch (IllegalAccessException e) {
				throw new BatchContainerRuntimeException("Tried but failed to load artifact with id: " + id, e);
			} catch (InstantiationException e) {
				throw new BatchContainerRuntimeException("Tried but failed to load artifact with id: " + id, e);
			}


			return artifactInstance;
		}

		private List getBatchTypeList(String id) {
			return idToArtifactTypeListMap.get(id);
		}

	}

	@Override
	public void init(IBatchConfig batchConfig) throws BatchContainerServiceException {
		// TODO Auto-generated method stub

	}

	@Override
	public void shutdown() throws BatchContainerServiceException {
		// TODO Auto-generated method stub

	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy