Package org.apache.slide.webdav.event

Source Code of org.apache.slide.webdav.event.NotificationTrigger

/*
* $Header: /home/cvs/jakarta-slide/src/webdav/server/org/apache/slide/webdav/event/NotificationTrigger.java,v 1.13 2004/08/05 14:43:30 dflorey Exp $
* $Revision: 1.13 $
* $Date: 2004/08/05 14:43:30 $
*
* ====================================================================
*
* Copyright 2004 The Apache Software Foundation
*
* Licensed 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.slide.webdav.event;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import org.apache.commons.httpclient.HttpConnection;
import org.apache.commons.httpclient.HttpState;
import org.apache.slide.common.Domain;
import org.apache.slide.event.ContentEvent;
import org.apache.slide.event.EventCollection;
import org.apache.slide.event.EventCollectionFilter;
import org.apache.slide.event.EventCollectionListener;
import org.apache.slide.event.ResourceEvent;
import org.apache.slide.event.VetoException;
import org.apache.slide.util.conf.Configurable;
import org.apache.slide.util.conf.Configuration;
import org.apache.slide.util.conf.ConfigurationException;
import org.apache.slide.util.logger.Logger;
import org.apache.slide.webdav.util.NotificationConstants;
import org.xml.sax.InputSource;
import org.xml.sax.helpers.AttributesImpl;

import de.zeigermann.xml.XMLEncode;
import de.zeigermann.xml.XMLOutputStreamWriter;
import de.zeigermann.xml.XMLWriter;
import de.zeigermann.xml.simpleImporter.ConversionHelpers;
import de.zeigermann.xml.simpleImporter.DefaultSimpleImportHandler;
import de.zeigermann.xml.simpleImporter.SimpleImporter;
import de.zeigermann.xml.simpleImporter.SimplePath;

/**
* @version $Revision: 1.13 $
*/
public class NotificationTrigger implements NotificationConstants, EventCollectionListener, Configurable {
    protected static final String LOG_CHANNEL = NotificationTrigger.class.getName();
    private final static String A_INCLUDE_EVENTS = "include-events";
    private final static String A_FILENAME = "filename";

    private final static String TCP_PROTOCOL = "http://";
    private final static String UDP_PROTOCOL = "httpu://";

    private final static String E_SUBSCRIPTIONS = "subscriptions";
    private final static String E_SUBSCRIPTION = "subscription";
    private final static String A_ID = "id";
    private final static String E_URI = "uri";
    private final static String E_DEPTH = "depth";
    private final static String E_NOTIFICATION_DELAY = "notification-delay";
    private final static String E_NOTIFICATION_TYPE = "notification-type";
    private final static String E_CALLBACK = "callback";
    private final static String E_SUBSCRIPTION_END = "subscription-end";
   
    protected static final Timer timer = new Timer();

    protected List subscribers = new ArrayList();
    protected int subscriberId = 0;
    protected boolean includeEvents = false;
    protected DatagramSocket socket;
    protected String filename = null;

    private static NotificationTrigger notificationTrigger = new NotificationTrigger();

    private NotificationTrigger() {
        Domain.log("Creating notification trigger", LOG_CHANNEL, Logger.INFO);
        try {
            socket = new DatagramSocket();
        } catch ( SocketException exception ) {
            Domain.log("Server socket creation failed, no UDP notifications available", LOG_CHANNEL, Logger.ERROR);
            socket = null;
        }
    }

    public static NotificationTrigger getInstance() {
        return notificationTrigger;
    }

    public int addSubscriber(Subscriber subscriber) {
        Domain.log("Adding subscriber", LOG_CHANNEL, Logger.INFO);
        subscriberId++;
        subscriber.setId(subscriberId);
        subscribers.add(subscriber);
        refreshSubscriber(subscriber, true);
        return subscriberId;
    }

    public boolean removeSubscriber(Subscriber subscriber) {
       Domain.log("Removing subscriber with ID: "+subscriber.getId(), LOG_CHANNEL, Logger.INFO);
       subscriber.getLifetime().cancel();
       saveSubscribers();
       return subscribers.remove(subscriber);
    }

