org.glite.ce.monitor.holder.NotificationHolder.java Source code

Java tutorial

Introduction

Here is the source code for org.glite.ce.monitor.holder.NotificationHolder.java

Source

/*
 * Copyright (c) Members of the EGEE Collaboration. 2004. 
 * See http://www.eu-egee.org/partners/ for details on the copyright
 * holders.  
 *
 * 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.glite.ce.monitor.holder;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Properties;

import javax.naming.NamingException;

import org.apache.axis2.databinding.types.URI;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.log4j.Logger;
import org.glite.ce.monitor.configuration.CEMonServiceConfig;
import org.glite.ce.monitor.registry.SubscriptionRegistry;
import org.glite.ce.monitorapij.queryprocessor.QueryProcessor;
import org.glite.ce.monitorapij.queryprocessor.QueryResult;
import org.glite.ce.monitorapij.resource.types.Action;
import org.glite.ce.monitorapij.resource.types.Dialect;
import org.glite.ce.monitorapij.resource.types.Parameter;
import org.glite.ce.monitorapij.resource.types.Policy;
import org.glite.ce.monitorapij.resource.types.Query;
import org.glite.ce.monitorapij.resource.types.SubscriptionPersistent;
import org.glite.ce.monitorapij.resource.types.Topic;
import org.glite.ce.monitorapij.sensor.SensorEvent;
import org.glite.ce.monitorapij.sensor.SensorOutputDataFormat;
import org.glite.ce.monitorapij.types.Event;
import org.glite.ce.monitorapij.ws.CEMonitorConsumerStub;

import eu.emi.security.canl.axis2.CANLAXIS2SocketFactory;

/**
 * This classed is used to take care of all aspects relating notifications. It
 * uses a set of classes to process a query request for sensor events, apply
 * predefined dialects and actions to these events, and hold a list of listeners
 * subscribed for notifications. Finally it submits the opportunely formatted
 * notifications.
 */
public final class NotificationHolder implements Runnable {

    private final static Logger logger = Logger.getLogger(NotificationHolder.class.getName());

    private final static int EVN_PAGE_SIZE = 100;

    private final static String CREDENTIALS_PROXY_FILE = "proxy";

    private final static String CREDENTIALS_CERT_FILE = "cert";

    private final static String CREDENTIALS_KEY_FILE = "key";

    private final static String CREDENTIALS_KEY_PASSWD = "password";

    private final static String CA_LOCATION = "truststore";

    private final static String CRL_MODE = "crlcheckingmode";

    private final static String CACRL_REFRESH = "updateinterval";

    private String name;

    private TopicHolder topicHolder;

    private int rate = 0;

    private QueryProcessorHolder queryProcessorHolder;

    private SubscriptionRegistry subscriptionRegistry;

    private boolean exit = false;

    private Thread notificationThread;

    private HashMap<String, SubscriptionPersistent> subscrMap;

    private NotificationPool notificationPool;

    /**
     * Creates a new NotificationHolder object specifying the rate and the
     * <code>TopicHolder</code>.
     * 
     * @param name
     *            The name of this holder
     * @param rate
     *            The subscription rate.
     * @param topicHolder
     *            The <code>TopicHolder</code>.
     */
    public NotificationHolder(String name, int rate, TopicHolder topicHolder,
            QueryProcessorHolder queryProcessorHolder, SubscriptionRegistry subscriptionRegistry,
            NotificationPool pool) throws IllegalArgumentException {

        super();

        if (name == null) {
            throw new IllegalArgumentException("name not specified!");
        }

        this.name = name;

        if (rate <= 0) {
            throw new IllegalArgumentException("rate must be > 0!");
        }

        if (topicHolder == null) {
            throw new IllegalArgumentException("topicHolder not specified!");
        }

        if (queryProcessorHolder == null) {
            throw new IllegalArgumentException("queryProcessorHolder not specified!");
        }

        if (subscriptionRegistry == null) {
            throw new IllegalArgumentException("subscriptionRegistry not specified!");
        }

        this.rate = rate;
        this.topicHolder = topicHolder;
        this.queryProcessorHolder = queryProcessorHolder;
        this.subscriptionRegistry = subscriptionRegistry;
        this.notificationPool = pool;

        subscrMap = new HashMap<String, SubscriptionPersistent>();

        Protocol.registerProtocol("https", new Protocol("https", new CANLAXIS2SocketFactory(), 8443));

        notificationThread = new Thread(this);
        notificationThread.setName(name);
        notificationThread.start();
    }

