/* * 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.models; import de.fub.agg2graph.agg.AggContainer; import de.fub.agg2graph.agg.tiling.ICachingStrategy; import de.fub.maps.project.aggregator.filetype.AggregatorDataObject; import de.fub.maps.project.aggregator.pipeline.AbstractAggregationProcess; import de.fub.maps.project.aggregator.pipeline.AggregatorProcessPipeline; import de.fub.maps.project.aggregator.pipeline.wrapper.interfaces.AggregationStrategy; import de.fub.maps.project.aggregator.pipeline.wrapper.interfaces.CachingStrategy; import de.fub.maps.project.aggregator.pipeline.wrapper.interfaces.DescriptorFactory; import de.fub.maps.project.aggregator.xml.AggregatorDescriptor; import de.fub.maps.project.aggregator.xml.ProcessDescriptor; import de.fub.maps.project.aggregator.xml.Source; import de.fub.maps.project.api.process.Process; import de.fub.maps.project.api.process.ProcessPipeline.PipelineListener; import de.fub.maps.project.api.statistics.StatisticProvider; import de.fub.utilsmodule.synchronizer.ModelSynchronizer; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.File; import java.io.IOException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.xml.bind.JAXBException; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.util.Exceptions; import org.openide.util.NbBundle; /** * A high level implementation of an gps navigation graph renderer. * * @author Serdar */ public class Aggregator extends ModelSynchronizer { private static final Logger LOG = Logger.getLogger(Aggregator.class.getName()); public static final String PROP_NAME_AGGREGATOR_STATE = "aggregator.state"; private final AggregatorDataObject dataObject; private AggregatorState aggregatorState = AggregatorState.INACTIVE; private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); private final AggregatorProcessPipeline pipeline; private AggContainer aggContainer; private ModelSynchronizerClient dataObjectModelSynchonizerClient; private final Object MUTEX_PROCESS_CREATOR = new Object(); private PipelineListener pipelineListener = null; public Aggregator(AggregatorDataObject dataObject) { assert dataObject != null; this.dataObject = dataObject; pipeline = new AggregatorProcessPipeline(this); init(); } /** * Initialized this Aggregator instance. */ private void init() { dataObjectModelSynchonizerClient = create(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { // do nothing } }); this.dataObject.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { initAggregator(); dataObjectModelSynchonizerClient.modelChangedFromSource(); } }); AggregatorDescriptor aggregatorDescriptor = getAggregatorDescriptor(); if (aggregatorDescriptor != null) { initAggregator(); if (pipelineListener == null) { pipelineListener = new PipelineListenerImpl(); getPipeline().addPipelineListener(pipelineListener); } } else { setAggregatorState(AggregatorState.ERROR_NOT_EXECUTABLE); } } private void initAggregator() { setAggregatorState(AggregatorState.INACTIVE); initAggregatorContainer(); initPipeline(); } private void initAggregatorContainer() { AggregatorDescriptor descriptor = getAggregatorDescriptor(); File sourceFolder = null; if (descriptor.getCacheFolderPath() == null) { sourceFolder = createDefaultCacheFolder(); } else { sourceFolder = new File(descriptor.getCacheFolderPath()); if (!sourceFolder.exists()) { if (!sourceFolder.mkdir()) { // failed to create the cache folder. Fall back // to create the cache folder in the same folder where // the .agg file lies. LOG.log(Level.WARNING, MessageFormat.format("sourceFolder: {0}! Creating default sourceFolder in the same folder where the agg file lies !", sourceFolder.getAbsolutePath())); //NO18N sourceFolder = createDefaultCacheFolder(); } } } AggregationStrategy aggregateStrategy = null; try { aggregateStrategy = AggregationStrategy.Factory.find(descriptor.getAggregationStrategy(), Aggregator.this); } catch (DescriptorFactory.InstanceNotFountException ex) { try { aggregateStrategy = AggregationStrategy.Factory.getDefault(); } catch (DescriptorFactory.InstanceNotFountException ex1) { Exceptions.printStackTrace(ex1); } } ICachingStrategy cachingStrategy = null; try { cachingStrategy = CachingStrategy.Factory.find(descriptor.getTileCachingStrategy()); } catch (DescriptorFactory.InstanceNotFountException ex) { try { cachingStrategy = CachingStrategy.Factory.getDefault(); } catch (DescriptorFactory.InstanceNotFountException ex1) { Exceptions.printStackTrace(ex1); } } if (aggContainer != null) { aggContainer.setAggregationStrategy(aggregateStrategy); aggContainer.setCachingStrategy(cachingStrategy); aggContainer.setDataSource(sourceFolder); } else { aggContainer = AggContainer.createContainer(sourceFolder, aggregateStrategy, cachingStrategy); } } private void initPipeline() { AggregatorDescriptor descriptor = getAggregatorDescriptor(); pipeline.clear(); try { AbstractAggregationProcess process = null; for (ProcessDescriptor processDescriptor : descriptor.getPipeline().getList()) { process = createProcess(processDescriptor); if (process != null) { pipeline.add(process); } else { break; } } } catch (AggregatorProcessPipeline.PipelineException ex) { setAggregatorState(AggregatorState.ERROR_NOT_EXECUTABLE); Exceptions.printStackTrace(ex); } } private File createDefaultCacheFolder() { AggregatorDescriptor descriptor = getAggregatorDescriptor(); File sourceFolder = null; FileObject parent = this.dataObject.getPrimaryFile().getParent(); if (parent != null) { try { if (parent.getFileObject(descriptor.getName()) != null) { sourceFolder = FileUtil.toFile(parent.getFileObject(descriptor.getName())); } else { FileObject cacheFolder = parent.createFolder(descriptor.getName()); descriptor.setCacheFolderPath(cacheFolder.getPath()); sourceFolder = FileUtil.toFile(cacheFolder); } descriptor.setCacheFolderPath(sourceFolder.getAbsolutePath()); } catch (IOException ex) { Exceptions.printStackTrace(ex); LOG.info(ex.getMessage()); } } dataObject.save(); return sourceFolder; } /** * Starts and processes the specified process units in the list. The process * units in the list can't be in out of order * * @param processes */ @NbBundle.Messages({ "# {0} - processName", "# {1} - aggregatorName", "CLT_Proceeding_Process={1}: Running {0}..."}) @SuppressWarnings("unchecked") public void start(final List<AbstractAggregationProcess<?, ?>> processes) { getPipeline().start(processes); } /** * Convenience method to run all process units that are in the pipeline from * the first process unit to the last one. */ public void start() { start(new ArrayList<AbstractAggregationProcess<?, ?>>(getPipeline().getProcesses())); } /** * Returns the underlying Aggregator container. * * @return Always an AggContainer instance. */ public AggContainer getAggContainer() { return aggContainer; } /** * Returns the process pipeline of this Aggregator object. * * @return Always an AggregatorProcessPipeline instance. */ public AggregatorProcessPipeline getPipeline() { return pipeline; } /** * A convenience method to delegate the call to the respective method of the * Aggregator descriptor. * * @return in all cases a List instance. */ public List<Source> getSourceList() { return getAggregatorDescriptor().getDatasources(); } /** * Returns the current aggregator state. * * @return Always an AggregatorState instance. */ public AggregatorState getAggregatorState() { return aggregatorState; } /** * Sets the current aggregator state and fires a property change event. * * @param aggregatorState an AggregatorState, null not permitted. */ public synchronized void setAggregatorState(AggregatorState aggregatorState) { assert aggregatorState != null : "null permitted as aggregator state."; Object oldValue = this.aggregatorState; this.aggregatorState = aggregatorState; pcs.firePropertyChange(PROP_NAME_AGGREGATOR_STATE, oldValue, this.aggregatorState); } /** * Returns the underlying DataObject. * * @return Always an AggregatorDataObject instance. */ public AggregatorDataObject getDataObject() { return dataObject; } /** * Convenience method to get the Aggregator descriptor. This method * delegates the call to the underlying AggregatorDataObject. * * @return A valid AggregatorDescriptor instance if possible, otherwise * null. */ public AggregatorDescriptor getAggregatorDescriptor() { try { return dataObject.getAggregatorDescriptor(); } catch (IOException ex) { Exceptions.printStackTrace(ex); } catch (JAXBException ex) { Exceptions.printStackTrace(ex); } return null; } public void addPropertyChangeListener(PropertyChangeListener listener) { pcs.addPropertyChangeListener(listener); } public void removePropertyChangeListener(PropertyChangeListener listener) { pcs.removePropertyChangeListener(listener); } public List<StatisticProvider> getStatistics() { List<StatisticProvider> statisticProviders = new ArrayList<StatisticProvider>(); for (AbstractAggregationProcess<?, ?> process : getPipeline().getProcesses()) { if (process instanceof StatisticProvider) { statisticProviders.add(((StatisticProvider) process)); } } return statisticProviders; } @Override public void updateSource() { if (dataObject != null) { dataObject.modifySourceEditor(); } } public AbstractAggregationProcess createProcess(ProcessDescriptor processDescriptor) { synchronized (MUTEX_PROCESS_CREATOR) { assert processDescriptor != null; AbstractAggregationProcess<?, ?> aggregateProcess = null; try { aggregateProcess = AbstractAggregationProcess.find(processDescriptor.getJavaType(), Aggregator.this); aggregateProcess.setProcessDescriptor(processDescriptor); } catch (AbstractAggregationProcess.AbstractAggregationProcessNotFoundException ex) { Exceptions.printStackTrace(ex); } return aggregateProcess; } } public enum AggregatorState { ERROR("Error"), RUNNING("Running"), INACTIVE("Inactive"), ERROR_NOT_EXECUTABLE("Errot not executable"), WARNING("Warning"); private String displayName; private AggregatorState(String displayName) { this.displayName = displayName; } public String getDisplayName() { return displayName; } } private class PipelineListenerImpl implements PipelineListener { public PipelineListenerImpl() { } @Override public void willStart(List<Process> propcesses) { setAggregatorState(Aggregator.AggregatorState.INACTIVE); } @Override public void started() { setAggregatorState(Aggregator.AggregatorState.RUNNING); } @Override public void stoped(PipelineListener.Result result) { switch (result) { case CANCELED: setAggregatorState(Aggregator.AggregatorState.INACTIVE); break; case FINISHED: setAggregatorState(Aggregator.AggregatorState.INACTIVE); break; case ERROR: setAggregatorState(Aggregator.AggregatorState.ERROR); break; default: break; } } @Override public void pipelineChanged() { // something to do? } } }