    public void refreshSubscriber(final Subscriber subscriber, boolean persist) {
        TimerTask lifetimeTask = subscriber.getLifetime();
        if ( lifetimeTask != null ) lifetimeTask.cancel();
        if ( subscriber.getSubscriptionLifetime() > 0 ) {
            Domain.log("Refreshing subscriber with ID: "+subscriber.getId(), LOG_CHANNEL, Logger.INFO);
            TimerTask lifetime = new TimerTask() {
                public void run() {
                    Domain.log("Removing subscriber with ID: "+subscriber.getId(), LOG_CHANNEL, Logger.INFO);
                    refreshSubscriber(subscriber, true);
                }
            };
            subscriber.setLifetime(lifetime);
            timer.schedule(lifetime, subscriber.getSubscriptionLifetime()*1000);
        }
        if ( persist ) saveSubscribers();
    }

    public List getSubscribers() {
        return subscribers;
    }

    public Subscriber getSubscriber(int id) {
        for ( Iterator i = subscribers.iterator(); i.hasNext(); ) {
            Subscriber subsciber = (Subscriber)i.next();
            if ( subsciber.getId() == id ) {
                return subsciber;
            }
        }
        return null;
    }

    public void vetoableCollected(EventCollection collection) throws VetoException {
    }

    public void collected(EventCollection collection) {
        notifySubscribers(collection);
    }

    private void notifySubscribers(EventCollection collection) {
        Map subscriberEnumerations = new HashMap();
        List matchingSubscribers = new ArrayList();

        // get subscribers with matching notification types
        // (and remember events)
        ContentEvent[] update = EventCollectionFilter.getChangedContents(collection);
        for ( int i = 0; i < update.length; i++ ) {
            matchingSubscribers.addAll(getSubscribers(Subscriber.UPDATE, update[i]));
        }
        ContentEvent[] create = EventCollectionFilter.getCreatedContents(collection);
        for ( int i = 0; i < create.length; i++ ) {
            matchingSubscribers.addAll(getSubscribers(Subscriber.NEW_MEMBER, create[i]));
        }
        ContentEvent[] delete = EventCollectionFilter.getRemovedContents(collection);
        for ( int i = 0; i < delete.length; i++ ) {
            matchingSubscribers.addAll(getSubscribers(Subscriber.DELETE, delete[i]));
        }
        // FIXME: Add methods for MOVE, and NEW_MAIL (??) to get full exchange notification compliance
       
        // notifiy subscribers
        for ( Iterator i = matchingSubscribers.iterator(); i.hasNext(); ) {
           final Subscriber subscriber = (Subscriber)i.next();
          
           // skip subscribers that has no callback (we can't notify them)
           if (!subscriber.hasCallback()) continue;
          
           if ( subscriber.getNotificationDelay() == 0 ) {
              // send notification without delay
              List idList = (List)subscriberEnumerations.get(subscriber.getCallback());
              if ( idList == null ) {
                 idList = new ArrayList();
                 subscriberEnumerations.put(subscriber.getCallback(), idList);
              }
              Integer subscriberId = new Integer(subscriber.getId());
              if ( !idList.contains(subscriberId) ) {
                 idList.add(subscriberId);
              }
           } else {
              // send delayed notification
              TimerTask notifyTask = subscriber.getNotify();
              if ( notifyTask == null ) {
                 Domain.log("Starting notification delay: "+subscriber.getNotificationDelay(),
                       LOG_CHANNEL, Logger.INFO);
                 notifyTask = new TimerTask() {
                    public void run() {
                       notifySubscriber(subscriber.getCallback(),
                             String.valueOf(subscriber.getId()));
                       subscriber.setNotify(null);
                    }
                 };
                 subscriber.setNotify(notifyTask);
                 timer.schedule(notifyTask, subscriber.getNotificationDelay()*1000);
              }
           }
        }
        for ( Iterator i = subscriberEnumerations.entrySet().iterator(); i.hasNext(); ) {
            Map.Entry entry = (Map.Entry)i.next();
            String callBack = (String)entry.getKey();
            List idList = (List)entry.getValue();
            StringBuffer subscriberBuffer = new StringBuffer(128);
            boolean firstSubscriber = true;
            for ( Iterator j = idList.iterator(); j.hasNext(); ) {
                Integer id = (Integer)j.next();
                if ( !firstSubscriber ) {
                    subscriberBuffer.append(", ");
                }
                firstSubscriber = false;
                subscriberBuffer.append(id);
            }
            if ( !firstSubscriber ) {
                notifySubscriber(callBack, subscriberBuffer.toString());
            }
        }
    }

