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 */
017 package org.apache.camel.script.osgi;
018
019 import java.io.BufferedReader;
020 import java.io.InputStreamReader;
021 import java.lang.reflect.Method;
022 import java.net.URL;
023 import java.util.ArrayList;
024 import java.util.Enumeration;
025 import java.util.List;
026 import java.util.Map;
027 import java.util.concurrent.ConcurrentHashMap;
028
029 import javax.script.ScriptEngine;
030 import javax.script.ScriptEngineFactory;
031
032 import org.apache.camel.impl.osgi.tracker.BundleTracker;
033 import org.apache.camel.impl.osgi.tracker.BundleTrackerCustomizer;
034 import org.osgi.framework.Bundle;
035 import org.osgi.framework.BundleActivator;
036 import org.osgi.framework.BundleContext;
037 import org.osgi.framework.BundleEvent;
038 import org.osgi.framework.InvalidSyntaxException;
039 import org.osgi.framework.ServiceReference;
040 import org.osgi.framework.ServiceRegistration;
041 import org.slf4j.Logger;
042 import org.slf4j.LoggerFactory;
043
044 public class Activator implements BundleActivator, BundleTrackerCustomizer {
045 public static final String META_INF_SERVICES_DIR = "META-INF/services";
046 public static final String SCRIPT_ENGINE_SERVICE_FILE = "javax.script.ScriptEngineFactory";
047
048 private static final transient Logger LOG = LoggerFactory.getLogger(Activator.class);
049 private static BundleContext context;
050 private BundleTracker tracker;
051
052 private Map<Long, List<BundleScriptEngineResolver>> resolvers
053 = new ConcurrentHashMap<Long, List<BundleScriptEngineResolver>>();
054
055 public static BundleContext getBundleContext() {
056 return context;
057 }
058
059 public void start(BundleContext context) throws Exception {
060 Activator.context = context;
061 LOG.info("Camel-Script activator starting");
062 tracker = new BundleTracker(context, Bundle.ACTIVE, this);
063 tracker.open();
064 LOG.info("Camel-Script activator started");
065 }
066
067 public void stop(BundleContext context) throws Exception {
068 LOG.info("Camel-Script activator stopping");
069 tracker.close();
070 LOG.info("Camel-Script activator stopped");
071 Activator.context = null;
072 }
073
074 public Object addingBundle(Bundle bundle, BundleEvent event) {
075 List<BundleScriptEngineResolver> r = new ArrayList<BundleScriptEngineResolver>();
076 registerScriptEngines(bundle, r);
077 for (BundleScriptEngineResolver service : r) {
078 service.register();
079 }
080 resolvers.put(bundle.getBundleId(), r);
081 return bundle;
082 }
083
084 public void modifiedBundle(Bundle bundle, BundleEvent event, Object object) {
085 }
086
087 public void removedBundle(Bundle bundle, BundleEvent event, Object object) {
088 LOG.debug("Bundle stopped: {}", bundle.getSymbolicName());
089 List<BundleScriptEngineResolver> r = resolvers.remove(bundle.getBundleId());
090 if (r != null) {
091 for (BundleScriptEngineResolver service : r) {
092 service.unregister();
093 }
094 }
095 }
096
097 public static ScriptEngine resolveScriptEngine(String scriptEngineName) throws InvalidSyntaxException {
098 ServiceReference[] refs = context.getServiceReferences(ScriptEngineResolver.class.getName(), null);
099 if (refs == null) {
100 LOG.info("No OSGi script engine resolvers available!");
101 return null;
102 }
103
104 LOG.debug("Found " + refs.length + " OSGi ScriptEngineResolver services");
105
106 for (ServiceReference ref : refs) {
107 ScriptEngineResolver resolver = (ScriptEngineResolver) context.getService(ref);
108 ScriptEngine engine = resolver.resolveScriptEngine(scriptEngineName);
109 context.ungetService(ref);
110 LOG.debug("OSGi resolver " + resolver + " produced " + scriptEngineName + " engine " + engine);
111 if (engine != null) {
112 return engine;
113 }
114 }
115 return null;
116 }
117
118
119 protected void registerScriptEngines(Bundle bundle, List<BundleScriptEngineResolver> resolvers) {
120 URL configURL = null;
121 for (Enumeration e = bundle.findEntries(META_INF_SERVICES_DIR, SCRIPT_ENGINE_SERVICE_FILE, false); e != null && e.hasMoreElements();) {
122 configURL = (URL) e.nextElement();
123 }
124 if (configURL != null) {
125 LOG.info("Found ScriptEngineFactory in " + bundle.getSymbolicName());
126 resolvers.add(new BundleScriptEngineResolver(bundle, configURL));
127 }
128 }
129 public static interface ScriptEngineResolver {
130 ScriptEngine resolveScriptEngine(String name);
131 }
132 protected static class BundleScriptEngineResolver implements ScriptEngineResolver {
133 protected final Bundle bundle;
134 private ServiceRegistration reg;
135 private final URL configFile;
136
137 public BundleScriptEngineResolver(Bundle bundle, URL configFile) {
138 this.bundle = bundle;
139 this.configFile = configFile;
140 }
141 public void register() {
142 reg = bundle.getBundleContext().registerService(ScriptEngineResolver.class.getName(),
143 this, null);
144 }
145 public void unregister() {
146 reg.unregister();
147 }
148 public ScriptEngine resolveScriptEngine(String name) {
149 try {
150 BufferedReader in = new BufferedReader(new InputStreamReader(configFile.openStream()));
151 String className = in.readLine();
152 in.close();
153 Class cls = bundle.loadClass(className);
154 if (!ScriptEngineFactory.class.isAssignableFrom(cls)) {
155 throw new IllegalStateException("Invalid ScriptEngineFactory: " + cls.getName());
156 }
157 ScriptEngineFactory factory = (ScriptEngineFactory) cls.newInstance();
158 List<String> names = factory.getNames();
159 for (String test : names) {
160 if (test.equals(name)) {
161 ClassLoader old = Thread.currentThread().getContextClassLoader();
162 ScriptEngine engine;
163 try {
164 // JRuby seems to require the correct TCCL to call getScriptEngine
165 Thread.currentThread().setContextClassLoader(factory.getClass().getClassLoader());
166 engine = factory.getScriptEngine();
167 } finally {
168 Thread.currentThread().setContextClassLoader(old);
169 }
170 LOG.trace("Resolved ScriptEngineFactory: {} for expected name: {}", engine, name);
171 return engine;
172 }
173 }
174 LOG.debug("ScriptEngineFactory: {} does not match expected name: {}", factory.getEngineName(), name);
175 return null;
176 } catch (Exception e) {
177 LOG.warn("Cannot create ScriptEngineFactory: " + e.getClass().getName(), e);
178 return null;
179 }
180 }
181
182 @Override
183 public String toString() {
184 return "OSGi script engine resolver for " + bundle.getSymbolicName();
185 }
186 }
187
188
189 }