    public String getName() {
        return name;
    }

    /**
     * Get the number of registered subscriptions.
     * 
     * @return The size of the subscription map.
     */
    public synchronized int getSubscriptionListSize() {
        return subscrMap.size();
    }

    /**
     * Add a <code>SubscriptionPersistent</code> to the list of the registered
     * subscriptions.
     * 
     * @param subscription
     *            The <code>SubscriptionPersistent</code> to add.
     */
    public synchronized void addSubscription(SubscriptionPersistent subscription) {
        if (subscription != null) {
            /*
             * TODO missing clone
             */
            subscrMap.put(subscription.getId(), subscription);
        }
    }

    /**
     * Remove a <code>SubscriptionPersistent</code> from the list of the
     * registered subscriptions.
     * 
     * @param subscription
     *            The <code>SubscriptionPersistent</code> to remove.
     */
    public synchronized void removeSubscription(SubscriptionPersistent subscription) {
        if (subscription != null) {
            subscrMap.remove(subscription.getId());
        }
    }

    public synchronized List<SubscriptionPersistent> getSubscriptions() {
        return new ArrayList<SubscriptionPersistent>(subscrMap.values());
    }

    private void processPolicy(SensorEventArrayList eventList, Policy policy,
            CEMonitorConsumerStub.Notification notification, SubscriptionPersistent subscription) throws Exception {

        Query query = policy.getQuery();
        QueryProcessor qp = null;

        Action[] actions = policy.getAction();
        if (actions == null) {
            throw (new Exception("policy.actions not found!"));
        }

        if (query != null) {

            qp = queryProcessorHolder.getQueryProcessor(query.getQueryLanguage());

            if (qp == null) {
                logger.error("QueryProcessor \"" + query.getQueryLanguage() + "\" not found!");
                throw (new Exception("QueryProcessor \"" + query.getQueryLanguage() + "\" not found!"));
            }
        }

        Dialect[] dialectArray = subscription.getTopic().getDialect();
        String dataFormat = "default";

        if ((dialectArray != null) && dialectArray.length > 0 && dialectArray[0].getName() != null) {
            dataFormat = dialectArray[0].getName();
        }

        ArrayList<Event> notificationEventList = new ArrayList<Event>(0);
        ArrayList<QueryResult> queryResultList = new ArrayList<QueryResult>(0);

        for (int i = 0; i < eventList.size(); i++) {
            SensorEvent event = eventList.get(i);

            if (event == null) {
                continue;
            }

            if (event.isExpired()) {

                try {
                    logger.debug("removing expired event [id=" + event.getID() + "] [name=" + event.getName()
                            + "] [expirationTime=" + event.getExpirationTime() + "]");
                    topicHolder.removeEvent(event);
                } catch (Throwable t) {
                    logger.error(
                            "cannot remove the event [id=" + event.getID() + "] [name=" + event.getName() + "]!",
                            t);
                }

                eventList.remove(i);

                continue;

            }

            SensorOutputDataFormat format = event.getSensorOutputDataFormatApplied();

            if (format == null || !format.getName().equals(dataFormat)) {
                event.applyFormat(dataFormat);
            }

            if (qp != null) {
                queryResultList.add(qp.evaluate(query, event));
            } else {
                QueryResult queryRes = new QueryResult(event.getMessage().length);
                for (int x = 0; x < queryRes.size(); x++) {
                    queryRes.setResult(x, true);
                }

                queryResultList.add(queryRes);
            }

            if (event.getMessage() != null && event.getMessage().length > 0) {
                Event tmpEvent = new Event();
                tmpEvent.setID(event.getID());
                tmpEvent.setTimestamp(event.getTimestamp());
                tmpEvent.setMessage(event.getMessage());
                tmpEvent.setProducer(event.getProducer());
                notificationEventList.add(tmpEvent);
            }
        }

        notificationEventList.trimToSize();
        CEMonitorConsumerStub.Event[] eventArray = new CEMonitorConsumerStub.Event[notificationEventList.size()];
        for (int k = 0; k < eventArray.length; k++) {
            Event srcEvn = notificationEventList.get(k);
            CEMonitorConsumerStub.Event dstEvn = new CEMonitorConsumerStub.Event();
            dstEvn.setID(srcEvn.getID());
            dstEvn.setMessage(srcEvn.getMessage());
            dstEvn.setProducer(srcEvn.getProducer());
            dstEvn.setTimestamp(srcEvn.getTimestamp());
            eventArray[k] = dstEvn;
        }
        notificationEventList.clear();

        notification.setEvent(eventArray);

        QueryResult[] queryResult = new QueryResult[notificationEventList.size()];
        queryResult = (QueryResult[]) queryResultList.toArray(queryResult);

        for (int i = 0; i < actions.length; i++) {
            try {

                Action action = new Action();
                action.setCreationTime(actions[i].getCreationTime());
                action.setDoActionWhenQueryIs(actions[i].isDoActionWhenQueryIs());
                action.setId(actions[i].getId());
                action.setJarPath(actions[i].getJarPath());
                action.setName(actions[i].getName());
                action.setType(actions[i].getType());
                action.setParameter(actions[i].getParameter());
                action.setProperty(actions[i].getProperty());
                action.addParameter(new Parameter("notification", notification));
                action.addParameter(new Parameter("queryResult", queryResult));
                action.addParameter(new Parameter("subscriptionId", subscription.getId()));
                action.execute();

            } catch (Exception ex) {
                logger.error(ex.getMessage(), ex);
            }
        }
    }

