You are viewing a plain text version of this content. The canonical link for it is here.
Posted to slide-dev@jakarta.apache.org by st...@apache.org on 2004/07/01 20:30:11 UTC

cvs commit: jakarta-slide/proposals/jcrri/src/org/apache/slide/jcr/core/observation EventListenerIteratorImpl.java EventState.java EventStateCollection.java FilteredEventIterator.java ObservationManagerFactory.java

stefan      2004/07/01 11:30:10

  Added:       proposals/jcrri/src/org/apache/slide/jcr/core/observation
                        EventListenerIteratorImpl.java EventState.java
                        EventStateCollection.java
                        FilteredEventIterator.java
                        ObservationManagerFactory.java
  Log:
  jcrri
  
  Revision  Changes    Path
  1.1                  jakarta-slide/proposals/jcrri/src/org/apache/slide/jcr/core/observation/EventListenerIteratorImpl.java
  
  Index: EventListenerIteratorImpl.java
  ===================================================================
  /*
   * $Id: EventListenerIteratorImpl.java,v 1.1 2004/07/01 18:30:08 stefan Exp $
   *
   * Copyright 2002-2004 Day Management AG, Switzerland.
   *
   * Licensed under the Day RI License, Version 2.0 (the "License"),
   * as a reference implementation of the following specification:
   *
   *   Content Repository API for Java Technology, revision 0.13
   *        <http://www.jcp.org/en/jsr/detail?id=170>
   *
   * You may not use this file except in compliance with the License.
   * You may obtain a copy of the License files at
   *
   *     http://www.day.com/content/en/licenses/day-ri-license-2.0
   *     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 org.apache.slide.jcr.core.observation;
  
  import javax.jcr.observation.EventListenerIterator;
  import javax.jcr.observation.EventListener;
  import javax.jcr.Ticket;
  import java.util.Iterator;
  import java.util.Collection;
  import java.util.NoSuchElementException;
  
  /**
   * @author Marcel Reutegger
   * @version $Revision: 1.1 $, $Date: 2004/07/01 18:30:08 $
   */
  class EventListenerIteratorImpl implements EventListenerIterator {
  
      /**
       * This iterator will return {@link EventListener}s registered by this
       * <code>Ticket</code>.
       */
      private final Ticket ticket;
  
      /** Iterator over {@link EventConsumer} instances */
      private final Iterator consumers;
  
      /**
       * The next <code>EventListener</code> that belongs to the ticket
       * passed in the constructor of this <code>EventListenerIteratorImpl</code>.
       */
      private EventListener next;
  
      /** Current position */
      private long pos = 0;
  
      /**
       * Creates a new <code>EventListenerIteratorImpl</code>.
       *
       * @param ticket
       * @param consumers
       * @throws NullPointerException if <code>ticket</code> or <code>consumer</code>
       *                              is <code>null</code>.
       */
      EventListenerIteratorImpl(Ticket ticket, Collection consumers) {
          if (ticket == null) throw new NullPointerException("ticket");
          if (consumers == null) throw new NullPointerException("consumers");
          this.ticket = ticket;
          this.consumers = consumers.iterator();
          fetchNext();
      }
  
      /**
       * @see javax.jcr.observation.EventListenerIterator#nextEventListener()
       */
      public EventListener nextEventListener() {
          if (next == null) throw new NoSuchElementException();
          EventListener l = next;
          fetchNext();
          pos++;
          return l;
      }
  
      /**
       * @see javax.jcr.RangeIterator#skip(int)
       */
      public void skip(int skipNum) {
          while (skipNum-- > 0) {
              next();
          }
      }
  
      /**
       * Always returns <code>-1</code>.
       * @return <code>-1</code>.
       */
      public long getSize() {
          return -1;
      }
  
      /**
       * @see javax.jcr.RangeIterator#getPos()
       */
      public long getPos() {
          return pos;
      }
  
      /**
       * Remove is not supported on this Iterator.
       * @exception UnsupportedOperationException
       */
      public void remove() {
          throw new UnsupportedOperationException("EventListenerIterator.remove()");
      }
  
      /**
       * Returns <tt>true</tt> if the iteration has more elements. (In other
       * words, returns <tt>true</tt> if <tt>next</tt> would return an element
       * rather than throwing an exception.)
       *
       * @return <tt>true</tt> if the consumers has more elements.
       */
      public boolean hasNext() {
          return (next != null);
      }
  
      /**
       * @see Iterator#next()
       */
      public Object next() {
          return nextEventListener();
      }
  
      /**
       * Fetches the next {@link javax.jcr.observation.EventListener} associated
       * with the <code>Ticket</code> passed in the constructor of this
       * <code>EventListenerIteratorImpl</code> from all register
       * <code>EventListener</code>s
       */
      private void fetchNext() {
          EventConsumer consumer;
          next = null;
          while (next == null && consumers.hasNext()) {
              consumer = (EventConsumer)consumers.next();
              if (consumer.getUserId().equals(ticket.getUserId())) {
                  next = consumer.getEventListener();
              }
  
          }
      }
  }
  
  
  
  1.1                  jakarta-slide/proposals/jcrri/src/org/apache/slide/jcr/core/observation/EventState.java
  
  Index: EventState.java
  ===================================================================
  /*
   * $Id: EventState.java,v 1.1 2004/07/01 18:30:08 stefan Exp $
   *
   * Copyright 2002-2004 Day Management AG, Switzerland.
   *
   * Licensed under the Day RI License, Version 2.0 (the "License"),
   * as a reference implementation of the following specification:
   *
   *   Content Repository API for Java Technology, revision 0.13
   *        <http://www.jcp.org/en/jsr/detail?id=170>
   *
   * You may not use this file except in compliance with the License.
   * You may obtain a copy of the License files at
   *
   *     http://www.day.com/content/en/licenses/day-ri-license-2.0
   *     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 org.apache.slide.jcr.core.observation;
  
  import org.apache.slide.jcr.core.QName;
  
  import javax.jcr.observation.EventType;
  
  /**
   * The <code>EventState</code> class encapsulates the ticket
   * independent state of an {@link javax.jcr.observation.Event}.
   *
   * @author Marcel Reutegger
   * @version $Revision: 1.1 $
   */
  class EventState {
  
      /** The {@link javax.jcr.observation.EventType} of this event. */
      private final long type;
  
      /** The UUID of the parent node associated with this event. */
      private final String parentUUID;
  
      /** The qualified name of the child item associated with this event. */
      private final QName childName;
  
      /** The userId of the ticket that caused this event. */
      private final String userId;
  
      /** Cached String representation of this <code>EventState</code>. */
      private String stringValue;
  
      /** Cached hashCode value for this <code>Event</code>. */
      private int hashCode;
  
      /**
       * Creates a new <code>EventState</code> instance.
       *
       * @param type the {@link javax.jcr.observation.EventType} of this
       *  event.
       * @param parentUUID the uuid of the parent node associated with this event.
       * @param childName the qualified name of the child item associated with
       *   this event.
       * @param userId the userId of the {@link javax.jcr.Ticket} that
       *   caused this event.
       */
      private EventState(long type, String parentUUID, QName childName, String userId) {
          this.type = type;
          this.parentUUID = parentUUID;
          this.childName = childName;
          this.userId = userId;
      }
  
      //-----------------< factory methods >--------------------------------------
  
      /**
       * Creates a new {@link javax.jcr.observation.Event} of type
       * {@link javax.jcr.observation.EventType#CHILD_NODE_ADDED}.
       *
       * @param parentUUID the uuid of the parent node associated with
       *   this <code>EventState</code>.
       * @param childName the qualified name of the child node that was added.
       * @param userId the userId of the ticket that added the node.
       * @return an <code>EventState</code> instance.
       */
      public static EventState ChildNodeAdded(String parentUUID,
                                         QName childName,
                                         String userId) {
          return new EventState(EventType.CHILD_NODE_ADDED,
                  parentUUID,
                  childName,
                  userId);
      }
  
      /**
       * Creates a new {@link javax.jcr.observation.Event} of type
       * {@link javax.jcr.observation.EventType#CHILD_NODE_REMOVED}.
       *
       * @param parentUUID the uuid of the parent node associated with
       *   this <code>EventState</code>.
       * @param childName the qualified name of the child node that was removed.
       * @param userId the userId of the ticket that removed the node.
       * @return an <code>EventState</code> instance.
       */
      public static EventState ChildNodeRemoved(String parentUUID,
                                           QName childName,
                                           String userId) {
          return new EventState(EventType.CHILD_NODE_REMOVED,
                  parentUUID,
                  childName,
                  userId);
      }
  
      /**
       * Creates a new {@link javax.jcr.observation.Event} of type
       * {@link javax.jcr.observation.EventType#PROPERTY_ADDED}.
       *
       * @param parentUUID the uuid of the parent node associated with
       *   this <code>EventState</code>.
       * @param childName the qualified name of the property that was added.
       * @param userId the userId of the ticket that added the property.
       * @return an <code>EventState</code> instance.
       */
      public static EventState PropertyAdded(String parentUUID,
                                        QName childName,
                                        String userId) {
          return new EventState(EventType.PROPERTY_ADDED,
                  parentUUID,
                  childName,
                  userId);
      }
  
      /**
       * Creates a new {@link javax.jcr.observation.Event} of type
       * {@link javax.jcr.observation.EventType#PROPERTY_REMOVED}.
       *
       * @param parentUUID the uuid of the parent node associated with
       *   this <code>EventState</code>.
       * @param childName the qualified name of the property that was removed.
       * @param userId the userId of the ticket that removed the property.
       * @return an <code>EventState</code> instance.
       */
      public static EventState PropertyRemoved(String parentUUID,
                                          QName childName,
                                          String userId) {
          return new EventState(EventType.PROPERTY_REMOVED,
                  parentUUID,
                  childName,
                  userId);
      }
  
      /**
       * Creates a new {@link javax.jcr.observation.Event} of type
       * {@link javax.jcr.observation.EventType#PROPERTY_CHANGED}.
       *
       * @param parentUUID the uuid of the parent node associated with
       *   this <code>EventState</code>.
       * @param childName the qualified name of the property that changed.
       * @param userId the userId of the ticket that changed the property.
       * @return an <code>EventState</code> instance.
       */
      public static EventState PropertyChanged(String parentUUID,
                                          QName childName,
                                          String userId) {
          return new EventState(EventType.PROPERTY_CHANGED,
                  parentUUID,
                  childName,
                  userId);
      }
  
      /**
       * @see javax.jcr.observation.Event#getType()
       */
      public long getType() {
          return type;
      }
  
      /**
       * Returns the uuid of the parent node.
       * @return the uuid of the parent node.
       */
      public String getParentUUID() {
          return parentUUID;
      }
  
      /**
       * Returns the {@link QName} of the
       * {@link javax.jcr.Item} associated with this event.
       * @return the <code>QName</code> associated with this event.
       */
      public QName getChildItemQName() {
          return childName;
      }
  
      /**
       * @see javax.jcr.observation.Event#getUserId()
       */
      public String getUserId() {
          return userId;
      }
  
      /**
       * Returns a String representation of this <code>EventState</code>.
       * @return a String representation of this <code>EventState</code>.
       */
      public String toString() {
          if (stringValue == null) {
              StringBuffer sb = new StringBuffer();
              sb.append("EventState: ").append(valueOf(type));
              sb.append(", Parent: ").append(parentUUID);
              sb.append(", Child: ").append(childName);
              sb.append(", UserId: ").append(userId);
              stringValue = sb.toString();
          }
          return stringValue;
      }
  
      /**
       * Returns a hashCode for this <code>EventState</code>.
       * @return a hashCode for this <code>EventState</code>.
       */
      public int hashCode() {
          int h = hashCode;
          if (h == 0) {
              h = 37;
              h = 37*h + (int)type;
              h = 37*h + parentUUID.hashCode();
              h = 37*h + childName.hashCode();
              h = 37*h + userId.hashCode();
              hashCode = h;
          }
          return hashCode;
      }
  
      /**
       * Returns <code>true</code> if this <code>EventState</code> is equal to
       * another object.
       *
       * @param obj the reference object with which to compare.
       * @return <code>true</code> if object <code>obj</code> is equal to this
       *         <code>EventState</code>; <code>false</code> otherwise.
       */
      public boolean equals(Object obj) {
          if (obj == this) {
              return true;
          }
          if (obj instanceof EventState) {
              EventState other = (EventState) obj;
              return this.type == other.type
                      && this.parentUUID.equals(other.parentUUID)
                      && this.childName.equals(other.childName)
                      && this.userId.equals(other.userId);
          }
          return false;
      }
  
      /**
       * Returns a String representation of <code>eventType</code>.
       *
       * @param eventType an event type defined by {@link EventType}.
       * @return a String representation of <code>eventType</code>.
       */
      public static String valueOf(long eventType) {
          if (eventType == EventType.CHILD_NODE_ADDED) {
              return "ChildNodeAdded";
          } else if (eventType == EventType.CHILD_NODE_REMOVED) {
              return "ChildNodeRemoved";
          } else if (eventType == EventType.PROPERTY_ADDED) {
              return "PropertyAdded";
          } else if (eventType == EventType.PROPERTY_CHANGED) {
              return "PropertyChanged";
          } else if (eventType == EventType.PROPERTY_REMOVED) {
              return "PropertyRemoved";
          } else {
              return "UnknownEventType";
          }
      }
  
  }
  
  
  
  1.1                  jakarta-slide/proposals/jcrri/src/org/apache/slide/jcr/core/observation/EventStateCollection.java
  
  Index: EventStateCollection.java
  ===================================================================
  /*
   * $Id: EventStateCollection.java,v 1.1 2004/07/01 18:30:08 stefan Exp $
   *
   * Copyright 2002-2004 Day Management AG, Switzerland.
   *
   * Licensed under the Day RI License, Version 2.0 (the "License"),
   * as a reference implementation of the following specification:
   *
   *   Content Repository API for Java Technology, revision 0.13
   *        <http://www.jcp.org/en/jsr/detail?id=170>
   *
   * You may not use this file except in compliance with the License.
   * You may obtain a copy of the License files at
   *
   *     http://www.day.com/content/en/licenses/day-ri-license-2.0
   *     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 org.apache.slide.jcr.core.observation;
  
  import org.apache.slide.jcr.core.state.*;
  import org.apache.log4j.Logger;
  
  import javax.jcr.RepositoryException;
  import java.util.*;
  
  /**
   * The <code>EventStateCollection</code> class implements how {@link EventState}
   * objects are created based on the {@link org.apache.slide.jcr.core.state.ItemState}s
   * passed to the {@link #createEventStates} method.
   *
   * @author mreutegg
   * @version $Revision: 1.1 $
   */
  final public class EventStateCollection {
  
      /** Logger instance for this class */
      private static Logger log = Logger.getLogger(EventStateCollection.class);
  
      /** List of events */
      private final List events = new ArrayList();
  
      /** UserId of the ticket that created these events */
      private final String userId;
  
      /**
       * Creates a new empty <code>EventStateCollection</code>. The <code>userId</code>
       * indicates that events in this collection are created by a ticket
       * associated to the user with id <code>userId</code>.
       *
       * @param userId the user id of the ticket that created these events.
       */
      public EventStateCollection(String userId) {
          this.userId = userId;
      }
  
      /**
       * Creates {@link EventState}s for the passed {@link org.apache.slide.jcr.core.state.ItemState state}
       * instance.
       *
       * @param state the transient <code>ItemState</code> for whom
       *   to create {@link EventState}s.
       * @throws RepositoryException if an error occurs.
       */
      public void createEventStates(ItemState state)
              throws RepositoryException {
          int status = state.getStatus();
  
          if (status == ItemState.STATUS_EXISTING_MODIFIED ||
                  status == ItemState.STATUS_NEW) {
  
              if (state.isNode()) {
  
                  // 1) check added properties
                  NodeState currentNode = (NodeState)state;
                  List addedProperties = currentNode.getAddedPropertyEntries();
                  for (Iterator it = addedProperties.iterator(); it.hasNext(); ) {
                      NodeState.PropertyEntry prop = (NodeState.PropertyEntry)it.next();
                      events.add(EventState.PropertyAdded(currentNode.getUUID(),
                              prop.getName(),
                              userId));
                  }
  
                  // 2) check removed properties
                  List removedProperties = currentNode.getRemovedPropertyEntries();
                  for (Iterator it = removedProperties.iterator(); it.hasNext(); ) {
                      NodeState.PropertyEntry prop = (NodeState.PropertyEntry)it.next();
                      events.add(EventState.PropertyRemoved(currentNode.getUUID(),
                              prop.getName(),
                              userId));
                  }
  
                  // 3) check for added nodes
                  List addedNodes = currentNode.getAddedChildNodeEntries();
                  for (Iterator it = addedNodes.iterator(); it.hasNext(); ) {
                      NodeState.ChildNodeEntry child = (NodeState.ChildNodeEntry)it.next();
                      events.add(EventState.ChildNodeAdded(currentNode.getUUID(),
                              child.getName(),
                              userId));
                  }
  
                  // 4) check for removed nodes
                  List removedNodes = currentNode.getRemovedChildNodeEntries();
                  for (Iterator it = removedNodes.iterator(); it.hasNext(); ) {
                      NodeState.ChildNodeEntry child = (NodeState.ChildNodeEntry)it.next();
                      events.add(EventState.ChildNodeRemoved(currentNode.getUUID(),
                              child.getName(),
                              userId));
                  }
              } else {
                  // only add property changed event if property is existing
                  if (state.getStatus() == ItemState.STATUS_EXISTING_MODIFIED) {
                      events.add(EventState.PropertyChanged(state.getParentUUID(),
                              ((PropertyState)state).getName(),
                              userId));
                  }
              }
          }
      }
  
      /**
       * Returns an iterator over {@link EventState} instance.
       * @return an iterator over {@link EventState} instance.
       */
      Iterator iterator() {
          return events.iterator();
      }
  }
  
  
  
  1.1                  jakarta-slide/proposals/jcrri/src/org/apache/slide/jcr/core/observation/FilteredEventIterator.java
  
  Index: FilteredEventIterator.java
  ===================================================================
  /*
   * $Id: FilteredEventIterator.java,v 1.1 2004/07/01 18:30:08 stefan Exp $
   *
   * Copyright 2002-2004 Day Management AG, Switzerland.
   *
   * Licensed under the Day RI License, Version 2.0 (the "License"),
   * as a reference implementation of the following specification:
   *
   *   Content Repository API for Java Technology, revision 0.13
   *        <http://www.jcp.org/en/jsr/detail?id=170>
   *
   * You may not use this file except in compliance with the License.
   * You may obtain a copy of the License files at
   *
   *     http://www.day.com/content/en/licenses/day-ri-license-2.0
   *     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 org.apache.slide.jcr.core.observation;
  
  import org.apache.log4j.Logger;
  
  import javax.jcr.observation.EventIterator;
  import javax.jcr.observation.Event;
  import javax.jcr.RepositoryException;
  import java.util.Iterator;
  import java.util.NoSuchElementException;
  
  /**
   *
   * @author Marcel Reutegger
   */
  class FilteredEventIterator implements EventIterator {
  
      /** Logger instance for this class */
      private static final Logger log
              = Logger.getLogger(FilteredEventIterator.class);
  
      /** The actual {@link EventState}s fired by the workspace (unfiltered) */
      private final Iterator actualEvents;
  
      /** For filtering the {@link javax.jcr.observation.Event}s. */
      private final EventFilter filter;
  
      /** The next {@link javax.jcr.observation.Event} in this iterator */
      private Event next;
  
      /** Current position */
      private long pos = 0;
  
      /**
       * Creates a new <code>FilteredEventIterator</code>.
       *
       * @param c an unmodifiable Collection of
       *    {@link javax.jcr.observation.Event}s.
       * @param filter only event that pass the filter will be
       *    dispatched to the event listener.
       */
      public FilteredEventIterator(EventStateCollection c, EventFilter filter) {
          actualEvents = c.iterator();
          this.filter = filter;
          fetchNext();
      }
  
      /**
       * @see Iterator#next()
       */
      public Object next() {
          if (next == null) throw new NoSuchElementException();
          Event e = next;
          fetchNext();
          pos++;
          return e;
      }
  
      /**
       * @see EventIterator#nextEvent()
       */
      public Event nextEvent() {
          return (Event)next();
      }
  
      /**
       * @see javax.jcr.RangeIterator#skip(int)
       */
      public void skip(int skipNum) {
          while (skipNum-- > 0) {
              next();
          }
      }
  
      /**
       * Always returns <code>-1</code>.
       * @return <code>-1</code>.
       */
      public long getSize() {
          return -1;
      }
  
      /**
       * @see javax.jcr.RangeIterator#getPos()
       */
      public long getPos() {
          return pos;
      }
  
      /**
       * This method is not supported.
       * Always throws a <code>UnsupportedOperationException</code>.
       */
      public void remove() {
          throw new UnsupportedOperationException("EventIterator.remove()");
      }
  
      /**
       * Returns <tt>true</tt> if the iteration has more elements. (In other
       * words, returns <tt>true</tt> if <tt>next</tt> would return an element
       * rather than throwing an exception.)
       *
       * @return <tt>true</tt> if the iterator has more elements.
       */
      public boolean hasNext() {
          return (next != null);
      }
  
      /**
       * Fetches the next Event from the collection of events
       * passed in the constructor of <code>FilteredEventIterator</code>
       * that is allowed by the {@link EventFilter}.
       */
      private void fetchNext() {
          EventState state;
          next = null;
          while (next == null && actualEvents.hasNext()) {
              state = (EventState)actualEvents.next();
              try {
                  next = filter.blocks(state) ? null : new EventImpl(filter.getTicket(),
                          filter.getItemManager(),
                          state);
              } catch (RepositoryException e) {
                  log.error("Exception while applying filter.", e);
              }
          }
      }
  }
  
  
  
  1.1                  jakarta-slide/proposals/jcrri/src/org/apache/slide/jcr/core/observation/ObservationManagerFactory.java
  
  Index: ObservationManagerFactory.java
  ===================================================================
  /*
   * $Id: ObservationManagerFactory.java,v 1.1 2004/07/01 18:30:08 stefan Exp $
   *
   * Copyright 2002-2004 Day Management AG, Switzerland.
   *
   * Licensed under the Day RI License, Version 2.0 (the "License"),
   * as a reference implementation of the following specification:
   *
   *   Content Repository API for Java Technology, revision 0.13
   *        <http://www.jcp.org/en/jsr/detail?id=170>
   *
   * You may not use this file except in compliance with the License.
   * You may obtain a copy of the License files at
   *
   *     http://www.day.com/content/en/licenses/day-ri-license-2.0
   *     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 org.apache.slide.jcr.core.observation;
  
  import org.apache.slide.jcr.core.ItemManager;
  import org.apache.slide.jcr.core.TicketImpl;
  import org.apache.commons.collections.Buffer;
  import org.apache.commons.collections.BufferUtils;
  import org.apache.commons.collections.UnboundedFifoBuffer;
  import org.apache.log4j.Logger;
  
  import javax.jcr.observation.*;
  import javax.jcr.observation.EventListener;
  import javax.jcr.RepositoryException;
  import javax.jcr.nodetype.NodeTypeManager;
  import javax.jcr.nodetype.NodeType;
  import java.util.*;
  
  /**
   * The class <code>ObservationManagerFactory</code> creates new
   * <code>ObservationManager</code> instances for tickets. It also implements the
   * {@link EventDispatcher} interface where {@link EventStateCollection}s can be
   * dispatched to {@link javax.jcr.observation.EventListener}s.
   *
   * @author Marcel Reutegger
   */
  final public class ObservationManagerFactory implements EventDispatcher, Runnable {
  
      /** Logger instance for this class */
      private static final Logger log
              = Logger.getLogger(ObservationManagerFactory.class);
  
      /** Dummy DispatchAction indicating the notification thread to end */
      private static final DispatchAction DISPOSE_MARKER = new DispatchAction(null, null);
  
      /** Currently active <code>EventConsumer</code>s for notification */
      private Set activeConsumers = new HashSet();
  
      /** Set of <code>EventConsumer</code>s for read only Set access */
      private Set readOnlyConsumers;
  
      /** synchronization monitor for listener changes */
      private Object consumerChange = new Object();
  
      /** Contains the pending events that will be delivered to event listeners */
      private Buffer eventQueue
              = BufferUtils.blockingBuffer(new UnboundedFifoBuffer());
  
      /** The background notification thread */
      private Thread notificationThread;
  
      /**
       * Creates a new <code>ObservationManagerFactory</code> instance
       * and starts the notification thread deamon.
       */
      public ObservationManagerFactory() {
          notificationThread = new Thread(this, "ObservationManager");
          notificationThread.setDaemon(true);
          notificationThread.start();
      }
  
      /**
       * Disposes this <code>ObservationManager</code>. This will
       * effectively stop the background notification thread.
       */
      public void dispose() {
          // dispatch dummy event to mark end of notification
          eventQueue.add(DISPOSE_MARKER);
          try {
              notificationThread.join();
          } catch (InterruptedException e) {
              // FIXME log exception ?
          }
          log.info("Notification of EventListeners stopped.");
      }
  
      /**
       * Returns an unmodifieable <code>Set</code> of <code>EventConsumer</code>s.
       * @return <code>Set</code> of <code>EventConsumer</code>s.
       */
      private Set getConsumers() {
          synchronized (consumerChange) {
              if (readOnlyConsumers == null) {
                  readOnlyConsumers = Collections.unmodifiableSet(
                          new HashSet(activeConsumers));
              }
              return readOnlyConsumers;
          }
      }
  
      /**
       * Creates a new <code>ticket</code> local <code>ObservationManager</code>
       * with an associated <code>NamespaceResolver</code>.
       *
       * @param ticket the ticket.
       * @param itemMgr the <code>ItemManager</code> of the <code>ticket</code>.
       * @return an <code>ObservationManager</code>.
       */
      public ObservationManager createObservationManager(TicketImpl ticket,
                                                         ItemManager itemMgr) {
          return new TicketLocalObservationManager(ticket, itemMgr);
      }
  
  
      /**
       * Implements the run method of the background notification
       * thread.
       */
      public void run() {
          DispatchAction action;
          while ((action = (DispatchAction)eventQueue.remove()) != DISPOSE_MARKER) {
  
              log.debug("got EventStateCollection");
              log.debug("event delivery to " + action.eventConsumers.size() + " consumers started...");
              for (Iterator it = action.eventConsumers.iterator(); it.hasNext(); ) {
                  EventConsumer c = (EventConsumer)it.next();
                  try {
                      c.consumeEvents(action.eventStates);
                  } catch (Throwable t) {
                      log.error("EventConsumer threw exception.", t);
                      // move on to the next consumer
                  }
              }
              log.debug("event delivery finished.");
  
          }
      }
  
      //-------------------------< EventDispatcher >------------------------------
  
      /**
       * @see EventDispatcher#dispatchEvents
       */
      public void dispatchEvents(EventStateCollection events) {
          eventQueue.add(new DispatchAction(events, getConsumers()));
      }
  
      //----------------------------< adapter class >-----------------------------
  
      /**
       * Each <code>Ticket</code> instance has its own <code>ObservationManager</code>
       * instance. The class <code>TicketLocalObservationManager</code> implements
       * this behaviour.
       */
      class TicketLocalObservationManager implements ObservationManager {
  
          /**
           * The <code>Ticket</code> this <code>ObservationManager</code>
           * belongs to.
           */
          private TicketImpl ticket;
  
          /**
           * The <code>ItemManager</code> for this <code>ObservationManager</code>.
           */
          private ItemManager itemMgr;
  
          /**
           * Creates an <code>ObservationManager</code> instance.
           *
           * @param ticket the <code>Ticket</code> this ObservationManager
           * belongs to.
           * @param itemMgr {@link org.apache.slide.jcr.core.ItemManager} of the passed
           * <code>Ticket</code>.
           *
           * @exception NullPointerException if <code>ticket</code> or <code>itemMgr</code>
           * is <code>null</code>.
           */
          TicketLocalObservationManager(TicketImpl ticket,
                                        ItemManager itemMgr) {
              if (ticket == null) throw new NullPointerException("ticket");
              if (itemMgr == null) throw new NullPointerException("itemMgr");
  
              this.ticket = ticket;
              this.itemMgr = itemMgr;
          }
  
          /**
           * @see ObservationManager#addEventListener
           */
          public void addEventListener(EventListener listener,
                                       long eventTypes,
                                       String absPath,
                                       boolean isDeep,
                                       String[] uuid,
                                       String[] nodeTypeName,
                                       boolean noLocal)
                  throws RepositoryException {
  
              // create NodeType instances from names
              NodeType[] nodeTypes;
              if (nodeTypeName == null) {
                  nodeTypes = null;
              } else {
                  NodeTypeManager ntMgr = ticket.getWorkspace().getNodeTypeManager();
                  nodeTypes = new NodeType[nodeTypeName.length];
                  for (int i = 0; i < nodeTypes.length; i++) {
                      nodeTypes[i] = ntMgr.getNodeType(nodeTypeName[i]);
                  }
              }
  
              // create filter
              EventFilter filter = new EventFilter(itemMgr,
                      ticket,
                      eventTypes,
                      absPath,
                      isDeep,
                      uuid,
                      nodeTypes,
                      noLocal);
  
              EventConsumer consumer =
                      new EventConsumer(ticket.getUserId(), listener, filter);
  
              synchronized (consumerChange) {
                  // remove existing if any
                  activeConsumers.remove(consumer);
                  // re-add it
                  activeConsumers.add(consumer);
                  // reset read only consumer set
                  readOnlyConsumers = null;
              }
          }
  
          /**
           * @see ObservationManager#removeEventListener(javax.jcr.observation.EventListener)
           */
          public void removeEventListener(EventListener listener)
                  throws RepositoryException {
              EventConsumer consumer =
                      new EventConsumer(ticket.getUserId(), listener, EventFilter.BLOCK_ALL);
  
              synchronized (consumerChange) {
                  activeConsumers.remove(consumer);
                  // reset read only listener set
                  readOnlyConsumers = null;
              }
          }
  
          /**
           * @see ObservationManager#getRegisteredEventListeners()
           */
          public EventListenerIterator getRegisteredEventListeners()
                  throws RepositoryException {
              return new EventListenerIteratorImpl(ticket, getConsumers());
          }
      }
  }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: slide-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: slide-dev-help@jakarta.apache.org