org.ow2.play.service.registry.mongo.RegistryImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.ow2.play.service.registry.mongo.RegistryImpl.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.service.registry.mongo;

import java.net.URL;
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 org.ow2.play.service.registry.api.Entry;
import org.ow2.play.service.registry.api.Registry;
import org.ow2.play.service.registry.api.RegistryException;

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;

/**
 * Stores and read data from the serviceregistry Mongo collection.
 * 
 * @author chamerling
 * 
 */
public class RegistryImpl implements Registry {

    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 = "serviceregistry";

    private final static String URL_KEY = "url";
    private final static String NAME_KEY = "name";

    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(RegistryImpl.class.getName());

    /*
     * (non-Javadoc)
     * 
     * @see org.ow2.play.service.registry.api.Registry#init()
     */
    @Override
    @WebMethod
    public void init() throws RegistryException {
        logger.info("Initializing registry service");

        if (initialized) {
            logger.info("Already initialized");
            return;
        }

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

        if (properties != null) {
            logger.fine("Getting properties from " + properties);
            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);
        logger.fine("Got server addresses " + addresses);
        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;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.ow2.play.service.registry.api.Registry#get(java.lang.String)
     */
    @Override
    @WebMethod
    public String get(String name) throws RegistryException {
        String url = null;

        if (logger.isLoggable(Level.FINE)) {
            logger.fine(String.format("Get url for name %s", name));
        }
        checkInitialized();

        DBObject filter = new BasicDBObject();
        filter.put(NAME_KEY, name);

        DBObject result = collection.findOne(filter);
        if (result != null && result.get(URL_KEY) != null) {
            url = result.get(URL_KEY).toString();
        }
        return url;
    }

    /* (non-Javadoc)
     * @see org.ow2.play.service.registry.api.Registry#entries()
     */
    @Override
    @WebMethod
    public List<Entry> entries() throws RegistryException {
        List<Entry> result = new ArrayList<Entry>();
        for (String key : keys()) {
            Entry entry = new Entry();
            entry.key = key;
            entry.value = get(key);
            result.add(entry);
        }
        return result;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.ow2.play.service.registry.api.Registry#put(java.lang.String,
     * java.lang.String)
     */
    @Override
    @WebMethod
    public void put(String name, String url) throws RegistryException {
        if (logger.isLoggable(Level.FINE)) {
            logger.fine(String.format("Put url %s for name %s", url, name));
        }
        checkInitialized();

        if (name == null || url == null) {
            throw new RegistryException("Can not put null values name = %s, url = %s", name, url);
        }

        // update the entry if it already exists
        DBObject filter = new BasicDBObject();
        filter.put(NAME_KEY, name);

        DBObject filtered = collection.findOne(filter);
        if (filtered != null) {
            filtered.put(URL_KEY, url);
            collection.save(filtered);
        } else {
            DBObject o = new BasicDBObject();
            o.put(NAME_KEY, name);
            o.put(URL_KEY, url);
            collection.insert(o);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.ow2.play.service.registry.api.Registry#keys()
     */
    @Override
    @WebMethod
    public List<String> keys() throws RegistryException {
        if (logger.isLoggable(Level.FINE)) {
            logger.fine("Get keys");
        }

        checkInitialized();

        List<String> result = new ArrayList<String>();

        DBCursor cursor = collection.find();
        Iterator<DBObject> iter = cursor.iterator();
        while (iter.hasNext()) {
            DBObject dbObject = iter.next();
            if (dbObject != null && dbObject.get(NAME_KEY) != null) {
                result.add(dbObject.get(NAME_KEY).toString());
            }
        }

        return result;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.ow2.play.service.registry.api.Registry#clear()
     */
    @Override
    @WebMethod
    public void clear() throws RegistryException {
        if (logger.isLoggable(Level.FINE)) {
            logger.fine("Clear");
        }
        checkInitialized();
        clearCollection();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.ow2.play.service.registry.api.Registry#load(java.lang.String)
     */
    @Override
    @WebMethod
    public void load(String url) throws RegistryException {
        if (logger.isLoggable(Level.FINE)) {
            logger.fine(String.format("Load from url %s", url));
        }

        if (url == null) {
            throw new RegistryException("Can not load properties from null value");
        }

        // This is the only time we initialize if not already done...
        if (!initialized) {
            init();
        }

        Properties props = new Properties();
        try {
            URL u = new URL(url);
            props.load(u.openStream());

            for (Object key : props.keySet()) {
                String k = key.toString();
                Object v = props.getProperty(k);
                if (v != null) {
                    this.put(k, v.toString());
                }
            }

        } catch (Exception e) {
            logger.warning(e.getMessage());
            throw new RegistryException(e);
        }
    }

    /**
     * 
     */
    protected void checkInitialized() throws RegistryException {
        if (!initialized) {
            throw new RegistryException("Registry has not been initialized");
        }
    }

    /**
     * Set the connection properties
     * 
     * @param props
     */
    public void setProperties(Properties props) {
        this.properties = props;
    }

    /*
     * 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 or for unit test with some mock libs
     */
    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;
    }

    protected void clearCollection() {
        if (logger.isLoggable(Level.FINE)) {
            logger.fine("Remove all objects from the collection");
        }
        WriteResult wr = getDbCollection().remove(new BasicDBObject());

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