/*
* Copyright (c) 2014 Red Hat, Inc. and/or its affiliates.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Cheng Fang - Initial API and implementation
*/
package org.jberet.job.model;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.batch.operations.JobStartException;
import org.jberet._private.BatchMessages;
import org.jberet.spi.BatchEnvironment;
import org.jberet.spi.JobXmlResolver;
/**
* Abstract base class for various job element merger types, such as {@link JobMerger}, {@link StepMerger}, and
* {@link FlowMerger}.
*
* @param <T> the job element type this merger class handles
*
* @see JobMerger
* @see StepMerger
* @see FlowMerger
*
* @since 1.0.1
*/
abstract class AbstractMerger<T extends AbstractJobElement> {
T parent;
T child;
Job currentJob;
ClassLoader classLoader;
List<Job> loadedJobs;
final JobXmlResolver jobXmlResolver;
/**
* For tracking cyclic inheritance
*/
private List<T> inheritingElements;
/**
* Constructs common parts defined in this class for various concrete subclasses.
*
* @param currentJob the current job to be processed
* @param classLoader the class loader to use for loading jobs and resources
* @param loadedJobs list of already loaded jobs to avoid reloading them while resolving inheritance
* @param jobXmlResolver job xml resolver, typically obtained from {@code org.jberet.spi.BatchEnvironment#getJobXmlResolver()}
*
* @see BatchEnvironment#getJobXmlResolver()
*/
AbstractMerger(final Job currentJob, final ClassLoader classLoader, final List<Job> loadedJobs, final JobXmlResolver jobXmlResolver) {
this.currentJob = currentJob;
this.classLoader = classLoader;
this.loadedJobs = loadedJobs;
this.jobXmlResolver = jobXmlResolver;
}
/**
* Checks if the element is already recorded in {@link #inheritingElements}.
* If yes, a {@code JobStartException} is thrown to indicate cyclic inheritance.
*
* @param element a job element of type {@code Job}, {@code Step}, or {@code Flow}
* @param elementId the id of the element
* @throws JobStartException if a inheritance cycle is detected
*/
void checkInheritingElements(final T element, final String elementId) throws JobStartException {
if (inheritingElements != null && inheritingElements.contains(element)) {
final StringBuilder sb = new StringBuilder();
for (final AbstractJobElement e : inheritingElements) {
sb.append(e.id).append(" -> ");
}
sb.append(elementId);
throw BatchMessages.MESSAGES.cycleInheritance(sb.toString());
}
}
/**
* Records current parent and child elements in {@link #inheritingElements}, and pass them along when creating a new
* merger in order to resolve further inheritance.
*
* @param nextMerger the new merger for resolving the further inheritance
*/
void recordInheritingElements(final AbstractMerger<T> nextMerger) {
if (inheritingElements != null) {
nextMerger.inheritingElements = inheritingElements;
} else {
nextMerger.inheritingElements = new ArrayList<T>();
}
if (!nextMerger.inheritingElements.contains(child)) {
nextMerger.inheritingElements.add(child);
}
nextMerger.inheritingElements.add(parent);
}
/**
* Merges parent properties and child properties.
*
* @param parent parent job element that can contain properties
* @param child child job element that can contain properties
*/
static void mergeProperties(final PropertiesHolder parent, final PropertiesHolder child) {
if (parent.getProperties() != null) {
final Properties childProps = child.getProperties();
final Properties parentProps = parent.getProperties();
if (childProps == null) {
child.setProperties(parentProps.clone());
} else if (childProps.isMerge()) {
for (final Map.Entry<String, String> e : parentProps.getPropertiesMapping().entrySet()) {
childProps.addIfAbsent(e.getKey(), e.getValue());
}
}
}
}
/**
* Merges listeners in parent with listeners in child element.
*
* @param parent parent job element that can contain listeners
* @param child child job element that can contain listeners
*/
static void mergeListeners(final InheritableJobElement parent, final InheritableJobElement child) {
if (parent.getListeners() != null && !parent.getListeners().getListeners().isEmpty()) {
final Listeners childListeners = child.getListeners();
final List<RefArtifact> parentListenerList = parent.getListeners().getListeners();
if (childListeners == null) {
final Listeners newListeners = new Listeners();
newListeners.getListeners().addAll(parentListenerList);
child.setListeners(newListeners);
} else if (childListeners.isMerge()) {
final List<RefArtifact> childListenerList = childListeners.getListeners();
for (final RefArtifact l : parentListenerList) {
final String pr = l.getRef();
boolean existsInChild = false;
for (final RefArtifact cr : childListenerList) {
if (cr.getRef().equals(pr)) {
existsInChild = true;
break;
}
}
if (!existsInChild) {
childListenerList.add(l);
//if the child already has the same-named listener, ignore the parent one,
//will not attempt to merge Properties under this listener
}
}
}
}
}
}