/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.tuscany.sca; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URI; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Properties; import javax.xml.stream.XMLStreamException; import org.apache.tuscany.sca.assembly.AssemblyFactory; import org.apache.tuscany.sca.common.java.io.IOHelper; import org.apache.tuscany.sca.contribution.processor.ContributionReadException; import org.apache.tuscany.sca.contribution.processor.ProcessorContext; import org.apache.tuscany.sca.core.DefaultExtensionPointRegistry; import org.apache.tuscany.sca.core.ExtensionPointRegistry; import org.apache.tuscany.sca.core.FactoryExtensionPoint; import org.apache.tuscany.sca.core.ModuleActivatorExtensionPoint; import org.apache.tuscany.sca.core.UtilityExtensionPoint; import org.apache.tuscany.sca.core.assembly.RuntimeAssemblyFactory; import org.apache.tuscany.sca.deployment.Deployer; import org.apache.tuscany.sca.impl.NodeImpl; import org.apache.tuscany.sca.monitor.ValidationException; import org.apache.tuscany.sca.node.configuration.ContributionConfiguration; import org.apache.tuscany.sca.node.configuration.NodeConfiguration; import org.apache.tuscany.sca.runtime.ActivationException; import org.apache.tuscany.sca.runtime.CompositeActivator; import org.apache.tuscany.sca.runtime.DomainRegistry; import org.apache.tuscany.sca.runtime.ExtensibleDomainRegistryFactory; import org.apache.tuscany.sca.runtime.RuntimeProperties; import org.apache.tuscany.sca.work.WorkScheduler; import org.oasisopen.sca.ServiceRuntimeException; /** * The TuscanyRuntime is the main class for using Tuscany. It can create Nodes, * run composites, and provides access to various utility APIs */ public class TuscanyRuntime { public static final String DEFAUL_DOMAIN_NAME = "default"; private Deployer deployer; private ExtensionPointRegistry extensionPointRegistry; private CompositeActivator compositeActivator; private ExtensibleDomainRegistryFactory domainRegistryFactory; private RuntimeAssemblyFactory assemblyFactory; /** * Creates a new TuscanyRuntime * @return a TuscanyRuntime */ public static TuscanyRuntime newInstance() { return new TuscanyRuntime(null); } /** * Creates a new TuscanyRuntime * @param config Properties to configure the TuscanyRuntime * @return a TuscanyRuntime */ public static TuscanyRuntime newInstance(Properties config) { return new TuscanyRuntime(config); } /** * A helper method to run a standalone SCA composite in the default standalone SCA domain. * @param compositeURI URI within the contribution of a composite to run * if compositeURI is null then all deployable composites in the contribution will be run * @param contributionURL URL of the contribution * @param dependentContributionURLs optional URLs of dependent contributions * @return a Node with installed contributions */ public static Node runComposite(String compositeURI, String contributionURL, String... dependentContributionURLs) { return runComposite(null, compositeURI, contributionURL, dependentContributionURLs); } /** * A helper method to run a standalone SCA composite in a SCA domain * @param domainURI the URI of the SCA domain * @param compositeURI URI within the contribution of a composite to run * if compositeURI is null then all deployable composites in the contribution will be run * @param contributionURL URL of the contribution * @param dependentContributionURLs optional URLs of dependent contributions * @return a Node with installed contributions */ public static Node runComposite(URI domainURI, String compositeURI, String contributionURL, String... dependentContributionURLs) { try { TuscanyRuntime runtime = newInstance(); String domain = domainURI == null ? DEFAUL_DOMAIN_NAME : domainURI.toString(); DomainRegistry domainRegistry = runtime.domainRegistryFactory.getEndpointRegistry(domain, null); NodeImpl node = new NodeImpl(runtime.deployer, runtime.compositeActivator, domainRegistry, runtime.extensionPointRegistry, runtime); if (dependentContributionURLs != null) { for (int i=dependentContributionURLs.length-1; i>-1; i--) { node.installContribution(null, dependentContributionURLs[i], null, null); } } String curi = node.installContribution(null, contributionURL, null, null); if (compositeURI != null) { node.startComposite(curi, compositeURI); } else { for (String compURI : node.getDeployableCompositeURIs(curi)) { node.startComposite(curi, compURI); } } return node; } catch (Exception e) { throw new RuntimeException(e); } } protected TuscanyRuntime(Properties config) { init(config); } /** * Creates a Node * @return a Node */ public Node createNode() { return createNode((String)null); } /** * Creates a Node in an SCA domain * @param domainURI the URI of the SCA domain * @return a Node */ public Node createNode(String domainURI) { if (domainURI == null){ domainURI = DEFAUL_DOMAIN_NAME; } DomainRegistry domainRegistry = domainRegistryFactory.getEndpointRegistry(domainURI, null); return new NodeImpl(deployer, compositeActivator, domainRegistry, extensionPointRegistry, null); } /* * Create a node from a file system directory. * If the directory is actually a file use createNodeFromXML * if the directory contains a file named node.xml then use createNodeFromXML * Otherwise, the directory can contain: * domain.properties * contributions - jar, zip, or exploded directories * sca-contribution.xml metaData files to override whats in a contribution * .composite files to add to contributions as additional deployables * * TODO: Review if this is useful? */ public Node createNode(File directory) throws ContributionReadException, ValidationException, ActivationException, XMLStreamException, IOException { if (!directory.isDirectory()) { return createNodeFromXML(directory.toURI().toURL().toString()); } File nodeXML = new File(directory, "node.xml"); if (nodeXML.exists()) { return createNodeFromXML(nodeXML.toURI().toURL().toString()); } Properties domainProps = new Properties(); File propsFile = new File(directory, "domain.properties"); if (propsFile.exists()) { domainProps.load(new FileInputStream(propsFile)); } String domainName = domainProps.getProperty("domainName", directory.getName()); String domainURI = domainProps.getProperty("domainURI", domainName); DomainRegistry domainRegistry = domainRegistryFactory.getEndpointRegistry(domainURI, domainName); Node node = new NodeImpl(deployer, compositeActivator, domainRegistry, extensionPointRegistry, null); List installed = new ArrayList(); for (File f : directory.listFiles()) { if (f.getName().endsWith(".jar") || f.getName().endsWith(".zip") || (f.isDirectory() && !f.getName().startsWith("."))) { String fn = f.getName().lastIndexOf('.') == -1 ? f.getName() : f.getName().substring(0, f.getName().lastIndexOf('.')); // ignore the contribution if it has an associated exploded folder version if (!f.isDirectory() && new File(f.getParent(), fn).isDirectory()) { continue; } String metaData = null; for (File f2 : directory.listFiles()) { if (f2.getName().startsWith(fn) && f2.getName().endsWith(".xml")) { metaData = f2.getPath(); break; } } List dependencyURIs = new ArrayList(); File dependencyFile = new File(directory, fn + ".dependencies"); if (dependencyFile.exists()) { BufferedReader br = new BufferedReader(new FileReader(dependencyFile)); String s; while ((s = br.readLine()) != null) { if (!s.startsWith("#") && s.trim().length() > 0) { dependencyURIs.addAll(Arrays.asList(s.trim().split("[ ,]+"))); } } br.close(); } String curi = node.installContribution(null, f.getPath(), metaData, dependencyURIs); installed.add(curi); for (File f2 : directory.listFiles()) { if (f2.getName().startsWith(fn) && f2.getName().endsWith(".composite")) { node.addDeploymentComposite(curi, new FileReader(f2)); } } } } for (String curi : installed) { node.startDeployables(curi); } return node; } /* Node.xml hot update * - domain URi changed * - restart entire node * - List of contributions (matched on uri) * - uninstall removed * - install added * - for each existing * - if url or metadata or duris changed - update * - if startdeployables changed * - if now false then stop all started * - if now true then start deployables * * * */ /** * Creates a Node from an XML configuration file * @param configURL the URL to the XML configuration file * @return Node the configured Node */ public Node createNodeFromXML(String configURL) throws ContributionReadException, ActivationException, ValidationException { NodeConfiguration configuration = loadConfiguration(configURL); NodeImpl node = (NodeImpl)createNode(configuration.getDomainURI()); for ( ContributionConfiguration c : configuration.getContributions()) { String curi = node.installContribution(c.getURI(), c.getLocation(), c.getMetaDataURL(), c.getDependentContributionURIs()); if (c.isStartDeployables()) { for (String compURI : node.getDeployableCompositeURIs(curi)) { node.startComposite(curi, compURI); } } } return node; } /** * Stop the TuscanyRuntime */ public void stop() { extensionPointRegistry.stop(); } protected void init(Properties config) { if (config == null) { config = new Properties(); } this.extensionPointRegistry = new DefaultExtensionPointRegistry(); extensionPointRegistry.start(); FactoryExtensionPoint modelFactories = extensionPointRegistry.getExtensionPoint(FactoryExtensionPoint.class); this.assemblyFactory = new RuntimeAssemblyFactory(extensionPointRegistry); modelFactories.addFactory(assemblyFactory); UtilityExtensionPoint utilities = extensionPointRegistry.getExtensionPoint(UtilityExtensionPoint.class); this.compositeActivator = utilities.getUtility(CompositeActivator.class); this.deployer = utilities.getUtility(Deployer.class); utilities.getUtility(RuntimeProperties.class).setProperties(config); utilities.getUtility(WorkScheduler.class); // Initialize the Tuscany module activators // The module activators will be started extensionPointRegistry.getExtensionPoint(ModuleActivatorExtensionPoint.class); this.domainRegistryFactory = ExtensibleDomainRegistryFactory.getInstance(extensionPointRegistry); } /** * Get the ExtensionPointRegistry used by this runtime * @return extensionPointRegistry */ public ExtensionPointRegistry getExtensionPointRegistry() { return extensionPointRegistry; } /** * Get the Deployer. The Deployer can be used to create contribution artifacts * when configuring a Node programatically. * @return the Deployer */ public Deployer getDeployer() { return deployer; } /** * Get the AssemblyFactory. The AssemblyFactory can be used to create contribution * artifact contents when configuring a Node programatically. * @return the AssemblyFactory */ public AssemblyFactory getAssemblyFactory() { return assemblyFactory; } protected NodeConfiguration loadConfiguration(String configURL) { InputStream xml =null; try { URL base = IOHelper.getLocationAsURL(configURL); xml = IOHelper.openStream(base); InputStreamReader reader = new InputStreamReader(xml, "UTF-8"); ProcessorContext context = deployer.createProcessorContext(); NodeConfiguration config = deployer.loadXMLDocument(reader, context.getMonitor()); if (base != null && config != null) { // Resolve the contribution location against the node.xml // TODO: absolute locations? for (ContributionConfiguration c : config.getContributions()) { String location = c.getLocation(); if (location != null) { URL url = new URL(base, location); url = IOHelper.normalize(url); c.setLocation(url.toString()); } } } return config; } catch (Throwable e) { throw new ServiceRuntimeException(e); } finally { try { if (xml != null) xml.close(); } catch (IOException e) { throw new ServiceRuntimeException(e); } } } }