package org.juxtapose.streamline.producer; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import org.juxtapose.streamline.producer.executor.IExecutor; import org.juxtapose.streamline.stm.ISTM; import org.juxtapose.streamline.stm.ReferenceLink; import org.juxtapose.streamline.stm.STMTransaction; import org.juxtapose.streamline.stm.TemporaryController; import org.juxtapose.streamline.util.ISTMEntrySubscriber; import org.juxtapose.streamline.util.ISTMEntry; import org.juxtapose.streamline.util.Status; import org.juxtapose.streamline.util.data.DataType; import org.juxtapose.streamline.util.data.DataTypeRef; /** * @author Pontus J�rgne * Jan 8, 2012 * Copyright (c) Pontus J�rgne. All rights reserved */ public abstract class STMEntryProducer extends TemporaryController implements ISTMEntryProducer, ISTMEntrySubscriber { private final HashMap<String, TemporaryController> dependencies = new HashMap<String, TemporaryController>(); private final HashMap<String, ReferenceLink> keyToReferensLinks = new HashMap<String, ReferenceLink>(); protected final ISTMEntryKey entryKey; protected final ISTM stm; /** * @param inKey * @param inSTM */ public STMEntryProducer( ISTMEntryKey inKey, ISTM inSTM ) { super( IExecutor.LOW ); entryKey = inKey; stm = inSTM; } /** * @param inKey * @param inSTM * @param inPriority */ public STMEntryProducer( ISTMEntryKey inKey, ISTM inSTM, int inPriority ) { super( inPriority ); entryKey = inKey; stm = inSTM; } /** * @param inKey * Needs external Synchronization on dataKey */ protected Map<String, ReferenceLink> getReferensList( String inKey ) { return keyToReferensLinks; } /** * @param inKey * Needs external Synchronization on dataKey */ private void disposeAllReferenceLinks( ) { for( String key : keyToReferensLinks.keySet() ) { ReferenceLink link = keyToReferensLinks.get( key ); link.dispose(); } keyToReferensLinks.clear(); } private void disposeAllDependencies() { for( TemporaryController controller : dependencies.values() ) { controller.dispose(); } dependencies.clear(); } /** * @param inKey * Needs external Synchronization on dataKey */ public ReferenceLink removeReferenceLink( String inField ) { return keyToReferensLinks.remove( inField ); } /* (non-Javadoc) * @see org.juxtapose.streamline.producer.IDataProducer#removeDependency(java.lang.String) */ public TemporaryController removeDependency( String inDataKey ) { TemporaryController controller = dependencies.remove( inDataKey ); if( controller == null ) stm.logError( "Tried to remove not existing dependency" ); return controller; } /* (non-Javadoc) * @see org.juxtapose.streamline.producer.IDataProducer#addDataReferences(java.util.Map) * initDataReference is always done within STM sync and IDataKey lock. */ public void addDataReferences( String inFieldKey, ReferenceLink inLink ) { assert keyToReferensLinks.get( inFieldKey ) == null : "Reference already exists"; keyToReferensLinks.put( inFieldKey, inLink ); } public void addDependency( String inKey, TemporaryController inController ) { if( dependencies.containsKey( inKey )) { stm.logError( "Dependency for "+inKey+" is already added to "+entryKey ); return; } dependencies.put( inKey, inController ); } /* (non-Javadoc) * @see org.juxtapose.streamline.stm.TemporaryController#stop() * Stop is only called via TemporaryController.dispose from STM */ protected void stop() { disposeAllDependencies(); disposeAllReferenceLinks(); } /* (non-Javadoc) * @see org.juxtapose.streamline.producer.IDataProducer#referencedDataUpdated(java.lang.Integer, org.juxtapose.streamline.util.IPublishedData) * TODO Can this method be package private to ensure always called from ReferenceLink */ public void referencedDataUpdated( final String inFieldKey, final ReferenceLink inLink, final ISTMEntry inData ) { stm.commit( new STMTransaction( entryKey, this, 0, 0, false ) { @Override public void execute() { DataType<?> dataAtKey = get( inFieldKey ); if( dataAtKey == null || !(dataAtKey instanceof DataTypeRef) ) { //Reference has been removed in publishedDataObject return; } ISTMEntryKey key = (ISTMEntryKey)dataAtKey.get(); if( !key.equals( inLink.getRef().get() ) ) { //Reference has been replaced by another reference return; } DataTypeRef newRef = new DataTypeRef( inLink.getRef().get(), inData ); updateReferenceValue(inFieldKey, newRef); referenceDataCall( inFieldKey, inLink, inData, this ); } }); postReferenceDataCall( inFieldKey, inLink, inData ); } /** * @param inFieldKey * @param inLink * @param inData * @param inTransaction * To be overridden by subclasses that to continue the work on a transaction after the referenced Data has been updated */ protected void referenceDataCall( final String inFieldKey, final ReferenceLink inLink, final ISTMEntry inData, STMTransaction inTransaction ) { } /** * @param inFieldKey * @param inLink * @param inData * To be overridden by subclass that needs to take action after referenced Data has been updated and transaction completed */ protected void postReferenceDataCall( final String inFieldKey, final ReferenceLink inLink, final ISTMEntry inData ) { } protected void setStatus( final Status inStatus ) { stm.commit( new STMTransaction( entryKey, STMEntryProducer.this, 0, 0, false ) { @Override public void execute() { setStatus( inStatus ); } }); } /* (non-Javadoc) * @see org.juxtapose.streamline.util.IDataSubscriber#updateData(org.juxtapose.streamline.producer.IDataKey, org.juxtapose.streamline.util.IPublishedData, boolean) */ public void updateData( ISTMEntryKey inKey, final ISTMEntry inData, boolean inFirstUpdate ) { } /* (non-Javadoc) * @see org.juxtapose.streamline.stm.TemporaryController#priorityUpdated(int) */ public void priorityUpdated( int inPriority ) { } public HashSet<TemporaryController> getDependencyControllers() { HashSet<TemporaryController> hs = new HashSet<TemporaryController>(); for( TemporaryController tc : dependencies.values() ) { hs.add( tc ); } for( ReferenceLink rl : keyToReferensLinks.values() ) { hs.add( rl ); } return hs; } }