    /**
     * This method is used by a scheduled execution of this class.The operations
     * performed are:<br>
     * 1) For each registered subscription a control is made to verify if this
     * is expired. If this is the case the subscription is unregistered.<br>
     * 2) If the subscription is not expired or paused then the query is
     * processed on the event, then actions on notification are performed based
     * on the query processing results, and finally the consumer or purchaser
     * registered for that notification is notified. <br>
     */
    public void run() {

        while (!exit) {

            List<SubscriptionPersistent> subscriptionList = this.getSubscriptions();

            long tmpTS = System.currentTimeMillis();

            for (SubscriptionPersistent subscription : subscriptionList) {

                URI consumerURI = null;
                try {
                    java.net.URI tmpURI = subscription.getMonitorConsumerURL();
                    consumerURI = new URI(tmpURI.toString());
                } catch (URI.MalformedURIException mEx) {
                    logger.error(mEx.getMessage(), mEx);
                }

                if (consumerURI == null) {
                    eraseSubscription(subscription, "consumer URL not found!");
                    continue;
                }

                if (subscription.isExpired()) {
                    eraseSubscription(subscription, "the subscription is expired!");
                    continue;
                }

                if (subscription.isPaused()) {
                    continue;
                }

                try {
                    logger.debug("[name=" + this.name + "] - processing subscription id " + subscription.getId());

                    Topic topic = subscription.getTopic();

                    Policy policy = subscription.getPolicy();
                    if (policy == null) {
                        continue;
                    }
                    SensorEventArrayList event = topicHolder.getEvents(topic.getName(),
                            subscription.getSubscriberId(), subscription.getSubscriberGroup());

                    logger.debug("[name=" + this.name + "] - found " + event.size() + " events");

                    CEMonitorConsumerStub.Notification notification = new CEMonitorConsumerStub.Notification();
                    notification.setExpirationTime(subscription.getExpirationTime());
                    CEMonitorConsumerStub.Topic newTopic = new CEMonitorConsumerStub.Topic();
                    newTopic.setName(topic.getName());
                    if (topic.getDialect() != null && topic.getDialect().length > 0) {
                        Dialect[] dialect = topic.getDialect();
                        CEMonitorConsumerStub.Dialect[] newDialect = new CEMonitorConsumerStub.Dialect[dialect.length];

                        for (int x = 0; x < dialect.length; x++) {
                            newDialect[x] = new CEMonitorConsumerStub.Dialect();
                            newDialect[x].setName(dialect[x].getName());
                            newDialect[x].setQueryLanguage(dialect[x].getQueryLanguage());
                        }
                        newTopic.setDialect(newDialect);
                    }
                    notification.setTopic(newTopic);
                    notification.setEvent(null);
                    notification.setConsumerURL(consumerURI);

                    while (event.size() > 0) {

                        SensorEventArrayList subEventList = event.drain(EVN_PAGE_SIZE);

                        processPolicy(subEventList, policy, notification, subscription);

                        if (notification.getEvent().length > 0) {
                            logger.info("[name=" + this.name + "] - sending notification (containing "
                                    + notification.getEvent().length + " events) to " + consumerURI.toString());

                            if (logger.isDebugEnabled()) {
                                printNotification(subscription, subEventList);
                            }

                            Properties sslConfig = this.getSSLParameters(subscription.getCredentialFile(),
                                    subscription.getPassphrase());

                            CANLAXIS2SocketFactory.setCurrentProperties(sslConfig);
                            CEMonitorConsumerStub consumer = new CEMonitorConsumerStub(consumerURI.toString());
                            CEMonitorConsumerStub.Notify msg = new CEMonitorConsumerStub.Notify();
                            msg.setNotification(notification);
                            consumer.notify(msg);

                            logger.info("[name=" + this.name + "] - [done]");
                        } else {
                            logger.info("[name=" + this.name
                                    + "] - the notification doesn't contains messages to be notified! [aborted]");
                        }

                        if (subscription.getMaxRetryCount() != -1) {
                            subscription.resetRetryCount();

                            try {

                                this.removeSubscription(subscription);
                                this.subscriptionRegistry.update(subscription);
                                this.addSubscription(subscription);

                            } catch (Throwable th) {
                                logger.error(th.getMessage(), th);
                            }
                        }

                    }

                } catch (NamingException e) {
                    logger.error("[name=" + this.name + "] - NamingException occurred: subscription id = "
                            + subscription.getId() + " consumer URL = " + subscription.getMonitorConsumerURL()
                            + " message error = " + e.getMessage());
                } catch (IOException e) {
                    logger.error("[name=" + this.name + "] - IOException occurred: subscription id = "
                            + subscription.getId() + " consumer URL = " + subscription.getMonitorConsumerURL()
                            + " message error = " + e.getMessage());

                    if (subscription.getMaxRetryCount() != -1) {
                        int retries = subscription.decrRetryCount();

                        logger.info("[name=" + this.name + "] - decrementing the retry count to " + retries
                                + " for the subscription id = " + subscription.getId() + "; consumer URL = "
                                + subscription.getMonitorConsumerURL() + "; message error = " + e.getMessage());

                        if (retries == 0) {

                            eraseSubscription(subscription, "retry count = 0");

                        } else {

                            try {
                                this.removeSubscription(subscription);
                                this.subscriptionRegistry.update(subscription);
                                this.addSubscription(subscription);

                            } catch (Throwable th) {
                                logger.error(th.getMessage(), th);
                            }
                        }
                    }
                } catch (Exception e) {
                    logger.error("[name=" + this.name + "] - Exception catched: subscriptionId = "
                            + subscription.getId() + " consumer URL = " + subscription.getMonitorConsumerURL()
                            + " message error = " + e.getMessage() + ". Follow the Stack trace:", e);
                }

            }

            try {
                long waitTime = rate - System.currentTimeMillis() + tmpTS;

                if (waitTime > 0) {
                    logger.debug("[name=" + this.name + "] - sleeping " + waitTime);
                    Thread.sleep(waitTime);
                } else {
                    logger.warn("Notifications overlapping");
                }
            } catch (InterruptedException intEx) {
                logger.info("[name=" + this.name + "] - destroyed");
            }
        }

    }

