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

org.jflux.spec.messaging.ConnectionLifecycle Maven / Gradle / Ivy

There is a newer version: 0.2.5
Show newest version
/*
 * Copyright 2013 The Friendularity Project (www.friendularity.org).
 *
 * 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.jflux.spec.messaging;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.jms.Connection;
import javax.jms.JMSException;
import org.jflux.api.service.ServiceLifecycle;
import org.apache.qpid.client.AMQConnectionFactory;
import org.apache.qpid.url.URLSyntaxException;
import org.jflux.api.service.ServiceDependency;

/**
 * This lifecycle comprises the JFlux object registry interface for the
 * Connection object. JFlux will provide any dependencies that are necessary and 
 * inform this class as they change. JFlux will also provide this object to
 * others once its dependencies are fulfilled.
 * 
 * Lifecycles are intended to be stateless, simply providing meta-code for the 
 * object to gracefully handle changes in its environment.
 * 
 * @author Jason R. Eads 
 */
public class ConnectionLifecycle implements ServiceLifecycle {
    private final static Logger theLogger = Logger.getLogger(ConnectionLifecycle.class.getName());
    /**
     * This is the format string for Advanced Message Queuing Protocol (AMQP).
     * AMQP is a language agnostic implementation similar to Java Messaging
     * Service (JMS)
     *
     * This is used to make a connection to the QPID server.
     */
    private final static String theAMQPFormatString = "amqp://%s:%s@%s/%s?brokerlist='%s'";

    /**
     * This formats the address to properly extend the ampqURL.
     */
    private final static String theTCPAddressFormatString = "tcp://%s:%s";

    /**
     * This provides the classnames for use in JFlux.
     */
    private final static String[] theClassNameArray = new String[]{Connection.class.getName()};
    
    public final static String theConnectionSpec = "connectionSpec";
    /**
     * This provides the dependencies which JFlux will provide.
     */
    private final static ServiceDependency[] theDependencyArray = {
        new ServiceDependency(theConnectionSpec, ConnectionSpec.class.getName(), ServiceDependency.Cardinality.MANDATORY_UNARY, ServiceDependency.UpdateStrategy.STATIC, Collections.EMPTY_MAP)
    };
    
    /**
     * Message Strings for Logger.
     */
    private final static String theFailedToCreateFactoryErrorMessage = "AMQP URL failed to create AMQConnectionFactory.";
    private final static String theFailedToProduceOrStartErrorMessage = "AMQP URL failed to produce or start an AMQconnection.";
    private final static String theFailedToStopConnectionErrorMessage = "Failed to stop AMQP connection.";
    
    //Lifecycle Constructor - required for automated assembly
    public ConnectionLifecycle() {}
    
    /**
     * Informs JFlux of all the dependencies this Connection object requires to
     * provide its service. In this case, only the spec object.
     * 
     * @return the dependency spec list.
     */
    @Override
    public List getDependencySpecs() {
        return Arrays.asList(theDependencyArray);
    }

    /**
     * Builds up the object that provides the service.
     * 
     * @param dependencyMap A map of the dependencies provided.
     * @return The actual object that provides the service function.
     */
    @Override
    public Connection createService(Map dependencyMap) {
        
        // Collect the connection specification 
        ConnectionSpec myConnectionSpec = (ConnectionSpec)dependencyMap.get(theConnectionSpec);
        
        // The address extension to the url
        String address = String.format(theTCPAddressFormatString,
                myConnectionSpec.getIpAddress(),
                myConnectionSpec.getPort());

        // The URL used for QPID messaging.
        String amqpURL = String.format(theAMQPFormatString,
                myConnectionSpec.getUsername(),
                myConnectionSpec.getPassword(),
                myConnectionSpec.getClientName(),
                myConnectionSpec.getVirtualHost(),
                address);
        
        // Feed the URL into the connectionFactory 
        AMQConnectionFactory connectionFactory;
        try {
            connectionFactory = new AMQConnectionFactory(amqpURL);
        } catch (URLSyntaxException ex) {
            theLogger.log(Level.SEVERE, theFailedToCreateFactoryErrorMessage, ex);
            return null;
        }
        
        // Retrieve the connection from the factory, activate it
        Connection connection = null;
        try {
            connection = connectionFactory.createConnection();
            connection.start();
        }
        catch (JMSException ex) {
            theLogger.log(Level.SEVERE, theFailedToProduceOrStartErrorMessage, ex);
        }
        return connection;
    }

    /**
     * Ensures the object can gracefully handle a change in its dependencies.
     * The Connection has no dependencies, so is unchanged.
     * 
     * @param service The Connection object.
     * @param changeType What kind of change occurred. Defined in ServiceLifecycle.
     * @param dependencyName The name of the dependency.
     * @param dependency The dependency object.
     * @param availableDependencies Map of all available dependencies.
     * @return Returns the service.
     */
    @Override
    public Connection handleDependencyChange(Connection service, String changeType, String dependencyName, Object dependency, Map availableDependencies) {
        /**
         * If the spec is lost, tear down the service.
         */
        if( changeType.equals(ServiceLifecycle.PROP_DEPENDENCY_UNAVAILABLE) ) {
            this.disposeService(service, availableDependencies);
            return null;
        }
        /**
         * If the spec becomes available, build the connection.
         */
        else if( changeType.equals(ServiceLifecycle.PROP_DEPENDENCY_AVAILABLE) ) {
            return this.createService(availableDependencies);
        }
        /**
         * If the spec has changed, attempt to handle the change.
         * As the Connection object does not allow fine-grain changes, re-create it.
         */
        else {
            this.disposeService(service, availableDependencies);
            return this.createService(availableDependencies);
        }
    }

    /**
     * Gracefully tears down the Connection.
     * 
     * @param service the Connection object to be disposed
     * @param availableDependencies dependencies that are available for the object.
     */
    @Override
    public void disposeService(Connection service, Map availableDependencies) {
        try {
            if(service != null) service.stop();
        } catch (JMSException ex) {
            theLogger.log(Level.WARNING, theFailedToStopConnectionErrorMessage, ex);
        }
    }

    /**
     * Returns the classname used for JFlux.
     * 
     * @return Array of the relevant class names 
     */
    @Override
    public String[] getServiceClassNames() {
        return theClassNameArray;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy