All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.jackrabbit.webdav.jcr.observation.SubscriptionManagerImpl Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.jackrabbit.webdav.jcr.observation;

import org.apache.jackrabbit.webdav.DavException;
import org.apache.jackrabbit.webdav.DavResourceLocator;
import org.apache.jackrabbit.webdav.DavServletResponse;
import org.apache.jackrabbit.webdav.transaction.TransactionResource;
import org.apache.jackrabbit.webdav.jcr.JcrDavException;
import org.apache.jackrabbit.webdav.jcr.JcrDavSession;
import org.apache.jackrabbit.webdav.jcr.transaction.TransactionListener;
import org.apache.jackrabbit.webdav.observation.EventDiscovery;
import org.apache.jackrabbit.webdav.observation.ObservationResource;
import org.apache.jackrabbit.webdav.observation.Subscription;
import org.apache.jackrabbit.webdav.observation.SubscriptionDiscovery;
import org.apache.jackrabbit.webdav.observation.SubscriptionInfo;
import org.apache.jackrabbit.webdav.observation.SubscriptionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;
import org.w3c.dom.Document;

import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.observation.ObservationManager;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;

/**
 * SubscriptionManager collects all subscriptions requested, handles
 * the subscription timeout and provides METHODS to discover subscriptions
 * present on a given resource as well as events for an specific subscription.
 */
// todo: make sure all expired subscriptions are removed!
public class SubscriptionManagerImpl implements SubscriptionManager, TransactionListener {

    private static Logger log = LoggerFactory.getLogger(SubscriptionManager.class);

    /**
     * Map containing all {@link org.apache.jackrabbit.webdav.observation.Subscription subscriptions}.
     */
    private final SubscriptionMap subscriptions = new SubscriptionMap();

    private final Map> transactionListenerById = new HashMap>();

