/* * Copyright 2013 Serdar. * * 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 de.fub.maps.project.aggregator.pipeline; import de.fub.maps.project.api.process.ProcessPipeline; import de.fub.maps.project.models.Aggregator; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandleFactory; import org.openide.util.Cancellable; import org.openide.util.Exceptions; import org.openide.util.NbBundle; /** * This class represents the pipeline of an aggregator. It contains the process * units of the parent Aggregator and handles the chained execution of the * contained process units. * * @author Serdar */ public class AggregatorProcessPipeline extends ProcessPipeline<AbstractAggregationProcess<?, ?>> implements ProcessPipeline.ProcessListener { private static final Logger LOG = Logger.getLogger(AggregatorProcessPipeline.class.getName()); private final Aggregator aggregator; private final AtomicBoolean canceled = new AtomicBoolean(false); private final Object MUTEX_PROCESS_RUNNING = new Object(); private AbstractAggregationProcess process; private int unit; private List<AbstractAggregationProcess<?, ?>> workingProcesses = new ArrayList<AbstractAggregationProcess<?, ?>>(); private ProgressHandle handler; public AggregatorProcessPipeline(Aggregator aggregator1) { super(); assert aggregator1 != null; this.aggregator = aggregator1; } public Aggregator getAggregator() { return aggregator; } /** * Starts the chained execution of the specified process units. * * @param processes A list of process units, which will be exceuted. */ @NbBundle.Messages({ "# {0} - processName", "# {1} - aggregatorName", "CLT_Proceeding_Process={1}: Running {0}..."}) @SuppressWarnings({"unchecked"}) public void start(final List<AbstractAggregationProcess<?, ?>> processes) { synchronized (MUTEX_PROCESS_RUNNING) { canceled.set(false); workingProcesses.clear(); workingProcesses.addAll(processes); handler = ProgressHandleFactory.createHandle(Bundle.CLT_Proceeding_Process("", aggregator.getAggregatorDescriptor().getName()), new CancellableImpl()); try { firePipelineEvent(PipelineEvents.WILL_START); handler.start(100); firePipelineEvent(PipelineEvents.STARTED); // int i = 0; AbstractAggregationProcess lastProcess = null; long lastTime = System.currentTimeMillis(); for (int j = 0; j < workingProcesses.size(); j++) { unit = j; process = workingProcesses.get(j); if (canceled.get()) { firePipelineEvent(PipelineEvents.CANCELED); break; } handler.setDisplayName(Bundle.CLT_Proceeding_Process(process.getName(), getAggregator().getAggregatorDescriptor().getName())); if (j == 0) { int indexOf = indexOf(process); if (indexOf > 0) { lastProcess = get(indexOf - 1); } } if (lastProcess != null) { Object result = lastProcess.getResult(); process.setInput(result); } process.addProcessListener(AggregatorProcessPipeline.this); lastTime = System.currentTimeMillis(); process.run(); long now = System.currentTimeMillis(); process.removeProcessListener(AggregatorProcessPipeline.this); LOG.log(Level.FINE, "Process {0} took {1} time.", new Object[]{process.getName(), (now - lastTime)}); lastProcess = process; lastTime = now; } firePipelineEvent(PipelineEvents.FINISHED); } catch (Throwable ex) { Exceptions.printStackTrace(ex); firePipelineEvent(PipelineEvents.ERROR); } finally { handler.finish(); } } } @Override public void changed(ProcessPipeline.ProcessEvent event) { if (workingProcesses != null && handler != null) { double progress = (100d / workingProcesses.size()); progress = (progress / 100 * event.getPercentfinished()) + (100 / workingProcesses.size() * unit); LOG.log(Level.FINE, "progress {0}", progress); handler.progress(event.getProcessMessage(), (int) progress); } } /** * Adds the specified process unit to this pipeline * * @param proc * @return * @throws * de.fub.maps.project.aggregator.pipeline.AggregatorProcessPipeline.PipelineException */ @Override public boolean add(AbstractAggregationProcess<?, ?> proc) throws PipelineException { checkIsValidProcess(size() - 1, proc); return super.add(proc); } /** * Adds the provided list of process units to this pipeline. * * @param index * @param proc */ @Override public void add(int index, AbstractAggregationProcess<?, ?> proc) { checkIsValidProcess(index, proc); super.add(index, proc); } @Override public boolean addAll(Collection<? extends AbstractAggregationProcess<?, ?>> collection) { boolean result = true; for (AbstractAggregationProcess<?, ?> proc : collection) { add(proc); } return result; } @Override public boolean addAll(int index, Collection<? extends AbstractAggregationProcess<?, ?>> collection) { boolean result = true; for (AbstractAggregationProcess<?, ?> proc : collection) { add(index, proc); index++; } return result; } /** * Checks whether the provided process unit violates the pipeline invariant. * * @param prevProcessIndex * @param process * @throws * de.fub.maps.project.aggregator.pipeline.AggregatorProcessPipeline.PipelineException */ public void checkIsValidProcess(int prevProcessIndex, AbstractAggregationProcess<?, ?> process) throws PipelineException { if (prevProcessIndex > -1) { ArrayList<AbstractAggregationProcess<?, ?>> arrayList = new ArrayList<AbstractAggregationProcess<?, ?>>(getProcesses()); AbstractAggregationProcess<?, ?> previousProcess = arrayList.get(prevProcessIndex); Class<? extends AbstractAggregationProcess> previousProcessClass = previousProcess.getClass(); Class<? extends AbstractAggregationProcess> currentProcessClass = process.getClass(); compareGenericTypes(currentProcessClass, previousProcessClass); } } @SuppressWarnings("unchecked") private void compareGenericTypes(Type currentProcessClass, Type previousProcessClass) throws PipelineException { Type previousProcessSuperclass = previousProcessClass instanceof Class ? ((Class) previousProcessClass).getGenericSuperclass() : previousProcessClass.getClass().getGenericSuperclass(); Type currentProcessSuperclass = currentProcessClass instanceof Class ? ((Class) currentProcessClass).getGenericSuperclass() : currentProcessClass.getClass().getGenericSuperclass(); // in case both types are of type class then check whether they are // LOG.fine(MessageFormat.format("prev : {0} , current: {1}", previousProcessSuperclass, currentProcessSuperclass)); if (previousProcessSuperclass instanceof Class && currentProcessSuperclass instanceof Class && previousProcessSuperclass.equals(currentProcessSuperclass)) { } else if (previousProcessSuperclass instanceof ParameterizedType && currentProcessSuperclass instanceof ParameterizedType) { Type[] outputTypeArray = ((ParameterizedType) previousProcessSuperclass).getActualTypeArguments(); Type[] inputTypeArray = ((ParameterizedType) currentProcessSuperclass).getActualTypeArguments(); // if both array have the length 2 // check there types if (outputTypeArray.length == inputTypeArray.length && outputTypeArray.length == 2) { Type outputType = outputTypeArray[1]; Type inputType = inputTypeArray[0]; if (outputType.getClass().equals(inputType.getClass())) { compareGenericTypes(inputType, outputType); } else { throw new PipelineException(MessageFormat.format("input type {0} and output type {1} don't match!", inputType, outputType)); } } else if (outputTypeArray.length == inputTypeArray.length && outputTypeArray.length == 0) { // recursive anchor. Both classes have no type parameters } else { // both type array lengths are not 2. Error state throw new PipelineException(MessageFormat.format("output generic interfaces type {0} and input generice interfaces type {1} length don't macth !", outputTypeArray, inputTypeArray)); } } else { throw new PipelineException(MessageFormat.format("Current case not supported type1 :{0} type2 : {1}", previousProcessSuperclass, currentProcessSuperclass)); } } @Override public void started() { // do nothing } @Override public void canceled() { canceled.set(true); } @Override public void finished() { // do nothing } private class CancellableImpl implements Cancellable { public CancellableImpl() { } @Override public boolean cancel() { boolean result = false; if (process != null) { result = process.cancel(); } canceled.set(true); result = canceled.get(); return result; } } public static class PipelineException extends IllegalArgumentException { private static final long serialVersionUID = 1L; public PipelineException() { } public PipelineException(String s) { super(s); } public PipelineException(String message, Throwable cause) { super(message, cause); } public PipelineException(Throwable cause) { super(cause); } } }