001    /*
002     * Cumulus4j - Securing your data in the cloud - http://cumulus4j.org
003     * Copyright (C) 2011 NightLabs Consulting GmbH
004     *
005     * This program is free software: you can redistribute it and/or modify
006     * it under the terms of the GNU Affero General Public License as
007     * published by the Free Software Foundation, either version 3 of the
008     * License, or (at your option) any later version.
009     *
010     * This program is distributed in the hope that it will be useful,
011     * but WITHOUT ANY WARRANTY; without even the implied warranty of
012     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013     * GNU Affero General Public License for more details.
014     *
015     * You should have received a copy of the GNU Affero General Public License
016     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017     */
018    package org.cumulus4j.store.crypto.keymanager.messagebroker;
019    
020    import org.cumulus4j.keymanager.back.shared.Message;
021    import org.cumulus4j.store.crypto.keymanager.messagebroker.inmemory.MessageBrokerInMemory;
022    import org.cumulus4j.store.crypto.keymanager.messagebroker.pmf.MessageBrokerPMF;
023    import org.slf4j.Logger;
024    import org.slf4j.LoggerFactory;
025    
026    /**
027     * JVM-singleton to access the {@link #getActiveMessageBroker() active message-broker}.
028     * It handles the registration and instantiation of {@link MessageBroker} implementations.
029     * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de
030     */
031    public class MessageBrokerRegistry
032    {
033            private static Logger logger = LoggerFactory.getLogger(MessageBrokerRegistry.class);
034    
035            private static MessageBrokerRegistry sharedInstance = new MessageBrokerRegistry();
036    
037            public static MessageBrokerRegistry sharedInstance() { return sharedInstance; }
038    
039            /**
040             * The system property configuring which message-broker-implementation is to be used.
041             * If it is not specified, a list of known implementations is tried out and the first one which
042             * could be instantiated successfully is used.
043             */
044            public static final String SYSTEM_PROPERTY_ACTIVE_MESSAGE_BROKER = "cumulus4j.MessageBrokerRegistry.activeMessageBroker";
045    
046            private static final Class<?>[] MESSAGE_BROKER_IMPLEMENTATION_CLASSES = {
047                    MessageBrokerPMF.class,
048                    MessageBrokerInMemory.class
049            };
050    
051            private volatile MessageBroker activeMessageBroker;
052    
053            /**
054             * Get the active {@link MessageBroker}. All {@link Message}s are transmitted over this active instance.
055             * If there is no active <code>MessageBroker</code>, yet, this method will
056             * check the system property {@value #SYSTEM_PROPERTY_ACTIVE_MESSAGE_BROKER} and either instantiate the
057             * class configured there or iterate a list of known <code>MessageBroker</code>-implementation-classes.
058             * @return the active <code>MessageBroker</code>; never <code>null</code>. If no active message-broker is set
059             * and none can be instantiated, an exception is thrown.
060             */
061            public MessageBroker getActiveMessageBroker()
062            {
063                    MessageBroker result = activeMessageBroker;
064                    if (result == null) {
065                            synchronized (this) { // correct & fast double-checked-locking with both 'volatile' and local variable 'result'.
066                                    result = activeMessageBroker;
067                                    if (result == null) {
068                                            String messageBrokerImplClassName = System.getProperty(SYSTEM_PROPERTY_ACTIVE_MESSAGE_BROKER);
069                                            if (messageBrokerImplClassName == null || messageBrokerImplClassName.trim().isEmpty()) {
070                                                    logger.info("getActiveMessageBroker: System property '{}' was not specified. Auto-detecting appropriate MessageBroker-implementation.", SYSTEM_PROPERTY_ACTIVE_MESSAGE_BROKER);
071    
072                                                    for (Class<?> c : MESSAGE_BROKER_IMPLEMENTATION_CLASSES) {
073                                                            try {
074                                                                    MessageBroker mb = (MessageBroker) c.newInstance();
075                                                                    result = mb;
076                                                                    break;
077                                                            } catch (Exception e) {
078                                                                    logger.warn("getActiveMessageBroker: Could not instantiate " + c.getName() + ": " + e, e);
079                                                            }
080                                                    }
081    
082                                                    if (result == null)
083                                                            throw new IllegalStateException("None of the available MessageBroker implementations could be successfully instantiated!");
084                                            }
085                                            else {
086                                                    try {
087                                                            Class<?> messageBrokerImplClass = Class.forName(messageBrokerImplClassName);
088                                                            result = (MessageBroker) messageBrokerImplClass.newInstance();
089                                                    } catch (ClassNotFoundException e) {
090                                                            throw new RuntimeException(e);
091                                                    } catch (InstantiationException e) {
092                                                            throw new RuntimeException(e);
093                                                    } catch (IllegalAccessException e) {
094                                                            throw new RuntimeException(e);
095                                                    }
096                                            }
097    
098                                            activeMessageBroker = result;
099                                            logger.info("getActiveMessageBroker: New activeMessageBroker={}", result);
100                                    }
101                            }
102                    }
103    
104                    return result;
105            }
106    
107            /**
108             * Set the active {@link MessageBroker}. Whatever is passed here will be returned by {@link #getActiveMessageBroker()}
109             * except for <code>null</code>. Setting <code>null</code> will cause {@link #getActiveMessageBroker()} to perform
110             * a re-initialisation (i.e. instantiate a <code>MessageBroker</code> as needed).
111             * @param messageBroker the {@link MessageBroker} instance to be set or <code>null</code> to clear the current one.
112             */
113            public void setActiveMessageBroker(MessageBroker messageBroker)
114            {
115                    MessageBroker amb = this.activeMessageBroker;
116                    if (amb != null && amb != messageBroker) {
117                            Exception x = new IllegalStateException("An active MessageBroker already exists! Changing the active MessageBroker now is highly discouraged as it may cause errors!");
118                            logger.warn("setActiveMessageBroker: " + x, x);
119                    }
120    
121                    this.activeMessageBroker = messageBroker;
122                    logger.info("setActiveMessageBroker: New activeMessageBroker={}", messageBroker);
123            }
124    }