    private void eraseSubscription(SubscriptionPersistent subscription, String msg) {
        logger.info("[name=" + this.name + "] - unregistering subscription " + subscription.getId() + " [reason: "
                + msg + "]");
        this.removeSubscription(subscription);
        try {
            this.subscriptionRegistry.unregister(subscription);
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
        }
        notificationPool.check();
    }

    private void printNotification(SubscriptionPersistent subscription, SensorEventArrayList eventList) {

        StringBuffer sb = new StringBuffer();
        sb.append("\n******* NOTIFICATION *******");
        sb.append("\nsubscriptionId: " + subscription.getId());
        sb.append("\nuser: " + subscription.getSubscriberId());
        sb.append("\nconsumer url: " + subscription.getMonitorConsumerURL());
        if (subscription.getTopic() != null && subscription.getTopic().getName() != null) {
            sb.append("\topic name: " + subscription.getTopic().getName());
        }

        if (eventList == null) {
            sb.append("\nNONE EVENT FOUND!");
        } else {
            SensorEvent event = null;

            for (int i = 0; i < eventList.size(); i++) {
                event = (SensorEvent) eventList.get(i);

                sb.append("\n").append(i).append(") event_name: ").append(event.getName());

                if (event.getTimestamp() != null) {
                    sb.append(" - timestamp: ").append(event.getTimestamp().getTime());
                }
                if (event.getExpirationTime() != null) {
                    sb.append(" - expirationTime: ").append(event.getExpirationTime().getTime());
                }

                sb.append(" - messages: ");

                String[] msg = event.getMessage();
                if (msg == null) {
                    sb.append("0");
                } else {
                    sb.append(msg.length);
                }
            }
        }
        sb.append("\n******* END NOTIFICATION *******\n");
        logger.debug(sb.toString());
    }

    private Properties getSSLParameters(String credFile, String passphrase) {

        Properties sslConfig = new Properties();

        CEMonServiceConfig sConfiguration = CEMonServiceConfig.getConfiguration();
        if (sConfiguration == null) {
            throw new RuntimeException("Service is not configured");
        }

        if (credFile != null) {

            sslConfig.put(CREDENTIALS_PROXY_FILE, credFile);

        } else {

            String tmps = sConfiguration.getGlobalAttributeAsString("gridproxyfile");
            if (tmps != "") {

                sslConfig.put(CREDENTIALS_PROXY_FILE, tmps);

            } else {

                String certFilename = sConfiguration.getGlobalAttributeAsString("sslcertfile");
                String keyFilename = sConfiguration.getGlobalAttributeAsString("sslkeyfile");
                String passwd = sConfiguration.getGlobalAttributeAsString("sslkeypasswd");
                if (passwd == null)
                    passwd = "";

                if (certFilename == "" || keyFilename == "") {
                    throw new RuntimeException("Missing user credentials");
                } else {
                    sslConfig.put(CREDENTIALS_CERT_FILE, certFilename);
                    sslConfig.put(CREDENTIALS_KEY_FILE, keyFilename);
                }

                sslConfig.put(CREDENTIALS_KEY_PASSWD, passwd);

            }
        }

        if (passphrase != "") {
            sslConfig.put(CREDENTIALS_KEY_PASSWD, passphrase);
        }

        /*
         * TODO changes in configurations: removed protocol from subscriptions
         * (no SSLv3 available); replaced sslCAfiles with sslCALocation; removed
         * sslCRLfiles; add disableCRL and sslRefreshTime
         */

        String CALocation = sConfiguration.getGlobalAttributeAsString("sslCALocation");
        if (CALocation != "") {
            sslConfig.put(CA_LOCATION, CALocation);
        }

        String disableCRL = sConfiguration.getGlobalAttributeAsString("disableCRL");
        if (disableCRL.equalsIgnoreCase("true")) {
            sslConfig.put(CRL_MODE, "ignore");
        } else {
            sslConfig.put(CRL_MODE, "ifvalid");
        }

        String updateinterval = sConfiguration.getGlobalAttributeAsString("sslRefreshTime", "3600000");
        sslConfig.put(CACRL_REFRESH, updateinterval);

        return sslConfig;
    }

    public void destroy() {
        exit = true;
        notificationThread.interrupt();
    }
}