package org.priha.core.observation; import java.util.*; import java.util.logging.Logger; import javax.jcr.AccessDeniedException; import javax.jcr.ItemNotFoundException; import javax.jcr.RepositoryException; import javax.jcr.UnsupportedRepositoryOperationException; import javax.jcr.observation.*; import javax.jcr.observation.EventListener; import org.priha.core.*; import org.priha.path.Path; import org.priha.path.PathFactory; import org.priha.util.ChangeStore; import org.priha.util.GenericIterator; import org.priha.util.ChangeStore.Change; /** * Implements an ObservationManager. Again, the actual implementation is a per-Session class instantiated * at request. * */ // FIXME: This class is not particularly optimized. public class ObservationManagerImpl { private static ObservationManagerImpl c_manager = new ObservationManagerImpl(); private Map<String,List<EventListenerWrapper>> m_listeners = new HashMap<String,List<EventListenerWrapper>>(); private Logger log = Logger.getLogger(ObservationManagerImpl.class.getName()); /** * Adds an event listener for a particular Session. * * @param session * @param listener * @param eventTypes * @param absPath * @param isDeep * @param uuid * @param nodeTypeName * @param noLocal * @throws RepositoryException */ public void addEventListener(SessionImpl session, EventListener listener, int eventTypes, Path absPath, boolean isDeep, String[] uuid, String[] nodeTypeName, boolean noLocal) throws RepositoryException { EventListenerWrapper elw = new EventListenerWrapper(session,listener,eventTypes,absPath,isDeep,uuid,nodeTypeName,noLocal); List<EventListenerWrapper> list = m_listeners.get(session.getWorkspace().getName()); if( list == null ) list = new ArrayList<EventListenerWrapper>(); list.add( elw ); m_listeners.put(session.getWorkspace().getName(), list); } /** * Lists EventListeners for a given Session. * * @param session * @return * @throws RepositoryException */ public EventListenerIterator getRegisteredEventListeners(SessionImpl session) throws RepositoryException { List<EventListener> list = new ArrayList<EventListener>(); List<EventListenerWrapper> l = m_listeners.get(session.getWorkspace().getName()); if( l != null ) { for( Iterator<EventListenerWrapper> i = l.iterator() ; i.hasNext(); ) { EventListenerWrapper elw = i.next(); if( elw.getSessionId().equals(session.getId()) ) { list.add( elw.getListener() ); } } } return new Iter(list); } /** * Removes an Event listener (for any session) * @param listener * @throws RepositoryException */ public void removeEventListener(EventListener listener) throws RepositoryException { for( List<EventListenerWrapper> le : m_listeners.values() ) { for( Iterator<EventListenerWrapper> i = le.iterator() ; i.hasNext(); ) { if( i.next().getListener() == listener ) { i.remove(); return; } } } } public void fireEvent( SessionImpl srcSession, ChangeStore changes ) { List<EventListenerWrapper> l = m_listeners.get(srcSession.getWorkspace().getName()); if( l != null ) { for( Iterator<EventListenerWrapper> i = l.iterator() ; i.hasNext(); ) { EventListenerWrapper elw = i.next(); // Fire the event try { elw.getListener().onEvent( new EventIteratorImpl(filterEvents(srcSession,elw,changes)) ); } catch( Exception e ) { log.warning("Unable to fire event "+ e.getMessage()); } } } } private List<Event> filterEvents( SessionImpl session, EventListenerWrapper elw, ChangeStore changes ) throws ItemNotFoundException, AccessDeniedException, RepositoryException { ArrayList<Event> list = new ArrayList<Event>(); for( Change c : changes ) { System.out.println(c); // Session filtering if( elw.isNoLocal() && session.getId().equals(elw.getSessionId()) ) { continue; } // Tags filtering if( !c.getItem().isNode() ) { if( ((PropertyImpl)c.getItem()).isTransient() ) continue; } // Internal tags filtering if( c.getPath().getLastComponent().equals(NodeImpl.Q_PRIHA_TMPMOVE) ) continue; // Path filtering if( elw.getAbsPath() != null ) { Path itemPath = c.getPath(); Path ePath = elw.getAbsPath(); // // This fairly complicated sentence fulfils the contract on page 272 of JCR-1.0 spec. // if( !(( (!itemPath.isRoot() && ePath.equals(itemPath.getParentPath())) && !elw.isDeep()) || (ePath.isParentOf(itemPath) && elw.isDeep() )) ) { continue; } } // UUID filtering if( elw.getUuids() != null ) { NodeImpl parent = c.getItem().getParent(); try { String parentUUID = parent.getUUID(); if( Arrays.binarySearch( elw.getUuids(), parentUUID ) < 0 ) continue; } catch( UnsupportedRepositoryOperationException e ) {} } // Nodetype filtering if( elw.getNodeTypeNames() != null ) { NodeImpl parent = c.getItem().getParent(); boolean success = false; for( String s : elw.getNodeTypeNames() ) { if( parent.isNodeType(s) ) { success = true; break; } } if( !success ) continue; } // Transient changes filtering. Priha will actually list all the events, both additions and removals // so we need to filter out the ones which result in no change in the repository. if( c.getState() != ItemState.REMOVED ) { if( changes.getLatestChange(c.getPath()).getState() == ItemState.REMOVED ) continue; } // EventType filtering int eventType = -1; switch( c.getState() ) { case NEW: case MOVED: if( c.getItem().isNode() ) eventType = Event.NODE_ADDED; else eventType = Event.PROPERTY_ADDED; break; case UPDATED: if( !c.getItem().isNode() ) eventType = Event.PROPERTY_CHANGED; break; case REMOVED: if( c.getItem().isNode() ) eventType = Event.NODE_REMOVED; else eventType = Event.PROPERTY_REMOVED; break; } if( (elw.getEventTypes() & eventType) == 0 ) { continue; } // Add to the event list if( eventType != -1 ) { Event e = new EventImpl(session, eventType, c.getPath()); list.add( e ); System.out.println("Firing "+e); } } return list; } private static class EventIteratorImpl extends GenericIterator implements EventIterator { public EventIteratorImpl(List<Event> list) { super(list); } public Event nextEvent() { return (Event) super.next(); } } /** * Provides iteration through EventListeners. * */ private static class Iter extends GenericIterator implements EventListenerIterator { public Iter(List<EventListener> list) { super( list ); } public EventListener nextEventListener() { return (EventListener)super.next(); } public Object next() { return nextEventListener(); } } /** * Wraps everything interesting for an EventListener. */ private static class EventListenerWrapper { private String m_sessionId; private EventListener m_listener; private int m_eventTypes; private Path m_absPath; private boolean m_isDeep; private String[] m_uuids; private String[] m_nodeTypeNames; private boolean m_noLocal; public EventListenerWrapper( SessionImpl session, EventListener listener, int eventTypes, Path absPath, boolean isDeep, String[] uuids, String[] nodeTypeNames, boolean noLocal ) { m_sessionId = session.getId(); m_listener = listener; m_eventTypes = eventTypes; m_absPath = absPath; m_isDeep = isDeep; m_uuids = uuids; m_nodeTypeNames = nodeTypeNames; m_noLocal = noLocal; if( m_uuids != null ) Arrays.sort(m_uuids); } public String getSessionId() { return m_sessionId; } public EventListener getListener() { return m_listener; } public int getEventTypes() { return m_eventTypes; } public Path getAbsPath() { return m_absPath; } public boolean isDeep() { return m_isDeep; } public String[] getUuids() { return m_uuids; } public String[] getNodeTypeNames() { return m_nodeTypeNames; } public boolean isNoLocal() { return m_noLocal; } } /** * Session-local ObservationManager. */ public class Impl implements ObservationManager { SessionImpl m_session; public Impl(SessionImpl session) { m_session = session; } public void addEventListener(EventListener listener, int eventTypes, String absPath, boolean isDeep, String[] uuid, String[] nodeTypeName, boolean noLocal) throws RepositoryException { ObservationManagerImpl.this.addEventListener(m_session, listener, eventTypes, PathFactory.getPath(m_session, absPath), isDeep, uuid, nodeTypeName, noLocal); } public EventListenerIterator getRegisteredEventListeners() throws RepositoryException { return ObservationManagerImpl.this.getRegisteredEventListeners(m_session); } public void removeEventListener(EventListener listener) throws RepositoryException { ObservationManagerImpl.this.removeEventListener(listener); } public void fireEvents( ChangeStore changes ) { ObservationManagerImpl.this.fireEvent( m_session, changes ); } } /** * Get an instance for a Workspace. */ public static ObservationManagerImpl.Impl getInstance(WorkspaceImpl workspaceImpl) { return c_manager.new Impl(workspaceImpl.getSession()); } }