    protected void notifySubscriber(String callback, String subscribers) {
        if ( callback.startsWith(TCP_PROTOCOL) ) {
            Domain.log("Notify subscribers with adress='"+callback+"' via TCP with id's "+subscribers, LOG_CHANNEL, Logger.INFO);
            NotifyMethod notifyMethod = new NotifyMethod(callback.toString());
            notifyMethod.addRequestHeader(H_SUBSCRIPTION_ID_RESPONSE, subscribers);
            try {
                URL url = new URL(callback);
                notifyMethod.execute(
                      new HttpState(), new HttpConnection(url.getHost(),
                            url.getPort()!=-1 ? url.getPort() : 80));
            } catch (IOException e) {
                Domain.log("Notification of subscriber '"+callback.toString()+"' failed!");
            }
        } else if ( callback.startsWith(UDP_PROTOCOL) && socket != null ) {
            Domain.log("Notify subscribers with adress='"+callback+"' via UDP with id's "+subscribers+"\n", LOG_CHANNEL, Logger.INFO);
            try {
                URL url = new URL(TCP_PROTOCOL+callback.substring(UDP_PROTOCOL.length()));
                String notification = "NOTIFY "+callback+" HTTP/1.1\nSubscription-id: "+subscribers;
                byte[] buf = notification.getBytes();
                InetAddress address = InetAddress.getByName(url.getHost());
                DatagramPacket packet = new DatagramPacket(
                      buf, buf.length, address, url.getPort()!=-1 ? url.getPort() : 80);
                socket.send(packet);
            } catch (IOException e) {
                Domain.log("Notification of subscriber '"+callback.toString()+"' failed!", LOG_CHANNEL, Logger.ERROR);
            }
        }
    }

    private List getSubscribers(String type, ResourceEvent event) {
        List matchingSubscribers = new ArrayList();
        for ( Iterator i = subscribers.iterator(); i.hasNext(); ) {
            Subscriber subscriber = (Subscriber)i.next();
            if ( subscriber.matches(type, event)) {
                matchingSubscribers.add(subscriber);
              // remember this event for later poll method call
              subscriber.addEvent(event);
            }
        }
        return matchingSubscribers;
    }

    public void configure(Configuration configuration) throws ConfigurationException {
        Configuration notification = configuration.getConfiguration("notification");
        includeEvents = notification.getAttributeAsBoolean(A_INCLUDE_EVENTS, false);
        Configuration persistSubscriptions = configuration.getConfiguration("persist-subscriptions");
        if  ( persistSubscriptions != null ) {
          filename = persistSubscriptions.getAttribute(A_FILENAME);
        }
        loadSubscribers();
    }
   
