org.ow2.play.governance.storage.MongoSubscriptionRegistry.java Source code

Java tutorial

Introduction

Here is the source code for org.ow2.play.governance.storage.MongoSubscriptionRegistry.java

Source

/**
 *
 * Copyright (c) 2012, PetalsLink
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA 
 *
 */
package org.ow2.play.governance.storage;

import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.jws.WebMethod;
import javax.jws.WebParam;

import org.ow2.play.governance.api.SubscriptionRegistry;
import org.ow2.play.governance.api.bean.Subscription;
import org.ow2.play.governance.api.bean.Topic;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.Mongo;
import com.mongodb.ServerAddress;
import com.mongodb.WriteResult;

/**
 * @author chamerling
 * 
 */
public class MongoSubscriptionRegistry implements SubscriptionRegistry {

    private final static String DEFAULT_MONGO_DB_HOSTNAME = "localhost";
    private final static String DEFAULT_MONGO_DB_PORT = "27017";
    private final static String DEFAULT_MONGO_DB_DATABASE_NAME = "play";
    private final static String DEFAULT_MONGO_DB_COLLECTION_NAME = "subscription";

    private static final String ID_KEY = "id";
    private static final String SUBSCRIBER_KEY = "subscriber";
    private static final String PRODUCER_KEY = "producer";
    private static final String TOPIC_KEY = "topic";
    private static final String DATE_KEY = "date";
    private static final String STATUS_KEY = "status";

    private static final String TOPIC_NAME_KEY = "name";
    private static final String TOPIC_NS_KEY = "ns";
    private static final String TOPIC_PREFIX_KEY = "prefix";

    private String hostname = DEFAULT_MONGO_DB_HOSTNAME;
    private String port = DEFAULT_MONGO_DB_PORT;
    private String databaseName = DEFAULT_MONGO_DB_DATABASE_NAME;
    private String collectionName = DEFAULT_MONGO_DB_COLLECTION_NAME;
    private String userName;
    private String password;
    private Mongo mongo;
    private DBCollection collection;

    private Properties properties;

    private boolean initialized = false;

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

    /**
     * 
     */
    public MongoSubscriptionRegistry() {
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.ow2.play.metadata.api.service.Initializable#init(java.util.Properties
     * )
     */
    @WebMethod(exclude = true)
    public void init() {
        logger.info("Initializing metadata service");

        if (mongo != null) {
            close();
        }

        if (properties != null) {
            hostname = properties.getProperty("mongo.hostname", DEFAULT_MONGO_DB_HOSTNAME);
            port = properties.getProperty("mongo.port", DEFAULT_MONGO_DB_PORT);
            userName = properties.getProperty("mongo.username", userName);
            password = properties.getProperty("mongo.password", password);
            collectionName = properties.getProperty("mongo.collection", DEFAULT_MONGO_DB_COLLECTION_NAME);
        }

        if (logger.isLoggable(Level.INFO)) {
            logger.info(String.format("Connection to %s %s with credentials %s %s", hostname, port, userName,
                    "******"));
        }

        List<ServerAddress> addresses = getServerAddresses(hostname, port);
        mongo = getMongo(addresses);

        DB database = getDatabase(mongo, databaseName);

        if (userName != null && userName.trim().length() > 0) {
            if (!database.authenticate(userName, password.toCharArray())) {
                throw new RuntimeException("Unable to authenticate with MongoDB server.");
            }

            // Allow password to be GCed
            password = null;
        }

        setCollection(database.getCollection(collectionName));
        initialized = true;
    }

    protected void clearCollection() {
        if (logger.isLoggable(Level.WARNING)) {
            logger.warning("Going to clear collection : " + collectionName);
        }

        if (initialized) {
            WriteResult wr = getDbCollection().remove(new BasicDBObject());
            if (logger.isLoggable(Level.INFO)) {
                logger.info("Clear collection result :" + wr);
            }
        }
    }

    /*
     * This method could be overridden to provide the DB instance from an
     * existing connection.
     */
    protected DB getDatabase(Mongo mongo, String databaseName) {
        return mongo.getDB(databaseName);
    }

