001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.activemq.osgi;
018
019import java.util.Collections;
020import java.util.Dictionary;
021import java.util.Enumeration;
022import java.util.HashMap;
023import java.util.Map;
024import java.util.Properties;
025
026import org.apache.activemq.broker.BrokerService;
027import org.apache.activemq.spring.SpringBrokerContext;
028import org.apache.activemq.spring.Utils;
029import org.apache.camel.blueprint.CamelContextFactoryBean;
030import org.osgi.framework.BundleContext;
031import org.osgi.framework.ServiceRegistration;
032import org.osgi.service.cm.ConfigurationException;
033import org.osgi.service.cm.ManagedServiceFactory;
034import org.slf4j.Logger;
035import org.slf4j.LoggerFactory;
036import org.springframework.beans.BeansException;
037import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
038import org.springframework.beans.factory.config.BeanPostProcessor;
039import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
040import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
041import org.springframework.context.support.ClassPathXmlApplicationContext;
042import org.springframework.core.io.Resource;
043
044public class ActiveMQServiceFactory implements ManagedServiceFactory {
045
046    private static final Logger LOG = LoggerFactory.getLogger(ActiveMQServiceFactory.class);
047
048    BundleContext bundleContext;
049    Map<String, BrokerService> brokers = new HashMap<>();
050    Map<String, ServiceRegistration<BrokerService>> brokerRegs = new HashMap<>();
051
052    @Override
053    public String getName() {
054        return "ActiveMQ Server Controller";
055    }
056
057    public Map<String, BrokerService> getBrokersMap() {
058        return Collections.unmodifiableMap(brokers);
059    }
060
061    @Override
062    synchronized public void updated(String pid, Dictionary<String, ?> properties) throws ConfigurationException {
063
064        // First stop currently running broker (if any)
065        deleted(pid);
066
067        String config = (String) properties.get("config");
068        if (config == null) {
069            throw new ConfigurationException("config", "Property must be set");
070        }
071        String name = (String) properties.get("broker-name");
072        if (name == null) {
073            throw new ConfigurationException("broker-name", "Property must be set");
074        }
075
076        LOG.info("Starting broker " + name);
077
078        try {
079            Thread.currentThread().setContextClassLoader(BrokerService.class.getClassLoader());
080            Resource resource = Utils.resourceFromString(config);
081
082            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
083                    new String[]{resource.getURL().toExternalForm()}, false);
084
085            if (isCamelContextFactoryBeanExist()) {
086
087                ctx.addBeanFactoryPostProcessor(new BeanFactoryPostProcessor() {
088
089                    @Override
090                    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
091
092                        beanFactory.addBeanPostProcessor(new BeanPostProcessor() {
093
094                            @Override
095                            public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
096                                if (bean instanceof CamelContextFactoryBean) {
097                                    ((CamelContextFactoryBean) bean).setBundleContext(bundleContext);
098                                }
099                                return bean;
100                            }
101
102                            @Override
103                            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
104                                return bean;
105                            }
106                        });
107                    }
108                });
109            }
110
111            // Handle properties in configuration
112            PropertyPlaceholderConfigurer configurator = new PropertyPlaceholderConfigurer();
113
114            // convert dictionary to properties. Is there a better way?
115            Properties props = new Properties();
116            Enumeration<?> elements = properties.keys();
117            while (elements.hasMoreElements()) {
118                Object key = elements.nextElement();
119                props.put(key, properties.get(key));
120            }
121
122            configurator.setProperties(props);
123            configurator.setIgnoreUnresolvablePlaceholders(true);
124
125            ctx.addBeanFactoryPostProcessor(configurator);
126
127            ctx.refresh();
128
129            // Start the broker
130            BrokerService broker = ctx.getBean(BrokerService.class);
131            if (broker == null) {
132                throw new ConfigurationException(null, "Broker not defined");
133            }
134            // TODO deal with multiple brokers
135
136            SpringBrokerContext brokerContext = new SpringBrokerContext();
137            brokerContext.setConfigurationUrl(resource.getURL().toExternalForm());
138            brokerContext.setApplicationContext(ctx);
139            broker.setBrokerContext(brokerContext);
140
141            broker.setStartAsync(true);
142            broker.start();
143
144            if (!broker.isSlave())
145                broker.waitUntilStarted();
146            brokers.put(pid, broker);
147            brokerRegs.put(pid, bundleContext.registerService(BrokerService.class, broker, properties));
148        } catch (Exception e) {
149            throw new ConfigurationException(null, "Cannot start the broker", e);
150        }
151    }
152
153    private boolean isCamelContextFactoryBeanExist() {
154        try {
155            Class.forName("org.apache.camel.osgi.CamelContextFactoryBean");
156            return true;
157        } catch (ClassNotFoundException e) {
158            return false;
159        }
160    }
161
162    @Override
163    synchronized public void deleted(String pid) {
164        ServiceRegistration<BrokerService> reg = brokerRegs.remove(pid);
165        if (reg != null) {
166            reg.unregister();
167        }
168        BrokerService broker = brokers.remove(pid);
169        if (broker != null) {
170            stop(pid, broker);
171        }
172    }
173
174        private void stop(String pid, BrokerService broker) {
175                try {
176            LOG.info("Stopping broker " + pid);
177            broker.stop();
178            broker.waitUntilStopped();
179        } catch (Exception e) {
180            LOG.error("Exception on stopping broker", e);
181        }
182        }
183
184    synchronized public void destroy() {
185        for (String broker : brokers.keySet()) {
186            deleted(broker);
187        }
188    }
189
190    public BundleContext getBundleContext() {
191        return bundleContext;
192    }
193
194    public void setBundleContext(BundleContext bundleContext) {
195        this.bundleContext = bundleContext;
196    }
197}