    private void loadSubscribers() {
      if ( filename != null ) {
        synchronized ( subscribers ) {
          File file = new File(filename);
          if ( file.exists() ) {
            try {
              FileInputStream inputStream = new FileInputStream(filename);
              SimpleImporter importer = new SimpleImporter();
              importer.addSimpleImportHandler(new DefaultSimpleImportHandler() {
                String callback, notificationType, uri;
                int depth, notificationDelay, subscriptionLifetime, id;
                List events = new ArrayList();
               
                public void startElement(SimplePath path, String name, AttributesImpl attributes, String leadingCDdata) {
                  if ( path.matches(E_SUBSCRIPTION) ) {
                    id = ConversionHelpers.getInt(attributes.getValue(A_ID));
                  } else if ( path.matches(E_URI) ) {
                    uri = leadingCDdata;
                  } else if ( path.matches(E_DEPTH) ) {
                    depth = Integer.valueOf(leadingCDdata).intValue();
                  } else if ( path.matches(E_CALLBACK) ) {
                    callback = leadingCDdata;
                  } else if ( path.matches(E_NOTIFICATION_DELAY) ) {
                    notificationDelay = Integer.valueOf(leadingCDdata).intValue();
                  } else if ( path.matches(E_NOTIFICATION_TYPE) ) {
                    notificationType = leadingCDdata;
                  } else if ( path.matches(E_SUBSCRIPTION_END) ) {
                    subscriptionLifetime = (int)(Long.valueOf(leadingCDdata).longValue() - System.currentTimeMillis());
                  }
                }
               
                public void endElement(SimplePath path, String name) {
                  if ( path.matches(E_SUBSCRIPTION) ) {
                    Subscriber subscriber = new Subscriber(uri, callback, notificationType, notificationDelay, subscriptionLifetime, depth);
                    subscribers.add(subscriber);
                    refreshSubscriber(subscriber, false);
                  }
                }
              });
              importer.parse(new InputSource(inputStream));
            } catch (Exception e) {
              Domain.log("Exception while restoring subscriptions. Skipping...");
            }       
          }
        }
      }
    }
   
    private void saveSubscribers() {
      if ( filename != null ) {
        synchronized ( subscribers ) {
          try {
            FileOutputStream outputStream = new FileOutputStream(filename);
            XMLOutputStreamWriter writer = new XMLOutputStreamWriter(outputStream);
            writer.writeXMLDeclaration();
            writer.writeStartTag(XMLWriter.createStartTag(E_SUBSCRIPTIONS));
            for ( Iterator i = subscribers.iterator(); i.hasNext(); ) {
              Subscriber subscriber = (Subscriber)i.next();
              writer.writeStartTag(XMLWriter.createStartTag(E_SUBSCRIPTION, new String[][] {
                  { A_ID, String.valueOf(subscriber.getId()) } }));
              writer.writeElementWithPCData(XMLWriter.createStartTag(E_URI), XMLEncode.xmlEncodeText(subscriber.getUri()), XMLWriter.createEndTag(E_URI));
              writer.writeElementWithPCData(XMLWriter.createStartTag(E_DEPTH), String.valueOf(subscriber.getDepth()), XMLWriter.createEndTag(E_DEPTH));
              writer.writeElementWithPCData(XMLWriter.createStartTag(E_CALLBACK), XMLEncode.xmlEncodeText(subscriber.getCallback()), XMLWriter.createEndTag(E_CALLBACK));
              writer.writeElementWithPCData(XMLWriter.createStartTag(E_NOTIFICATION_TYPE), XMLEncode.xmlEncodeText(subscriber.getNotificationType()), XMLWriter.createEndTag(E_NOTIFICATION_TYPE));
              writer.writeElementWithPCData(XMLWriter.createStartTag(E_NOTIFICATION_DELAY), String.valueOf(subscriber.getNotificationDelay()), XMLWriter.createEndTag(E_NOTIFICATION_DELAY));
              writer.writeElementWithPCData(XMLWriter.createStartTag(E_SUBSCRIPTION_END), String.valueOf(subscriber.getSubscriptionEnd()), XMLWriter.createEndTag(E_SUBSCRIPTION_END));
              writer.writeEndTag(XMLWriter.createEndTag(E_SUBSCRIPTION));
            }
            writer.writeEndTag(XMLWriter.createEndTag(E_SUBSCRIPTIONS));
            outputStream.close();
          } catch ( Exception e) {
            Domain.log(e);
          }
        }
      }
    }
}
TOP

Related Classes of org.apache.slide.webdav.event.NotificationTrigger

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.