    /**
     * Retrieve the {@link org.apache.jackrabbit.webdav.observation.SubscriptionDiscovery}
     * object for the given resource. Note, that the discovery object will be empty
     * if there are no subscriptions present.
* Note that all subscriptions present on the given resource are returned. * However, the subscription id will not be visible in order to avoid abuse * by clients not having registered the subscription originally. * * @param resource */ public SubscriptionDiscovery getSubscriptionDiscovery(ObservationResource resource) { Subscription[] subsForResource = subscriptions.getByPath(resource.getLocator()); return new SubscriptionDiscovery(subsForResource); } /** * Create a new Subscription or update an existing Subscription * and add it as eventlistener to the {@link javax.jcr.observation.ObservationManager}. * * @param info * @param subscriptionId * @param resource * @return Subscription that has been added to the {@link javax.jcr.observation.ObservationManager} * @throws DavException if the subscription fails */ public Subscription subscribe(SubscriptionInfo info, String subscriptionId, ObservationResource resource) throws DavException { Subscription subscription; if (subscriptionId == null) { // new subscription SubscriptionImpl newSubs = new SubscriptionImpl(info, resource); registerSubscription(newSubs, resource); // adjust references to this subscription subscriptions.put(newSubs.getSubscriptionId(), newSubs); resource.getSession().addReference(newSubs.getSubscriptionId()); subscription = newSubs; } else { // refresh/modify existing one SubscriptionImpl existing = validate(subscriptionId, resource); existing.setInfo(info); registerSubscription(existing, resource); subscription = new WrappedSubscription(existing); } return subscription; } /** * Register the event listener defined by the given subscription to the * repository's observation manager. * * @param subscription * @param resource * @throws DavException */ private void registerSubscription(SubscriptionImpl subscription, ObservationResource resource) throws DavException { try { Session session = getRepositorySession(resource); ObservationManager oMgr = session.getWorkspace().getObservationManager(); String itemPath = subscription.getLocator().getRepositoryPath(); oMgr.addEventListener(subscription, subscription.getJcrEventTypes(), itemPath, subscription.isDeep(), subscription.getUuidFilters(), subscription.getNodetypeNameFilters(), subscription.isNoLocal()); } catch (RepositoryException e) { log.error("Unable to register eventlistener: "+e.getMessage()); throw new JcrDavException(e); } } /** * Unsubscribe the Subscription with the given id and remove it * from the {@link javax.jcr.observation.ObservationManager} as well as * from the internal map. * * @param subscriptionId * @param resource * @throws DavException */ public void unsubscribe(String subscriptionId, ObservationResource resource) throws DavException { SubscriptionImpl subs = validate(subscriptionId, resource); unregisterSubscription(subs, resource); } /** * Remove the event listener defined by the specified subscription from * the repository's observation manager and clean up the references present * on the DavSession. * * @param subscription * @param resource * @throws DavException */ private void unregisterSubscription(SubscriptionImpl subscription, ObservationResource resource) throws DavException { try { Session session = getRepositorySession(resource); session.getWorkspace().getObservationManager().removeEventListener(subscription); String sId = subscription.getSubscriptionId(); // clean up any references subscriptions.remove(sId); resource.getSession().removeReference(sId); } catch (RepositoryException e) { log.error("Unable to remove eventlistener: "+e.getMessage()); throw new JcrDavException(e); } } /** * Retrieve all event bundles accumulated since for the subscription specified * by the given id. * * @param subscriptionId * @param timeout timeout in milliseconds * @param resource * @return object encapsulating the events. */ public EventDiscovery poll(String subscriptionId, long timeout, ObservationResource resource) throws DavException { SubscriptionImpl subs = validate(subscriptionId, resource); return subs.discoverEvents(timeout); } /** * Validate the given subscription id. The validation will fail under the following * conditions:
    *
  • The subscription with the given id does not exist,
  • *
  • DavResource path does not match the subscription id,
  • *
  • The subscription with the given id is already expired.
  • *
* * @param subscriptionId * @param resource * @return Subscription with the given id. * @throws DavException if an error occured while retrieving the Subscription */ private SubscriptionImpl validate(String subscriptionId, ObservationResource resource) throws DavException { SubscriptionImpl subs; if (subscriptions.contains(subscriptionId)) { subs = subscriptions.get(subscriptionId); if (!subs.isSubscribedToResource(resource)) { throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED, "Attempt to operate on subscription with invalid resource path."); } if (subs.isExpired()) { unregisterSubscription(subs, resource); throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED, "Attempt to operate on expired subscription."); } return subs; } else { throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED, "Attempt to modify or to poll for non-existing subscription."); } } /** * @param resource * @return JCR session */ private static Session getRepositorySession(ObservationResource resource) throws DavException { return JcrDavSession.getRepositorySession(resource.getSession()); } //---------------------------< TransactionListener >------------------------ /** * {@inheritDoc} */ public synchronized void beforeCommit(TransactionResource resource, String lockToken) { // suspend regular subscriptions during a commit List transactionListeners = new ArrayList(); for (Iterator it = subscriptions.iterator(); it.hasNext(); ) { SubscriptionImpl sub = it.next(); TransactionListener tl = sub.createTransactionListener(); tl.beforeCommit(resource, lockToken); transactionListeners.add(tl); } transactionListenerById.put(lockToken, transactionListeners); } /** * {@inheritDoc} */ public void afterCommit(TransactionResource resource, String lockToken, boolean success) { List transactionListeners = transactionListenerById.remove(lockToken); if (transactionListeners != null) { for (TransactionListener txListener : transactionListeners) { txListener.afterCommit(resource, lockToken, success); } } } //----------------------------------------------< private inner classes >--- /** * Private inner class wrapping around an Subscription as * present in the internal map. This allows to hide the subscription Id * from other sessions, that did create the subscription. */ private class WrappedSubscription implements Subscription { private final Subscription delegatee; private WrappedSubscription(Subscription subsc) { this.delegatee = subsc; } public String getSubscriptionId() { // always return null, since the subscription id must not be exposed // but to the client, that created the subscription. return null; } public Element toXml(Document document) { return delegatee.toXml(document); } } /** * Private inner class SubscriptionMap that allows for quick * access by resource path as well as by subscription id. */ private class SubscriptionMap { private HashMap subscriptions = new HashMap(); private HashMap> ids = new HashMap>(); private boolean contains(String subscriptionId) { return subscriptions.containsKey(subscriptionId); } private SubscriptionImpl get(String subscriptionId) { return subscriptions.get(subscriptionId); } private Iterator iterator() { return subscriptions.values().iterator(); } private void put(String subscriptionId, SubscriptionImpl subscription) { subscriptions.put(subscriptionId, subscription); DavResourceLocator key = subscription.getLocator(); Set idSet; if (ids.containsKey(key)) { idSet = ids.get(key); } else { idSet = new HashSet(); ids.put(key, idSet); } if (!idSet.contains(subscriptionId)) { idSet.add(subscriptionId); } } private void remove(String subscriptionId) { SubscriptionImpl sub = subscriptions.remove(subscriptionId); ids.get(sub.getLocator()).remove(subscriptionId); } private Subscription[] getByPath(DavResourceLocator locator) { Set idSet = ids.get(locator); if (idSet != null && !idSet.isEmpty()) { Subscription[] subsForResource = new Subscription[idSet.size()]; int i = 0; for (String id : idSet) { SubscriptionImpl s = subscriptions.get(id); subsForResource[i] = new WrappedSubscription(s); i++; } return subsForResource; } else { return new Subscription[0]; } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy