View Javadoc

1   package cz.cuni.amis.pogamut.multi.communication.worldview.impl;
2   
3   import java.util.LinkedList;
4   import java.util.Queue;
5   import java.util.logging.Level;
6   
7   import cz.cuni.amis.pogamut.base.communication.translator.event.IWorldChangeEvent;
8   import cz.cuni.amis.pogamut.base.communication.translator.event.IWorldEventWrapper;
9   import cz.cuni.amis.pogamut.base.communication.translator.event.IWorldObjectUpdateResult;
10  import cz.cuni.amis.pogamut.base.communication.translator.event.IWorldObjectUpdatedEvent;
11  import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEvent;
12  import cz.cuni.amis.pogamut.base.communication.worldview.impl.EventDrivenWorldView;
13  import cz.cuni.amis.pogamut.base.communication.worldview.object.event.WorldObjectDestroyedEvent;
14  import cz.cuni.amis.pogamut.base.communication.worldview.object.event.WorldObjectFirstEncounteredEvent;
15  import cz.cuni.amis.pogamut.base.communication.worldview.object.event.WorldObjectUpdatedEvent;
16  import cz.cuni.amis.pogamut.base.component.bus.exception.ComponentNotRunningException;
17  import cz.cuni.amis.pogamut.base.component.bus.exception.ComponentPausedException;
18  import cz.cuni.amis.pogamut.base.component.controller.ComponentController;
19  import cz.cuni.amis.pogamut.base.component.controller.ComponentDependencies;
20  import cz.cuni.amis.pogamut.base.component.lifecyclebus.ILifecycleBus;
21  import cz.cuni.amis.pogamut.base.utils.logging.IAgentLogger;
22  import cz.cuni.amis.pogamut.multi.agent.ITeamedAgentId;
23  import cz.cuni.amis.pogamut.multi.communication.translator.event.ICompositeWorldObjectUpdatedEvent;
24  import cz.cuni.amis.pogamut.multi.communication.translator.event.ILocalWorldObjectUpdatedEvent;
25  import cz.cuni.amis.pogamut.multi.communication.translator.event.ISharedWorldObjectUpdatedEvent;
26  import cz.cuni.amis.pogamut.multi.communication.translator.event.IStaticWorldObjectUpdatedEvent;
27  import cz.cuni.amis.pogamut.multi.communication.worldview.ISharedWorldView;
28  import cz.cuni.amis.pogamut.multi.communication.worldview.object.ICompositeWorldObject;
29  import cz.cuni.amis.pogamut.multi.communication.worldview.object.ILocalWorldObject;
30  import cz.cuni.amis.utils.NullCheck;
31  import cz.cuni.amis.utils.exception.PogamutException;
32  
33  public abstract class EventDrivenLocalWorldView extends AbstractLocalWorldView {
34  
35  	public EventDrivenLocalWorldView(ComponentDependencies dependencies, ILifecycleBus bus, IAgentLogger logger, ISharedWorldView sharedWV, ITeamedAgentId agentId)
36  	{
37  		super(dependencies, bus, logger, sharedWV, agentId);
38  	}
39  
40  	public static final String WORLDVIEW_DEPENDENCY = "EventDrivenWorldViewDependency";
41  
42      /**
43       * Flag that is telling us whether there is an event being processed or not.
44       * <p><p>
45       * It is managed only by notify() method - DO NOT MODIFY OUTSIDE IT!
46       */
47      protected boolean receiveEventProcessing = false;
48      
49      /**
50       * List of events we have to process.
51       * <p><p>
52       * It is managed only by notify() method - DO NOT MODIFY OUTSIDE IT!
53       */
54      protected LinkedList<IWorldChangeEvent> notifyEventsList =
55      	new LinkedList<IWorldChangeEvent>();
56      
57      
58      @Override
59      public synchronized void notify(IWorldChangeEvent event) throws ComponentNotRunningException, ComponentPausedException {
60      	
61      	if (log.isLoggable(Level.FINE)){log.fine("LocalWorldView notify( " + event.toString() + ")");};
62      	
63      	if (isPaused()) {
64      		throw new ComponentPausedException(controller.getState().getFlag(), this);
65      	}
66      	if (!isRunning()) {
67      		throw new ComponentNotRunningException(controller.getState().getFlag(), this);
68      	}
69      	
70      	if (!( event instanceof ILocalWorldObjectUpdatedEvent))
71          {
72      		if ( event instanceof ICompositeWorldObjectUpdatedEvent)
73      		{
74      			IWorldChangeEvent partEvent = ((ICompositeWorldObjectUpdatedEvent)event).getSharedEvent();
75      			if (partEvent != null) //shared part
76      			{
77      				log.finest("Notyfying sharedWV " + event.toString() + ")");
78      				sharedWorldView.notify(partEvent);	
79      			}
80      			partEvent = ((ICompositeWorldObjectUpdatedEvent)event).getStaticEvent();
81  				if ( partEvent != null) //static part
82  				{
83  					log.finest("Notyfying sharedWV " + event.toString() + ")");
84  					sharedWorldView.notify(partEvent);
85  					
86  				}
87      		}
88          	//shared or static event will not modify LocalObjects, no need to process it beyond notifying sharedWorldView
89      		else if ( (event instanceof ISharedWorldObjectUpdatedEvent) || (event instanceof IStaticWorldObjectUpdatedEvent) )
90          	{
91      			log.finest("Notyfying sharedWV " + event.toString() + ")");
92      			sharedWorldView.notify(event);
93          		return;
94          	}
95          }    	
96      	
97          // process event
98          // is this method recursively called?
99          if (receiveEventProcessing) {
100             // yes it is -> that means the previous event has not been
101             // processed! ... store this event and allows the previous one
102             // to be fully processed (e.g. postpone raising this event)
103             notifyEventsList.add(event);
104             
105             return;
106         } else {
107             // no it is not ... so raise the flag that we're inside the method
108             receiveEventProcessing = true;
109         }
110        
111         try {
112 	        innerNotify(event);
113 	        // check the events list size, do we have more events to process?
114 	        while (notifyEventsList.size() != 0) {
115 	            // yes -> do it!
116 	            innerNotify(notifyEventsList.poll());
117 	        }
118         } finally {
119         // all events has been processed, drop the flag that we're inside the method
120         	receiveEventProcessing = false;
121         }
122     }
123     
124     @Override
125     public synchronized void notifyAfterPropagation(IWorldChangeEvent event) throws ComponentNotRunningException, ComponentPausedException {
126     	notifyEventsList.addFirst(event);
127     }
128     
129     @Override
130     public synchronized void notifyImmediately(IWorldChangeEvent event) throws ComponentNotRunningException, ComponentPausedException {
131     	
132     	log.finest("LocalWorldView notify( " + event.toString() + ")");
133     	
134     	if (isPaused()) {
135     		throw new ComponentPausedException(controller.getState().getFlag(), this);
136     	}
137     	if (!isRunning()) {
138     		throw new ComponentNotRunningException(controller.getState().getFlag(), this);
139     	}
140     	
141     	if (!( event instanceof ILocalWorldObjectUpdatedEvent))
142         {
143     		if ( event instanceof ICompositeWorldObjectUpdatedEvent)
144     		{
145     			IWorldChangeEvent partEvent = ((ICompositeWorldObjectUpdatedEvent)event).getSharedEvent();
146     			if (partEvent != null) //shared part
147     			{
148     				log.finest("Notyfying sharedWV " + event.toString() + ")");
149     				sharedWorldView.notify(partEvent);	
150     			}
151     			partEvent = ((ICompositeWorldObjectUpdatedEvent)event).getStaticEvent();
152 				if ( partEvent != null) //static part
153 				{
154 					log.finest("Notyfying sharedWV " + event.toString() + ")");
155 					sharedWorldView.notify(partEvent);
156 					
157 				}
158     		}
159         	//shared or static event will not modify LocalObjects, no need to process it beyond notifying sharedWorldView
160     		else if ( (event instanceof ISharedWorldObjectUpdatedEvent) || (event instanceof IStaticWorldObjectUpdatedEvent) )
161         	{
162     			log.finest("Notyfying sharedWV " + event.toString() + ")");
163     			sharedWorldView.notify(event);
164         		return;
165         	}
166         }    	
167     	
168         // process event
169         // is this method recursively called?
170         if (receiveEventProcessing) {
171             // yes it is -> that means the previous event has not been
172             // processed! ... store this event and allows the previous one
173             // to be fully processed (e.g. postpone raising this event)
174             notifyEventsList.add(event);
175             
176             return;
177         } else {
178             // no it is not ... so raise the flag that we're inside the method
179             receiveEventProcessing = true;
180         }
181        
182         try {
183 	        innerNotify(event);
184 	        // check the events list size, do we have more events to process?
185 	        while (notifyEventsList.size() != 0) {
186 	            // yes -> do it!
187 	            innerNotify(notifyEventsList.poll());
188 	        }
189         } finally {
190         // all events has been processed, drop the flag that we're inside the method
191         	receiveEventProcessing = false;
192         }
193     }
194     
195         
196     
197     /**
198      * Catches exceptions. If exception is caught, it calls {@link ComponentController}.fatalError() and this.kill(). 
199      */
200     @Override
201     protected void raiseEvent(IWorldEvent event) {
202     	try {
203     		//log.info("Raise event : " + event);
204     		super.raiseEvent(event);
205     	} catch (Exception e) {
206     		this.controller.fatalError("Exception raising event " + event, e);
207     		this.kill();
208     	}
209     }
210 
211     /**
212      * Used to process IWorldChangeEvent - it has to be either IWorldChangeEvent or IWorldObjectUpdateEvent. Forbids recursion.
213      * <p>
214      * DO NOT CALL SEPARATELY - should be called only from notifyEvent().
215      * <p><p>
216      * You may override it to provide event-specific processing behavior.
217      *
218      * @param event
219      */
220     protected void innerNotify(IWorldChangeEvent event) {    	    	
221        	
222     	NullCheck.check(event, "event");
223     	if (log.isLoggable(Level.FINEST)) log.finest("processing " + event);
224     	
225         if (event instanceof ILocalWorldObjectUpdatedEvent)
226         {
227             objectUpdatedEvent((ILocalWorldObjectUpdatedEvent)event);
228         }
229         else if ( event instanceof ICompositeWorldObjectUpdatedEvent )
230         {
231         	//use the local part only.
232         	ILocalWorldObjectUpdatedEvent locEvent = ((ICompositeWorldObjectUpdatedEvent)event).getLocalEvent();
233         	if ( locEvent != null )
234         	{
235         		objectUpdatedEvent(locEvent);
236         	}
237         }
238         else
239         {
240             if (event instanceof IWorldEventWrapper)
241             {
242                 raiseEvent(((IWorldEventWrapper) event).getWorldEvent());
243             }
244             else if (event instanceof IWorldEvent)
245             {
246             	raiseEvent((IWorldEvent)event);
247             }
248             else 
249             {
250                 throw new PogamutException("Unsupported event type received (" + event.getClass() + ").", this);
251             }
252         }
253     }
254     
255     /**
256      * Called from {@link EventDrivenWorldView#innerNotify(IWorldChangeEvent)} if the event is {@link IWorldObjectUpdatedEvent}
257      * to process it.
258      * 
259      * @param updateEvent
260      */
261     protected void objectUpdatedEvent(ILocalWorldObjectUpdatedEvent updateEvent) {
262     	
263         ILocalWorldObject obj = getMostRecentLocalWorldObject( updateEvent.getId() ); //getting the most recent object available
264              
265         
266         ILocalWorldObject copy = null;
267         //if old timeKeys are held, store the original value, create a new copy, which will be updated
268         if ( obj != null)
269         {
270         	//log.finer("Obj : " + obj.toString() );
271         	copy = obj.clone();
272         }
273         else //may be created event
274         {
275         	copy = null;
276         }
277        
278         IWorldObjectUpdateResult<ILocalWorldObject> updateResult = updateEvent.update(copy);
279         switch (updateResult.getResult()) {
280         case CREATED:            	
281         	//log.finest("CreatedEvent [t:"+updateEvent.getSimTime()+"]");
282         	((EventDrivenSharedWorldView)sharedWorldView).addMsgClass(updateResult.getObject().getId(), updateResult.getObject().getCompositeClass());
283             objectCreated(updateResult.getObject(), updateEvent.getSimTime());
284             return;
285         case UPDATED:
286         	//log.finest("UpdatedEvent [t:"+updateEvent.getSimTime()+"]");
287         	if (updateResult.getObject() != copy) {
288         		throw new PogamutException("Update event " + updateEvent + " did not return the same instance of the object (result UPDATED).", this);
289         	}
290         	
291         	//log.finer("Updated : copy : " + copy.toString() );
292         	
293         	super.addOldLocalWorldObject( obj, updateEvent.getSimTime() );        	
294 
295         	actLocalWorldObjects.put(copy.getId(), copy);
296         	objectUpdated(copy, updateEvent.getSimTime());
297         	
298         	return;
299         case SAME:
300         	return;
301         case DESTROYED:
302         	
303         	super.addOldLocalWorldObject(obj, updateEvent.getSimTime() );        	
304         	objectDestroyed(copy, updateEvent.getSimTime());
305             return;
306         default:
307         	throw new PogamutException("Unhandled object update result " + updateResult.getResult() + " for the object " + obj + ".", this);
308         }
309     }
310 
311     //Since event raising is more complicated in local/shared worldviews, this will be implemented on batchWV level
312     
313     /**
314      * Must be called whenever an object was created, raises correct events.
315      * <p><p>
316      * Might be overridden to provide different behavior.
317      * @param obj
318      */
319     protected void objectCreated(ILocalWorldObject obj, long time) {
320     	addLocalWorldObject(obj);
321        }
322     
323     /**
324      * Must be called whenever an object was updated - raises correct event.
325      * <p><p>
326      * Might be overridden to provide a mechanism that will forbid
327      * update of certain objects (like items that can't move).
328      *
329      * @param obj
330      */
331     protected void objectUpdated(ILocalWorldObject obj, long time) {
332     }
333     
334     /**
335      * Must be called whenever an object was destroyed - raises correct events.
336      * <p><p>
337      * Might be overriden to provide different behavior.
338      * 
339      * @param obj
340      */
341     protected void objectDestroyed(ILocalWorldObject obj, long time) {
342     	removeLocalWorldObject(obj);
343     }
344 
345    
346 	
347 }