    protected DBCollection getDbCollection() {
        return this.collection;
    }

    /*
     * This method could be overridden to provide the Mongo instance from an
     * existing connection.
     */
    protected Mongo getMongo(List<ServerAddress> addresses) {
        if (addresses.size() == 1) {
            return new Mongo(addresses.get(0));
        } else {
            // Replica set
            return new Mongo(addresses);
        }
    }

    protected void close() {
        if (mongo != null) {
            collection = null;
            mongo.close();
        }
    }

    /**
     * Note: this method is primarily intended for use by the unit tests.
     * 
     * @param collection
     *            The MongoDB collection to use when logging events.
     */
    public void setCollection(final DBCollection collection) {
        assert collection != null : "collection must not be null";

        this.collection = collection;
    }

    /**
     * Returns a List of ServerAddress objects for each host specified in the
     * hostname property. Returns an empty list if configuration is detected to
     * be invalid, e.g.:
     * <ul>
     * <li>Port property doesn't contain either one port or one port per host</li>
     * <li>After parsing port property to integers, there isn't either one port
     * or one port per host</li>
     * </ul>
     * 
     * @param hostname
     *            Blank space delimited hostnames
     * @param port
     *            Blank space delimited ports. Must specify one port for all
     *            hosts or a port per host.
     * @return List of ServerAddresses to connect to
     */
    private List<ServerAddress> getServerAddresses(String hostname, String port) {
        List<ServerAddress> addresses = new ArrayList<ServerAddress>();

        String[] hosts = hostname.split(" ");
        String[] ports = port.split(" ");

        if (ports.length != 1 && ports.length != hosts.length) {
            // errorHandler
            // .error("MongoDB appender port property must contain one port or a port per host",
            // null, ErrorCode.ADDRESS_PARSE_FAILURE);
        } else {
            List<Integer> portNums = getPortNums(ports);
            // Validate number of ports again after parsing
            if (portNums.size() != 1 && portNums.size() != hosts.length) {
                // error("MongoDB appender port property must contain one port or a valid port per host",
                // null, ErrorCode.ADDRESS_PARSE_FAILURE);
            } else {
                boolean onePort = (portNums.size() == 1);

                int i = 0;
                for (String host : hosts) {
                    int portNum = (onePort) ? portNums.get(0) : portNums.get(i);
                    try {
                        addresses.add(new ServerAddress(host.trim(), portNum));
                    } catch (UnknownHostException e) {
                        // errorHandler
                        // .error("MongoDB appender hostname property contains unknown host",
                        // e, ErrorCode.ADDRESS_PARSE_FAILURE);
                    }
                    i++;
                }
            }
        }
        return addresses;
    }

    private List<Integer> getPortNums(String[] ports) {
        List<Integer> portNums = new ArrayList<Integer>();

        for (String port : ports) {
            try {
                Integer portNum = Integer.valueOf(port.trim());
                if (portNum < 0) {
                    // errorHandler
                    // .error("MongoDB appender port property can't contain a negative integer",
                    // null, ErrorCode.ADDRESS_PARSE_FAILURE);
                } else {
                    portNums.add(portNum);
                }
            } catch (NumberFormatException e) {
                // errorHandler
                // .error("MongoDB appender can't parse a port property value into an integer",
                // e, ErrorCode.ADDRESS_PARSE_FAILURE);
            }

        }

        return portNums;
    }

