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

com.ibm.batch.container.tck.bridge.BatchContainerJSLMerger Maven / Gradle / Ivy

The 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.batch.container.tck.bridge;

import jsr352.batch.jsl.Chunk;
import jsr352.batch.jsl.JSLJob;
import jsr352.batch.jsl.JSLProperties;
import jsr352.batch.jsl.Listener;
import jsr352.batch.jsl.Listeners;
import jsr352.batch.jsl.ObjectFactory;
import jsr352.batch.jsl.Property;
import jsr352.batch.jsl.Step;

import com.ibm.batch.container.xjcl.ExecutionElement;
import com.ibm.batch.container.xjcl.ModelResolver;
import com.ibm.batch.container.xjcl.ModelResolverFactory;
import com.ibm.batch.container.xjcl.ModelSerializer;
import com.ibm.batch.container.xjcl.ModelSerializerFactory;
import com.ibm.batch.tck.spi.JSLInheritanceMerger;

/**
 * Per Chris V.'s most recent input:
 * 
 *   1. Job inherits from Job
 *   a. only properties and listeners are inherited
 *     i. listener order is not guaranteed
 *     ii. property order doesn't matter
 *   2. Splits and flows are not inherited from a Job, and cannot inherit from other Splits and Flows
 *   3. Steps can inherit from Steps
 *   4. When inheriting,
 *     a. non-intersecting elements are merged
 *     b. for intersecting elements, non-intersecting attributes are merged
 *     c. for intersecting elements, for intersecting attributes, child overrides parent
 *   5. Lists ( and ) support merge=true|false attribute
 *    
 * @author ctedlock
 *
 */
public class BatchContainerJSLMerger implements JSLInheritanceMerger {

	@Override
	public String mergeStep(String childJobXml, String parentStepXml, String childStepID) {
		JSLJob childJob = jobResolveHelper(childJobXml);
		Step parentStep = stepResolveHelper(parentStepXml);
		
		for(ExecutionElement elem : childJob.getExecutionElements()) {
			if(elem instanceof Step && parentStep.getId().equals(((Step)elem).getParent())) {
				childJob.getExecutionElements().remove(elem);
				childJob.getExecutionElements().add(mergeStep(parentStep, (Step)elem));
			}
		}
		
		ModelSerializer serializer = ModelSerializerFactory.createJobModelSerializer();
		return serializer.serializeModel(childJob);		
	}
	
	@Override
	public String mergeJob(String parentJobXML, String childJobXML) {
		JSLJob parentJob = jobResolveHelper(parentJobXML);
		JSLJob childJob = jobResolveHelper(childJobXML);

		//job merges from job
		if(parentJob != null && childJob != null) {
			JSLJob mergedJob = mergeJob(parentJob, childJob);
			ModelSerializer serializer = ModelSerializerFactory.createJobModelSerializer();
			return serializer.serializeModel(mergedJob);
		}
		
		return childJobXML;
	}

	// Helper method to simplify path logic in main merge method
	public JSLJob jobResolveHelper(String jobXML) {
		ModelResolver jobResolver = ModelResolverFactory.createJobResolver();
		JSLJob job = null;
		try {
			job = jobResolver.resolveModel(jobXML);
		} catch(ClassCastException cce) {
			//NOP, this is not a JSLJob, return null
		}
		return job;
	}

	// Helper method to simplify path logic in main merge method
	public Step stepResolveHelper(String stepXML) {
		ModelResolver stepResolver = ModelResolverFactory.createStepResolver();
		Step step = null;
		try {
			step = stepResolver.resolveModel(stepXML);
		} catch(ClassCastException cce) {
			//NOP, this is not a Step, return null
		}
		return step;
	}


	/**
	 *  
	 * @param parent the parent Step
	 * @param child the child Step
	 * @return the resulting merged Step
	 */
	public Step mergeStep(Step parent, Step child) {
		//if child doesn't link to parent, result is just the original child doc
		if(!parent.getId().equals(child.getParent())) {
			return child;			
		}

		//initialize new model object
		ObjectFactory factory = new ObjectFactory();
		Step merge = factory.createStep();
		merge.setId(child.getId());

		//merge lists
		merge.setProperties(mergeProperties(parent.getProperties(), child.getProperties()));
		merge.setListeners(mergeListeners(parent.getListeners(), child.getListeners()));

		merge.getControlElements().addAll(parent.getControlElements());
		
		merge.setChunk(mergeChunk(parent.getChunk(), child.getChunk()));
				
		//merge batchlet properties
		if(child.getBatchlet() != null && parent.getBatchlet() != null) {
			if("true".equals(child.getBatchlet().getProperties().getMerge())) {
				
			}
			merge.setBatchlet(parent.getBatchlet());
		}
		
		return merge;
	}
	
	public Chunk mergeChunk(Chunk parentChunk, Chunk childChunk) {
		if(parentChunk == null && childChunk != null) return childChunk;
		if(parentChunk != null && childChunk == null) return parentChunk;
		if(parentChunk == null && childChunk == null) return null;
		
		Chunk merge = (new ObjectFactory()).createChunk();
		
		//merge chunks - 8 attributes
		//FIXME: getCheckpointPolicy will never return null (defaults to "item"), can't check this
//		merge.setCheckpointPolicy( (childChunk.getCheckpointPolicy() == null
//				? parentChunk.getCheckpointPolicy()
//				: childChunk.getCheckpointPolicy()));
		//FIXME: getItemCount will never return null (defaults to 10), can't check this
//		merge.setCommitInterval( (childChunk.getItemCount() == null
//				? parentChunk.getItemCount()
//				: childChunk.getItemCount()));
		merge.setProcessor( (childChunk.getProcessor() == null
				? parentChunk.getProcessor()
				: childChunk.getProcessor()));
		merge.setReader( (childChunk.getReader() == null
				? parentChunk.getReader()
				: childChunk.getReader()));
		merge.setRetryLimit( (childChunk.getRetryLimit() == null
				? parentChunk.getRetryLimit()
				: childChunk.getRetryLimit()));
		merge.setSkipLimit( (childChunk.getRetryLimit() == null
				? parentChunk.getSkipLimit()
				: childChunk.getSkipLimit()));
		merge.setWriter( (childChunk.getWriter() == null
				? parentChunk.getWriter()
				: childChunk.getWriter()));
		
		
		//merge chunks -  5 elements
		merge.setCheckpointAlgorithm( (childChunk.getCheckpointAlgorithm() == null
				? parentChunk.getCheckpointAlgorithm()
				: childChunk.getCheckpointAlgorithm()));
		merge.setSkippableExceptionClasses( (childChunk.getSkippableExceptionClasses() == null
				? parentChunk.getSkippableExceptionClasses()
				: childChunk.getSkippableExceptionClasses()));
		merge.setRetryableExceptionClasses( (childChunk.getRetryableExceptionClasses() == null
				? parentChunk.getRetryableExceptionClasses()
				: childChunk.getRetryableExceptionClasses()));
		merge.setNoRollbackExceptionClasses( (childChunk.getNoRollbackExceptionClasses() == null
				? parentChunk.getNoRollbackExceptionClasses()
				: childChunk.getNoRollbackExceptionClasses()));
		
		return merge;
	}
	
	/**
	 * 
	 * @param parent the parent Job
	 * @param child the child Job
	 * @return the resulting merged job
	 */
	public JSLJob mergeJob(JSLJob parent, JSLJob child) {
		
		//if child doesn't link to parent, result is just the original child doc
		if(!parent.getId().equals(child.getParent())) {
			return child;			
		}
		
		//initialize new model object
		ObjectFactory factory = new ObjectFactory();
		JSLJob merge = factory.createJSLJob();
		merge.setId(child.getId());

		//TODO: no merge attribute, what default behavior to have?
		//merge lists
		merge.setProperties(mergeProperties(parent.getProperties(), child.getProperties()));
		merge.setListeners(mergeListeners(parent.getListeners(), child.getListeners()));

		//add in any elements we don't already have
		//n^2 performance. how big do we expect a JSL to be?
		for(ExecutionElement pElem : parent.getExecutionElements()) {
			boolean hasCollidingId = false;
			ExecutionElement collidingChildElement = null;
			for(ExecutionElement cElem : child.getExecutionElements()) {
				if(pElem.getId().equals(cElem.getId())) { //id is required attribute, not null
					hasCollidingId = true;
					collidingChildElement = cElem;
				}
			}
			if(hasCollidingId) {
				//in case of intersection, child overrides, i.e.; do nothing
			} else {
				child.getExecutionElements().add(pElem);
			}
		}

		return merge;
	}
	
	/**
	 * Merge two properties lists by combining all  elements from parent and child.
	 * If child is null it will be instantiated, possibly empty.
	 * @param parentProps
	 * @param childProps
	 * @return a new merged JSLProperties object containing parent and child properties
	 */
	private JSLProperties mergeProperties(JSLProperties parentProps, JSLProperties childProps) {
		if(parentProps == null) return childProps;
		if(childProps == null) childProps = new JSLProperties();

		//for merge=false, child properties list takes precedence and is unchanged
		if("false".equals(childProps.getMerge())) {
			childProps.setMerge(null);
			return childProps;
		}
		
		JSLProperties mergedProps = new JSLProperties();
		mergedProps.getPropertyList().addAll(childProps.getPropertyList());
		//for each parent property, add it only if it does not exist in the child
		//i.e.; intersecting properties are overridden by the child
		for(Property parentProp : parentProps.getPropertyList()) {
			boolean foundParentProp = false;
			for(Property childProp : childProps.getPropertyList()) {
				if(parentProp.getName() != null && parentProp.getName().equals(childProp.getName()))
					foundParentProp = true;
			}
			if(!foundParentProp)
				mergedProps.getPropertyList().add(parentProp);
		}
		
		return mergedProps;
	}
	
	/**
	 * Merge two listeners lists by combining all  elements from parent and child.
	 * If child is null it will be instantiated, possibly empty
	 * @param parentListeners
	 * @param childListeners is modified
	 * @return a new merged Listeners object containing parent and child listeners
	 */
	private Listeners mergeListeners(Listeners parentListeners, Listeners childListeners) {
		if(parentListeners == null) return childListeners;
		if(childListeners == null) childListeners = new Listeners();
		
		//for merge=false, child properties list takes precedence and is unchanged
		if("false".equals(childListeners.getMerge())) {
			childListeners.setMerge(null);
			return childListeners;
		}

		Listeners mergedListeners = new Listeners();
		mergedListeners.getListenerList().addAll(childListeners.getListenerList());
		//for each parent property, add it only if it does not exist in the child
		//i.e.; intersecting properties are overridden by the child
		for(Listener parentListener : parentListeners.getListenerList()) {
			boolean foundParentListener = false;
			for(Listener childListener : childListeners.getListenerList()) {
				if(parentListener.getRef() != null && parentListener.getRef().equals(childListener.getRef()))
					foundParentListener = true;
			}
			if(!foundParentListener)
				mergedListeners.getListenerList().add(parentListener);
		}
		return mergedListeners;
	}
	

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy