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.osgi.CamelContextFactoryBean;
030import org.osgi.framework.BundleContext;
031import org.osgi.service.cm.ConfigurationException;
032import org.osgi.service.cm.ManagedServiceFactory;
033import org.slf4j.Logger;
034import org.slf4j.LoggerFactory;
035import org.springframework.beans.BeansException;
036import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
037import org.springframework.beans.factory.config.BeanPostProcessor;
038import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
039import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
040import org.springframework.context.support.ClassPathXmlApplicationContext;
041import org.springframework.core.io.Resource;
042
043public class ActiveMQServiceFactory implements ManagedServiceFactory {
044
045    private static final Logger LOG = LoggerFactory.getLogger(ActiveMQServiceFactory.class);
046
047    BundleContext bundleContext;
048    HashMap<String, BrokerService> brokers = new HashMap<String, BrokerService>();
049
050    @Override
051    public String getName() {
052        return "ActiveMQ Server Controller";
053    }
054
055    public Map<String, BrokerService> getBrokersMap() {
056        return Collections.unmodifiableMap(brokers);
057    }
058
059    @SuppressWarnings("rawtypes")
060    @Override
061    synchronized public void updated(String pid, Dictionary properties) throws ConfigurationException {
062
063        // First stop currently running broker (if any)
064        deleted(pid);
065
066        String config = (String) properties.get("config");
067        if (config == null) {
068            throw new ConfigurationException("config", "Property must be set");
069        }
070        String name = (String) properties.get("broker-name");
071        if (name == null) {
072            throw new ConfigurationException("broker-name", "Property must be set");
073        }
074
075        LOG.info("Starting broker " + name);
076
077        try {
078            Thread.currentThread().setContextClassLoader(BrokerService.class.getClassLoader());
079            Resource resource = Utils.resourceFromString(config);
080
081            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
082                    new String[]{resource.getURL().toExternalForm()}, false);
083
084            if (isCamelContextFactoryBeanExist()) {
085
086                ctx.addBeanFactoryPostProcessor(new BeanFactoryPostProcessor() {
087
088                    @Override
089                    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
090
091                        beanFactory.addBeanPostProcessor(new BeanPostProcessor() {
092
093                            @Override
094                            public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
095                                if (bean instanceof CamelContextFactoryBean) {
096                                    ((CamelContextFactoryBean) bean).setBundleContext(bundleContext);
097                                }
098                                return bean;
099                            }
100
101                            @Override
102                            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
103                                return bean;
104                            }
105                        });
106                    }
107                });
108            }
109
110            // Handle properties in configuration
111            PropertyPlaceholderConfigurer configurator = new PropertyPlaceholderConfigurer();
112
113            // convert dictionary to properties. Is there a better way?
114            Properties props = new Properties();
115            Enumeration<?> elements = properties.keys();
116            while (elements.hasMoreElements()) {
117                Object key = elements.nextElement();
118                props.put(key, properties.get(key));
119            }
120
121            configurator.setProperties(props);
122            configurator.setIgnoreUnresolvablePlaceholders(true);
123
124            ctx.addBeanFactoryPostProcessor(configurator);
125
126            ctx.refresh();
127
128            // Start the broker
129            BrokerService broker = ctx.getBean(BrokerService.class);
130            if (broker == null) {
131                throw new ConfigurationException(null, "Broker not defined");
132            }
133            // TODO deal with multiple brokers
134
135            SpringBrokerContext brokerContext = new SpringBrokerContext();
136            brokerContext.setConfigurationUrl(resource.getURL().toExternalForm());
137            brokerContext.setApplicationContext(ctx);
138            broker.setBrokerContext(brokerContext);
139
140            broker.start();
141            broker.waitUntilStarted();
142            brokers.put(pid, broker);
143        } catch (Exception e) {
144            throw new ConfigurationException(null, "Cannot start the broker", e);
145        }
146    }
147
148    private boolean isCamelContextFactoryBeanExist() {
149        try {
150            Class.forName("org.apache.camel.osgi.CamelContextFactoryBean");
151            return true;
152        } catch (ClassNotFoundException e) {
153            return false;
154        }
155    }
156
157    @Override
158    synchronized public void deleted(String pid) {
159        BrokerService broker = brokers.get(pid);
160        if (broker == null) {
161            return;
162        }
163        try {
164            LOG.info("Stopping broker " + pid);
165            broker.stop();
166            broker.waitUntilStopped();
167        } catch (Exception e) {
168            LOG.error("Exception on stopping broker", e);
169        }
170    }
171
172    synchronized public void destroy() {
173        for (String broker : brokers.keySet()) {
174            deleted(broker);
175        }
176    }
177
178    public BundleContext getBundleContext() {
179        return bundleContext;
180    }
181
182    public void setBundleContext(BundleContext bundleContext) {
183        this.bundleContext = bundleContext;
184    }
185}