    /**
     * @param properties
     *            the properties to set
     */
    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.ow2.play.governance.api.SubscriptionRegistry#addSubscription(org.
     * ow2.play.governance.api.bean.Subscription)
     */
    @Override
    @WebMethod
    public void addSubscription(Subscription subscription) {
        if (logger.isLoggable(Level.FINE)) {
            logger.fine("Adding subscription : " + subscription);
        }

        if (subscription == null) {
            logger.warning("Can not add null subscription");
            return;
        }

        DBObject dbo = new BasicDBObject();
        dbo.put(ID_KEY, subscription.getId());
        dbo.put(PRODUCER_KEY, subscription.getProvider());
        dbo.put(SUBSCRIBER_KEY, subscription.getSubscriber());
        dbo.put(DATE_KEY, Long.toString(subscription.getDate()));
        dbo.put(STATUS_KEY, subscription.getStatus());

        if (subscription.getTopic() != null) {
            DBObject topic = new BasicDBObject();
            topic.put(TOPIC_NAME_KEY, subscription.getTopic().getName());
            topic.put(TOPIC_NS_KEY, subscription.getTopic().getNs());
            topic.put(TOPIC_PREFIX_KEY, subscription.getTopic().getPrefix());
            dbo.put(TOPIC_KEY, topic);
        } else {
            logger.warning("No topic set in the subscription, added to collection without it...");
        }

        if (logger.isLoggable(Level.FINE)) {
            logger.fine("Adding subscription object to collection : " + dbo);
        }
        getDbCollection().insert(dbo);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.ow2.play.governance.api.SubscriptionRegistry#getSubscriptions()
     */
    @Override
    @WebMethod
    public List<Subscription> getSubscriptions() {
        if (logger.isLoggable(Level.FINE)) {
            logger.fine("Getting subscritions");
        }

        DBCursor cursor = getDbCollection().find();
        Iterator<DBObject> iter = cursor.iterator();
        List<Subscription> result = new ArrayList<Subscription>();
        while (iter.hasNext()) {
            DBObject dbo = iter.next();
            if (logger.isLoggable(Level.FINE)) {
                logger.fine("Extracting subscription : " + dbo);
            }

            Subscription s = toSubscription(dbo);
            if (s != null) {
                result.add(toSubscription(dbo));
            }
        }
        return result;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.ow2.play.governance.api.SubscriptionRegistry#getSubscriptions(org
     * .ow2.play.governance.api.bean.Subscription)
     */
    @Override
    @WebMethod
    public List<Subscription> getSubscriptions(@WebParam(name = "filter") Subscription filter) {
        List<Subscription> result = null;

        if (filter == null) {
            return getSubscriptions();
        }
        result = new ArrayList<Subscription>();

        DBObject f = new BasicDBObject();
        if (filter.getDate() >= 0L) {
            f.put(DATE_KEY, Long.toString(filter.getDate()));
        }

        if (filter.getId() != null) {
            f.put(ID_KEY, filter.getId());
        }

        if (filter.getProvider() != null) {
            f.put(PRODUCER_KEY, filter.getProvider());
        }

        if (filter.getSubscriber() != null) {
            f.put(SUBSCRIBER_KEY, filter.getSubscriber());
        }

        if (filter.getStatus() != null) {
            f.put(STATUS_KEY, filter.getStatus());
        }

        // use the dot notation to search inner args
        if (filter.getTopic() != null) {
            if (filter.getTopic().getName() != null) {
                f.put(TOPIC_KEY + "." + TOPIC_NAME_KEY, filter.getTopic().getName());
            }
            if (filter.getTopic().getNs() != null) {
                f.put(TOPIC_KEY + "." + TOPIC_NS_KEY, filter.getTopic().getNs());
            }
            if (filter.getTopic().getPrefix() != null) {
                f.put(TOPIC_KEY + "." + TOPIC_PREFIX_KEY, filter.getTopic().getNs());
            }
        }

        logger.fine("Filtering with : " + f);

        DBCursor cursor = getDbCollection().find(f);
        Iterator<DBObject> iter = cursor.iterator();
        while (iter.hasNext()) {
            DBObject dbo = iter.next();
            if (logger.isLoggable(Level.FINE)) {
                logger.fine("Extracting subscription : " + dbo);
            }

            Subscription s = toSubscription(dbo);
            if (s != null) {
                result.add(toSubscription(dbo));
            }
        }

        return result;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.ow2.play.governance.api.SubscriptionRegistry#removeAll()
     */
    @Override
    @WebMethod
    public List<Subscription> removeAll() {
        if (logger.isLoggable(Level.FINE)) {
            logger.fine("Remove all the subscriptions from the collection");
        }
        WriteResult wr = getDbCollection().remove(new BasicDBObject());

        if (logger.isLoggable(Level.FINE)) {
            logger.fine("Write result : " + wr);
        }
        return new ArrayList<Subscription>();
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.ow2.play.governance.api.SubscriptionRegistry#remove(org.ow2.play.
     * governance.api.bean.Subscription)
     */
    @Override
    @WebMethod
    public boolean remove(Subscription subscription) {
        if (logger.isLoggable(Level.FINE)) {
            logger.fine("Removing subscription : " + subscription);
        }

        // remove from the ID
        if (subscription == null || subscription.getId() == null) {
            return false;
        }

        DBObject filter = new BasicDBObject();
        filter.put(ID_KEY, subscription.getId());

        WriteResult wr = getDbCollection().remove(filter);
        if (logger.isLoggable(Level.FINE)) {
            logger.fine("Remove result : " + wr);
        }
        return true;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.ow2.play.governance.api.SubscriptionRegistry#removeAllFromProvider
     * (java.lang.String)
     */
    @Override
    @WebMethod
    public List<Subscription> removeAllFromProvider(String provider) {
        if (logger.isLoggable(Level.FINE)) {
            logger.fine("Remove subscriptions from provider : " + provider);
        }

        List<Subscription> result = new ArrayList<Subscription>();
        // remove from the ID
        if (provider == null) {
            return result;
        }

        DBObject filter = new BasicDBObject();
        filter.put(PRODUCER_KEY, provider);

        WriteResult wr = getDbCollection().remove(filter);

        if (logger.isLoggable(Level.FINE)) {
            logger.fine("Remove result : " + wr);
        }
        return result;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.ow2.play.governance.api.SubscriptionRegistry#removeAllFromConsumer
     * (java.lang.String)
     */
    @Override
    @WebMethod
    public List<Subscription> removeAllFromConsumer(String consumer) {
        if (logger.isLoggable(Level.FINE)) {
            logger.fine("Remove all from consumer : " + consumer);
        }

        List<Subscription> result = new ArrayList<Subscription>();
        // remove from the ID
        if (consumer == null) {
            return result;
        }

        DBObject filter = new BasicDBObject();
        filter.put(SUBSCRIBER_KEY, consumer);

        WriteResult wr = getDbCollection().remove(filter);
        if (logger.isLoggable(Level.FINE)) {
            logger.fine("Remove result : " + wr);
        }

        return result;
    }

    protected Subscription toSubscription(DBObject dbo) {
        if (dbo == null) {
            return null;
        }
        Subscription s = new Subscription();
        s.setId(dbo.get(ID_KEY) == null ? "" : dbo.get(ID_KEY).toString());
        s.setSubscriber(dbo.get(SUBSCRIBER_KEY) == null ? "" : dbo.get(SUBSCRIBER_KEY).toString());
        s.setProvider(dbo.get(PRODUCER_KEY) == null ? "" : dbo.get(PRODUCER_KEY).toString());
        s.setDate(dbo.get(DATE_KEY) == null ? 0L : Long.parseLong(dbo.get(DATE_KEY).toString()));
        s.setStatus(dbo.get(STATUS_KEY) == null ? "" : dbo.get(STATUS_KEY).toString());

        if (dbo.get(TOPIC_KEY) != null && dbo.get(TOPIC_KEY) instanceof DBObject) {
            DBObject to = (DBObject) dbo.get(TOPIC_KEY);
            Topic t = new Topic();
            t.setName(to.get(TOPIC_NAME_KEY) == null ? "" : to.get(TOPIC_NAME_KEY).toString());
            t.setNs(to.get(TOPIC_NS_KEY) == null ? "" : to.get(TOPIC_NS_KEY).toString());
            t.setPrefix(to.get(TOPIC_PREFIX_KEY) == null ? "" : to.get(TOPIC_PREFIX_KEY).toString());
            s.setTopic(t);
        } else {
            // WARN
            if (logger.isLoggable(Level.FINE)) {
                logger.fine("Topic is null");
            }
        }

        return s;
    }

}