/* * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors. * * 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.switchyard.deploy.osgi.internal; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.util.ArrayList; import java.util.Dictionary; import java.util.Enumeration; import java.util.Hashtable; import java.util.List; import org.osgi.framework.Bundle; import org.osgi.framework.ServiceRegistration; import org.osgi.service.cm.ConfigurationAdmin; import org.osgi.util.tracker.ServiceTracker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.switchyard.config.Configuration; import org.switchyard.config.Configurations; import org.switchyard.deploy.Component; import org.switchyard.deploy.osgi.base.SimpleExtension; /** * ComponentExtension. */ public class ComponentExtension extends SimpleExtension { /** * Location of component descriptor. */ public static final String META_INF_COMPONENT = "META-INF/services/org.switchyard.deploy.Component"; /** * List of activation types. */ public static final String SWITCHYARD_TYPES = "switchyard.types"; private final Logger _logger = LoggerFactory.getLogger(SwitchYardExtender.class); private final SwitchYardExtender _extender; private List<ServiceRegistration<Component>> _registrations = new ArrayList<ServiceRegistration<Component>>(); private final ServiceTracker<ConfigurationAdmin, ConfigurationAdmin> _configTracker; private ConfigurationAdmin _configAdmin; /** * Create a new instance of ComponentExtension. * @param extender extender * @param bundle component bundle */ public ComponentExtension(SwitchYardExtender extender, Bundle bundle) { super(bundle); _extender = extender; _configTracker = new ServiceTracker<ConfigurationAdmin, ConfigurationAdmin>( bundle.getBundleContext(), ConfigurationAdmin.class, null); _configTracker.open(); } @Override protected void doStart() throws Exception { _configAdmin = _configTracker.waitForService(0); URL url = getBundle().getEntry(META_INF_COMPONENT); List<String> classNames = parse(Component.class, url); for (String className : classNames) { Component component = initializeComponent(className); Dictionary<String, Object> props = new Hashtable<String, Object>(); props.put(SWITCHYARD_TYPES, component.getActivationTypes()); ServiceRegistration<Component> reg = getBundleContext().registerService(Component.class, component, props); _registrations.add(reg); } } @Override protected void doDestroy() throws Exception { for (ServiceRegistration<Component> reg : _registrations) { reg.unregister(); } _configTracker.close(); } private Component initializeComponent(String className) throws Exception { Class<Component> clazz = (Class<Component>) getBundle().loadClass(className); Component component = clazz.newInstance(); // load configuration for a component - SY components all use the same package // naming conventions so grab the config name from the package name. For custom // components (non-SY), use the name of the component returned from Component.getName(). String configName = className.contains("org.switchyard.component") && className.indexOf(".deploy") > 0 ? className.substring(0, className.indexOf(".deploy")) : component.getName(); Configuration config = loadConfiguration(configName); // invoke the component's init method with config loaded from config admin - the // destroy() method is called in ComponentRegistryImpl.unregisterComponent() component.init(config); return component; } private Configuration loadConfiguration(String configName) throws Exception { Configuration syConfig = Configurations.newConfiguration(); org.osgi.service.cm.Configuration osgiConfig = _configAdmin.getConfiguration(configName, null); Dictionary<String, Object> props = osgiConfig.getProperties(); if (props != null) { Enumeration<String> keys = props.keys(); while (keys.hasMoreElements()) { String key = keys.nextElement(); Configuration config = Configurations.newConfiguration(key); config.setValue((String)props.get(key)); syConfig.addChild(config); } } return syConfig; } // Parse a single line from the given configuration file, adding the name // on the line to the names list. // // Parse the content of the given URL as a provider-configuration file. // // @param service // The service type for which providers are being sought; // used to construct error detail strings // // @param u // The URL naming the configuration file to be parsed // // @return A (possibly empty) iterator that will yield the provider-class // names in the given configuration file that are not yet members // of the returned set // // @throws ServiceConfigurationError // If an I/O error occurs while reading from the given URL, or // if a configuration-file format error is detected // private List<String> parse(Class service, URL u) { InputStream in = null; BufferedReader r = null; ArrayList<String> names = new ArrayList<String>(); try { in = u.openStream(); r = new BufferedReader(new InputStreamReader(in, "utf-8")); int lc = 1; while ((lc = parseLine(service, u, r, lc, names)) >= 0); } catch (IOException x) { fail(service, "Error reading configuration file", x); } finally { try { if (r != null) { r.close(); } if (in != null) { in.close(); } } catch (IOException y) { fail(service, "Error closing configuration file", y); } } return names; } private int parseLine(Class service, URL u, BufferedReader r, int lc, List<String> names) throws IOException { String ln = r.readLine(); if (ln == null) { return -1; } int ci = ln.indexOf('#'); if (ci >= 0) { ln = ln.substring(0, ci); } ln = ln.trim(); int n = ln.length(); if (n != 0) { if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0)) { fail(service, u, lc, "Illegal configuration-file syntax"); } int cp = ln.codePointAt(0); if (!Character.isJavaIdentifierStart(cp)) { fail(service, u, lc, "Illegal provider-class name: " + ln); } for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) { cp = ln.codePointAt(i); if (!Character.isJavaIdentifierPart(cp) && (cp != '.')) { fail(service, u, lc, "Illegal provider-class name: " + ln); } } if (!names.contains(ln)) { names.add(ln); } } return lc + 1; } private static void fail(Class service, String msg, Throwable cause) { throw new IllegalStateException(service.getName() + ": " + msg, cause); } private static void fail(Class service, String msg) { throw new IllegalStateException(service.getName() + ": " + msg); } private static void fail(Class service, URL u, int line, String msg) { fail(service, u + ":" + line + ": " + msg); } }