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.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.setStartAsync(true);
141            broker.start();
142
143            if (!broker.isSlave())
144                broker.waitUntilStarted();
145            brokers.put(pid, broker);
146        } catch (Exception e) {
147            throw new ConfigurationException(null, "Cannot start the broker", e);
148        }
149    }
150
151    private boolean isCamelContextFactoryBeanExist() {
152        try {
153            Class.forName("org.apache.camel.osgi.CamelContextFactoryBean");
154            return true;
155        } catch (ClassNotFoundException e) {
156            return false;
157        }
158    }
159
160    @Override
161    synchronized public void deleted(String pid) {
162        BrokerService broker = brokers.get(pid);
163        if (broker == null) {
164            return;
165        }
166        try {
167            LOG.info("Stopping broker " + pid);
168            broker.stop();
169            broker.waitUntilStopped();
170        } catch (Exception e) {
171            LOG.error("Exception on stopping broker", e);
172        }
173    }
174
175    synchronized public void destroy() {
176        for (String broker : brokers.keySet()) {
177            deleted(broker);
178        }
179    }
180
181    public BundleContext getBundleContext() {
182        return bundleContext;
183    }
184
185    public void setBundleContext(BundleContext bundleContext) {
186        this.bundleContext = bundleContext;
187    }
188}