001: /*
002: * All content copyright (c) 2003-2007 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tc.plugins;
006:
007: import org.apache.commons.io.IOUtils;
008: import org.apache.xmlbeans.XmlException;
009: import org.osgi.framework.Bundle;
010: import org.osgi.framework.BundleException;
011: import org.osgi.framework.Constants;
012: import org.osgi.framework.InvalidSyntaxException;
013: import org.osgi.framework.ServiceReference;
014:
015: import com.tc.bundles.EmbeddedOSGiEventHandler;
016: import com.tc.bundles.EmbeddedOSGiRuntime;
017: import com.tc.bundles.Resolver;
018: import com.tc.config.schema.setup.ConfigurationSetupException;
019: import com.tc.logging.CustomerLogging;
020: import com.tc.logging.TCLogger;
021: import com.tc.logging.TCLogging;
022: import com.tc.object.config.ConfigLoader;
023: import com.tc.object.config.DSOClientConfigHelper;
024: import com.tc.object.config.ModuleSpec;
025: import com.tc.object.config.StandardDSOClientConfigHelper;
026: import com.tc.object.loaders.ClassProvider;
027: import com.tc.object.loaders.NamedClassLoader;
028: import com.tc.object.loaders.Namespace;
029: import com.tc.object.util.JarResourceLoader;
030: import com.tc.properties.TCProperties;
031: import com.tc.properties.TCPropertiesImpl;
032: import com.tc.util.Assert;
033: import com.tc.util.VendorVmSignature;
034: import com.tc.util.VendorVmSignatureException;
035: import com.terracottatech.config.DsoApplication;
036: import com.terracottatech.config.Module;
037: import com.terracottatech.config.Modules;
038:
039: import java.io.IOException;
040: import java.io.InputStream;
041: import java.lang.reflect.Method;
042: import java.net.MalformedURLException;
043: import java.net.URL;
044: import java.util.ArrayList;
045: import java.util.Arrays;
046: import java.util.Comparator;
047: import java.util.Dictionary;
048: import java.util.Hashtable;
049: import java.util.List;
050: import java.util.regex.Matcher;
051: import java.util.regex.Pattern;
052:
053: public class ModulesLoader {
054:
055: private static final Comparator SERVICE_COMPARATOR = new Comparator() {
056:
057: public int compare(Object arg0, Object arg1) {
058: ServiceReference s1 = (ServiceReference) arg0;
059: ServiceReference s2 = (ServiceReference) arg1;
060:
061: Integer r1 = (Integer) s1
062: .getProperty(Constants.SERVICE_RANKING);
063: Integer r2 = (Integer) s2
064: .getProperty(Constants.SERVICE_RANKING);
065:
066: if (r1 == null)
067: r1 = ModuleSpec.NORMAL_RANK;
068: if (r2 == null)
069: r2 = ModuleSpec.NORMAL_RANK;
070:
071: return r2.compareTo(r1);
072: }
073:
074: };
075:
076: private static final TCLogger logger = TCLogging
077: .getLogger(ModulesLoader.class);
078: private static final TCLogger consoleLogger = CustomerLogging
079: .getConsoleLogger();
080:
081: private static final Object lock = new Object();
082:
083: private ModulesLoader() {
084: // cannot be instantiated
085: }
086:
087: public static void initModules(
088: final DSOClientConfigHelper configHelper,
089: final ClassProvider classProvider, final boolean forBootJar) {
090: EmbeddedOSGiRuntime osgiRuntime = null;
091: synchronized (lock) {
092: final Modules modules = configHelper
093: .getModulesForInitialization();
094: if (modules == null) {
095: consoleLogger
096: .warn("Modules configuration might not have been properly initialized.");
097: return;
098: }
099:
100: try {
101: osgiRuntime = EmbeddedOSGiRuntime.Factory
102: .createOSGiRuntime(modules);
103: initModules(osgiRuntime, configHelper, classProvider,
104: modules.getModuleArray(), forBootJar);
105: if (!forBootJar) {
106: getModulesCustomApplicatorSpecs(osgiRuntime,
107: configHelper);
108: }
109: } catch (Exception e) {
110: System.err
111: .println("Unable to initialize modules runtime; "
112: + e.getMessage());
113: logger.error(e); // at least log this exception, it's very frustrating if it is completely swallowed
114: System.exit(-9);
115: } finally {
116: if (forBootJar) {
117: shutdown(osgiRuntime);
118: }
119: }
120: }
121: }
122:
123: private static void shutdown(final EmbeddedOSGiRuntime osgiRuntime) {
124: if (osgiRuntime != null) {
125: osgiRuntime.shutdown();
126: }
127: }
128:
129: private static void initModules(
130: final EmbeddedOSGiRuntime osgiRuntime,
131: final DSOClientConfigHelper configHelper,
132: final ClassProvider classProvider, final Module[] modules,
133: final boolean forBootJar) throws BundleException {
134:
135: if (configHelper instanceof StandardDSOClientConfigHelper) {
136: final Dictionary serviceProps = new Hashtable();
137: serviceProps.put(Constants.SERVICE_VENDOR,
138: "Terracotta, Inc.");
139: serviceProps
140: .put(
141: Constants.SERVICE_DESCRIPTION,
142: "Main point of entry for programmatic access to"
143: + " the Terracotta bytecode instrumentation");
144: osgiRuntime.registerService(
145: StandardDSOClientConfigHelper.class.getName(),
146: configHelper, serviceProps);
147: }
148:
149: EmbeddedOSGiEventHandler handler = new EmbeddedOSGiEventHandler() {
150: public void callback(final Object payload)
151: throws BundleException {
152: Assert.assertTrue(payload instanceof Bundle);
153: Bundle bundle = (Bundle) payload;
154: if (bundle != null) {
155: if (!forBootJar) {
156: registerClassLoader(classProvider, bundle);
157: }
158: loadConfiguration(configHelper, bundle);
159: }
160: }
161: };
162:
163: final List moduleList = new ArrayList();
164: moduleList.addAll(getAdditionalModules());
165: moduleList.addAll(Arrays.asList(modules));
166:
167: final Module[] allModules = (Module[]) moduleList
168: .toArray(new Module[moduleList.size()]);
169: final Resolver resolver = new Resolver(osgiRuntime
170: .getRepositories());
171: final URL[] locations = resolver.resolve(allModules);
172:
173: osgiRuntime.installBundles(locations);
174: osgiRuntime.startBundles(locations, handler);
175: }
176:
177: private static List getAdditionalModules() {
178: final List modules = new ArrayList();
179: final TCProperties modulesProps = TCPropertiesImpl
180: .getProperties().getPropertiesFor("l1.modules");
181: final String additionalModuleList = modulesProps != null ? modulesProps
182: .getProperty("additional", true)
183: : null;
184:
185: if (additionalModuleList != null) {
186: final String[] additionalModules = additionalModuleList
187: .split(";");
188: Pattern pattern = Pattern
189: .compile("(.+?)-([0-9\\.]+)-([0-9\\.\\-]+)");
190: for (int i = 0; i < additionalModules.length; i++) {
191: if (additionalModules[i].length() == 0) {
192: continue;
193: }
194:
195: final Matcher matcher = pattern
196: .matcher(additionalModules[i]);
197: if (!matcher.find() || matcher.groupCount() < 3) {
198: logger.error("Invalid bundle-jar filename "
199: + additionalModules[i]
200: + "; filenames need to match the pattern: "
201: + pattern.toString());
202: continue;
203: }
204:
205: String component = matcher.group(1);
206: final String componentVersion = matcher.group(2);
207: final String moduleVersion = matcher.group(3)
208: .replaceFirst("\\.$", "");
209:
210: final Module module = Module.Factory.newInstance();
211: String groupId = module.getGroupId(); // rely on the constant defined in the schema for the default groupId
212: final int n = component.lastIndexOf('.');
213: if (n > 0) {
214: groupId = component.substring(0, n);
215: component = component.substring(n + 1);
216: module.setGroupId(groupId);
217: }
218:
219: module.setName(component + "-" + componentVersion);
220: module.setVersion(moduleVersion);
221: modules.add(module);
222: }
223: }
224:
225: return modules;
226: }
227:
228: private static void registerClassLoader(
229: final ClassProvider classProvider, final Bundle bundle)
230: throws BundleException {
231: NamedClassLoader ncl = getClassLoader(bundle);
232:
233: String loaderName = Namespace.createLoaderName(
234: Namespace.MODULES_NAMESPACE, ncl.toString());
235: ncl.__tc_setClassLoaderName(loaderName);
236: classProvider.registerNamedLoader(ncl);
237: }
238:
239: private static NamedClassLoader getClassLoader(Bundle bundle)
240: throws BundleException {
241: try {
242: Method m = bundle.getClass().getDeclaredMethod(
243: "getClassLoader", new Class[0]);
244: m.setAccessible(true);
245: ClassLoader classLoader = (ClassLoader) m.invoke(bundle,
246: new Object[0]);
247: return (NamedClassLoader) classLoader;
248: } catch (Throwable t) {
249: throw new BundleException(
250: "Unable to get classloader for bundle.", t);
251: }
252: }
253:
254: private static void getModulesCustomApplicatorSpecs(
255: final EmbeddedOSGiRuntime osgiRuntime,
256: final DSOClientConfigHelper configHelper)
257: throws InvalidSyntaxException {
258: ServiceReference[] serviceReferences = osgiRuntime
259: .getAllServiceReferences(ModuleSpec.class.getName(),
260: null);
261: if (serviceReferences != null && serviceReferences.length > 0) {
262: Arrays.sort(serviceReferences, SERVICE_COMPARATOR);
263: }
264:
265: if (serviceReferences == null) {
266: return;
267: }
268: ModuleSpec[] modulesSpecs = new ModuleSpec[serviceReferences.length];
269: for (int i = 0; i < serviceReferences.length; i++) {
270: modulesSpecs[i] = (ModuleSpec) osgiRuntime
271: .getService(serviceReferences[i]);
272: osgiRuntime.ungetService(serviceReferences[i]);
273: }
274: configHelper.setModuleSpecs(modulesSpecs);
275: }
276:
277: /**
278: * Extract the list of xml-fragment files that a config bundle should use for instrumentation.
279: */
280: public static String[] getConfigPath(final Bundle bundle)
281: throws BundleException {
282: final VendorVmSignature vmsig;
283: try {
284: vmsig = new VendorVmSignature();
285: } catch (VendorVmSignatureException e) {
286: throw new BundleException(e.getMessage());
287: }
288:
289: final String TERRACOTTA_CONFIGURATION = "Terracotta-Configuration";
290: final String TERRACOTTA_CONFIGURATION_FOR_VM = TERRACOTTA_CONFIGURATION
291: + VendorVmSignature.SIGNATURE_SEPARATOR
292: + vmsig.getSignature();
293:
294: String path = (String) bundle.getHeaders().get(
295: TERRACOTTA_CONFIGURATION_FOR_VM);
296: if (path == null) {
297: path = (String) bundle.getHeaders().get(
298: TERRACOTTA_CONFIGURATION);
299: if (path == null) {
300: path = "terracotta.xml";
301: }
302: }
303:
304: final String[] paths = path.split(",");
305: for (int i = 0; i < paths.length; i++) {
306: paths[i] = paths[i].trim();
307: if (!paths[i].endsWith(".xml")) {
308: paths[i] = paths[i].concat(".xml");
309: }
310: }
311:
312: return paths;
313: }
314:
315: private static void loadConfiguration(
316: final DSOClientConfigHelper configHelper,
317: final Bundle bundle) throws BundleException {
318: // attempt to load all of the config fragments found in the config-bundle
319: final String[] paths = getConfigPath(bundle);
320: for (int i = 0; i < paths.length; i++) {
321: final String configPath = paths[i];
322: final InputStream is;
323: try {
324: is = JarResourceLoader.getJarResource(new URL(bundle
325: .getLocation()), configPath);
326: } catch (MalformedURLException murle) {
327: throw new BundleException("Unable to create URL from: "
328: + bundle.getLocation(), murle);
329: } catch (IOException ioe) {
330: throw new BundleException("Unable to extract "
331: + configPath + " from URL: "
332: + bundle.getLocation(), ioe);
333: }
334:
335: if (is == null) {
336: continue;
337: }
338:
339: // otherwise, merge it with the current configuration
340: try {
341: final DsoApplication application = DsoApplication.Factory
342: .parse(is);
343: if (application != null) {
344: final ConfigLoader loader = new ConfigLoader(
345: configHelper, logger);
346: loader.loadDsoConfig(application);
347: logger.info("Module configuration loaded for "
348: + bundle.getSymbolicName() + " ("
349: + configPath + ")");
350: // loader.loadSpringConfig(application.getSpring());
351: }
352: is.close();
353: } catch (IOException ioe) {
354: String msg = "Unable to read configuration from bundle: "
355: + bundle.getSymbolicName();
356: consoleLogger.warn(msg, ioe);
357: logger.warn(msg, ioe);
358: } catch (XmlException xmle) {
359: String msg = "Unable to parse configuration from bundle: "
360: + bundle.getSymbolicName();
361: consoleLogger.warn(msg, xmle);
362: logger.warn(msg, xmle);
363: } catch (ConfigurationSetupException cse) {
364: String msg = "Unable to load configuration from bundle: "
365: + bundle.getSymbolicName();
366: consoleLogger.warn(msg, cse);
367: logger.warn(msg, cse);
368: } finally {
369: if (is != null) {
370: IOUtils.closeQuietly(is);
371: }
372: }
373: }
374: }
375: }
|