diff options
Diffstat (limited to '')
-rw-r--r-- | sca-java-2.x/tags/2.0-Beta1-RC2/modules/node-api/src/main/java/org/apache/tuscany/sca/node/NodeFactory.java | 630 |
1 files changed, 630 insertions, 0 deletions
diff --git a/sca-java-2.x/tags/2.0-Beta1-RC2/modules/node-api/src/main/java/org/apache/tuscany/sca/node/NodeFactory.java b/sca-java-2.x/tags/2.0-Beta1-RC2/modules/node-api/src/main/java/org/apache/tuscany/sca/node/NodeFactory.java new file mode 100644 index 0000000000..95d1adc92e --- /dev/null +++ b/sca-java-2.x/tags/2.0-Beta1-RC2/modules/node-api/src/main/java/org/apache/tuscany/sca/node/NodeFactory.java @@ -0,0 +1,630 @@ +/* + * 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.node; + +import static org.apache.tuscany.sca.node.ContributionLocationHelper.getContributionLocations; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.lang.reflect.InvocationTargetException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.apache.tuscany.sca.node.configuration.DefaultNodeConfigurationFactory; +import org.apache.tuscany.sca.node.configuration.NodeConfiguration; +import org.apache.tuscany.sca.node.configuration.NodeConfigurationFactory; +import org.oasisopen.sca.ServiceReference; +import org.oasisopen.sca.ServiceRuntimeException; + +/** + * A factory for SCA processing nodes. An SCA processing node can be loaded + * with an SCA composite and the SCA contributions required by the composite. + * + * @version $Rev$ $Date$ + * @tuscany.spi.extension.asclient + */ +public abstract class NodeFactory extends DefaultNodeConfigurationFactory { + /** + * Default location of contribution metadata in an SCA contribution. + */ + private static final String SCA_CONTRIBUTION_META = "META-INF/sca-contribution.xml"; + + /** + * Default location of a generated contribution metadata in an SCA contribution. + */ + private static final String SCA_CONTRIBUTION_GENERATED_META = "META-INF/sca-contribution-generated.xml"; + + protected static NodeFactory instance; + protected static Class<?> factoryImplClass; + + protected static List<NodeFactory> factories = new ArrayList<NodeFactory>(); + + protected static void setNodeFactory(NodeFactory factory) { + instance = factory; + } + + public static class NodeProxy implements Node { + private Object node; + + private NodeProxy(Object node) { + super(); + this.node = node; + } + + public static <T> T createProxy(Class<T> type, Object node) { + try { + return type.getDeclaredConstructor(Object.class).newInstance(node); + } catch (Exception e) { + throw new IllegalArgumentException(e); + } + } + + public <B, R extends ServiceReference<B>> R cast(B target) throws IllegalArgumentException { + try { + return (R)node.getClass().getMethod("cast", Object.class).invoke(node, target); + } catch (Throwable e) { + handleException(e); + return null; + } + } + + public <B> B getService(Class<B> businessInterface, String serviceName) { + try { + return (B)node.getClass().getMethod("getService", Class.class, String.class).invoke(node, businessInterface, serviceName); + } catch (Throwable e) { + handleException(e); + return null; + } + } + + public <B> ServiceReference<B> getServiceReference(Class<B> businessInterface, String serviceName) { + try { + return (ServiceReference<B>)node.getClass().getMethod("getServiceReference", Class.class, String.class).invoke(node, businessInterface, serviceName); + } catch (Throwable e) { + handleException(e); + return null; + } + } + + public Node start() { + try { + return new NodeProxy(node.getClass().getMethod("start").invoke(node)); + } catch (Throwable e) { + handleException(e); + return null; + } + } + + public void stop() { + try { + node.getClass().getMethod("stop").invoke(node); + } catch (Throwable e) { + handleException(e); + } + } + + public void destroy() { + try { + node.getClass().getMethod("destroy").invoke(node); + } catch (Throwable e) { + handleException(e); + } + } + + private static void handleException(Throwable ex) { + if (ex instanceof InvocationTargetException) { + ex = ((InvocationTargetException)ex).getTargetException(); + } + if (ex instanceof RuntimeException) { + throw (RuntimeException)ex; + } + if (ex instanceof Error) { + throw (Error)ex; + } else { + throw new RuntimeException(ex); + } + } + + } + + /** + * Returns the SCA node factory instance. + * + * @return the SCA node factory + */ + public synchronized static NodeFactory getInstance() { + if (instance == null) { + instance = newInstance(); + } + return instance; + } + + /** + * Returns a new SCA node factory instance. + * + * @return a new SCA node factory + */ + public static NodeFactory newInstance() { + NodeFactory nodeFactory = null; + try { + Class<?> factoryClass = getFactoryImplClass(); + nodeFactory = (NodeFactory)factoryClass.newInstance(); + + } catch (Exception e) { + throw new ServiceRuntimeException(e); + } + factories.add(nodeFactory); + return nodeFactory; + } + + public static NodeFactory newInstance(Map<String, Map<String, String>> attributes) { + NodeFactory nodeFactory = null; + try { + Class<?> factoryClass = getFactoryImplClass(); + nodeFactory = (NodeFactory)factoryClass.newInstance(); + nodeFactory.configure(attributes); + } catch (Exception e) { + throw new ServiceRuntimeException(e); + } + factories.add(nodeFactory); + return nodeFactory; + } + + protected Properties properties; + + protected NodeFactory() { + this.properties = new Properties(); + properties.setProperty("defaultScheme", "vm"); + properties.setProperty("defaultDomainName", "default"); + } + + public static NodeFactory newInstance(Properties configProperties) { + NodeFactory nodeFactory = null; + try { + Class<?> factoryClass = getFactoryImplClass(); + nodeFactory = (NodeFactory)factoryClass.newInstance(); + nodeFactory.properties = configProperties; + nodeFactory.configure(new HashMap<String, Map<String,String>>()); + } catch (Exception e) { + throw new ServiceRuntimeException(e); + } + factories.add(nodeFactory); + return nodeFactory; + } + + public static NodeFactory newInstance(String configURI) { + Properties properties; + if (configURI == null || configURI.length() < 1) { + return newInstance(); + } else if (configURI.startsWith("properties:")) { + try { + properties = loadProperties(configURI.substring("properties:".length())); + } catch (IOException e) { + throw new ServiceRuntimeException(e); + } + } else if (configURI.startsWith("uri:")) { + properties = parseConfigURI(configURI.substring("uri:".length())); + } else { + properties = new Properties(); + properties.setProperty("defaultDomainName", configURI); + } + return newInstance(properties); + } + + /** + * Parse the config string into a Properties object. + * The config URI has the following format: + * uri:<domainName>?name=value&... + */ + private static Properties parseConfigURI(String configURI) { + Properties properties = new Properties(); + int qm = configURI.indexOf('?'); + if (qm < 0) { + properties.setProperty("defaultDomainName", configURI); + } else { + if (qm == 0) { + properties.setProperty("defaultDomainName", "default"); + } else { + properties.setProperty("defaultDomainName", configURI.substring(0, qm)); + } + if (configURI.length() > qm+1) { + Map<String, String> params = new HashMap<String, String>(); + for (String param : configURI.substring(qm+1).split("&")) { + String[] px = param.split("="); + if (px.length == 2) { + params.put(px[0], px[1]); + } else { + params.put(px[0], ""); + } + } + for (String name : params.keySet()) { + properties.setProperty(name, params.get(name)); + } + } + } + return properties; + } + + /** + * load the properties from external URL or a relative file + * properties:<url to properties file> + */ + private static Properties loadProperties(String propsURL) throws IOException { + + Properties properties = new Properties(); + File file = new File(propsURL); + + InputStream inputStream = null; + if (file.exists()) { + inputStream = new FileInputStream(file); + } else { + URL url = null; + try { + url = new URL(propsURL); + } catch (MalformedURLException e) { + inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(propsURL); + if (inputStream == null) { + throw new IOException("File does not exist: " + propsURL + ", could not be found on the classpath and is not a valid URL: " + e); + } + } + if (inputStream == null && url != null) { + inputStream = url.openStream(); + } + } + if (inputStream != null) { + properties.load(inputStream); + inputStream.close(); + } + + return properties; + } + + /** + * Configure the NodeFactory instance + * @param attributes + */ + public void configure(Map<String, Map<String, String>> attributes) { + } + + private synchronized static Class<?> getFactoryImplClass() throws Exception { + if (factoryImplClass == null) { + // Use reflection APIs to call ServiceDiscovery to avoid hard dependency to tuscany-extensibility + try { + Class<?> discoveryClass = Class.forName("org.apache.tuscany.sca.extensibility.ServiceDiscovery"); + Object instance = discoveryClass.getMethod("getInstance").invoke(null); + Object factoryDeclaration = + discoveryClass.getMethod("getServiceDeclaration", Class.class).invoke(instance, NodeFactory.class); + if (factoryDeclaration != null) { + factoryImplClass = + (Class<?>)factoryDeclaration.getClass().getMethod("loadClass").invoke(factoryDeclaration); + } + } catch (ClassNotFoundException e) { + // Ignore + } + + if (factoryImplClass == null) { + // Fail back to default impl + String className = "org.apache.tuscany.sca.node.impl.NodeFactoryImpl"; + + factoryImplClass = Class.forName(className); + } + } + return factoryImplClass; + } + + /** + * Open a URL connection without cache + * @param url + * @return + * @throws IOException + */ + private static InputStream openStream(URL url) throws IOException { + InputStream is = null; + URLConnection connection = url.openConnection(); + connection.setUseCaches(false); + is = connection.getInputStream(); + return is; + } + + /** + * Escape the space in URL string + * @param uri + * @return + */ + private static URI createURI(String uri) { + int index = uri.indexOf(':'); + String scheme = null; + String ssp = uri; + if (index != -1) { + scheme = uri.substring(0, index); + ssp = uri.substring(index + 1); + } + try { + return new URI(scheme, ssp, null); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * Creates a new SCA node from the configuration URL + * + * @param configurationURL the URL of the node configuration which is the XML document + * that contains the URI of the composite and a collection of URLs for the contributions + * + * @return a new SCA node. + */ + public Node createNode(URL configurationURL) { + try { + InputStream is = openStream(configurationURL); + NodeConfiguration configuration = loadConfiguration(is, configurationURL); + return createNode(configuration); + } catch (IOException e) { + throw new ServiceRuntimeException(e); + } + } + + /** + * Creates a new SCA node from the XML configuration of the node + * @param is The input stream that the XML configuration can be read. The stream will be closed + * after this call. + * @return a new SCA node + */ + public Node createNode(InputStream is) { + NodeConfiguration configuration = loadConfiguration(is, null); + return createNode(configuration); + } + + /** + * Creates a new SCA node. + * + * @param deploymentCompositeURI the URI of the deployment composite. If the URI is relative, it should + * be resolved against the first contribution. Otherwise, the absolute URI is used to load the XML + * description of the composite. The deployment composite will be attached to the first contribution. + * + * @param contributions the URI of the contributions that provides the composites and related + * artifacts. If the list is empty, then we will use the thread context classloader to discover + * the contribution on the classpath + * + * @return a new SCA node. + */ + public Node createNode(String deploymentCompositeURI, Contribution... contributions) { + if (contributions == null || contributions.length == 0) { + if (deploymentCompositeURI == null || deploymentCompositeURI.indexOf(':') != -1) { + throw new ServiceRuntimeException("No SCA contribution is provided or discovered"); + } + // Try to find contributions on the classpath by the composite URI + List<String> locations = getContributionLocations(null, deploymentCompositeURI); + if (locations.isEmpty()) { + throw new ServiceRuntimeException("No SCA contributions are found on the classpath"); + } + contributions = getContributions(locations); + } + NodeConfiguration configuration = createConfiguration(contributions); + if (deploymentCompositeURI != null && configuration.getContributions().size() > 0) { + configuration.getContributions().get(0).addDeploymentComposite(createURI(deploymentCompositeURI)); + } + return createNode(configuration); + } + + public final Node createNode(URI domainRegistryURI, String... locations) { + return createNode(domainRegistryURI, null, locations); + } + + public final Node createNode(URI domainRegistryURI, String deploymentCompositeURI, String[] locations) { + Contribution[] contributions = getContributions(Arrays.asList(locations)); + NodeConfiguration configuration = createConfiguration(contributions); + if (deploymentCompositeURI != null && configuration.getContributions().size() > 0) { + configuration.getContributions().get(0).addDeploymentComposite(createURI(deploymentCompositeURI)); + } + configuration.setDomainRegistryURI(domainRegistryURI.toString()); + configuration.setDomainURI(getDomainURI(domainRegistryURI)); + return createNode(configuration); + } + + /** + * TODO: cleanup node use of registry uri, domain uri, and domain name + * so that its consistent across the code base + */ + public static String getDomainURI(URI configURI) { + String s = configURI.getHost(); + if (s == null) { + s = configURI.getSchemeSpecificPart(); + if (s != null) { + if (s.indexOf('?') > -1) { + s = s.substring(0, s.indexOf('?')); + } + } + } + return s; + } + + /** + * The following methods are used by the node launcher + */ + public final Node createNode(String deploymentCompositeURI, String[] uris, String locations[]) { + return createNode(deploymentCompositeURI, getContributions(Arrays.asList(uris), Arrays.asList(locations))); + } + + public final Node createNode(String deploymentCompositeURI, String locations[]) { + return createNode(deploymentCompositeURI, getContributions(Arrays.asList(locations))); + } + + public final Node createNode(Reader deploymentCompositeContent, String[] uris, String locations[]) { + return createNode(deploymentCompositeContent, getContributions(Arrays.asList(uris), Arrays.asList(locations))); + } + + public final Node createNode(String compositeURI, ClassLoader classLoader) { + List<String> locations = ContributionLocationHelper.getContributionLocations(classLoader, compositeURI); + return createNode(compositeURI, locations.toArray(new String[locations.size()])); + } + /** + * ------------------- end of methods ----------------- + */ + + /** + * Create a new SCA node using the list of SCA contributions + * @param contributions + * @return + */ + public Node createNode(Contribution... contributions) { + NodeConfiguration configuration = createConfiguration(contributions); + return createNode(configuration); + } + + + /** + * Creates a new SCA node. + * + * @param compositeContent the XML content of the deployment composite + * @param contributions the URI of the contributions that provides the composites and related artifacts + * @return a new SCA node. + */ + public Node createNode(InputStream compositeContent, Contribution... contributions) { + NodeConfiguration configuration = createConfiguration(contributions); + if (compositeContent != null && configuration.getContributions().size() > 0) { + configuration.getContributions().get(0).addDeploymentComposite(compositeContent); + } + return createNode(configuration); + } + + /** + * Creates a new SCA node. + * + * @param compositeContent the XML content of the deployment composite + * @param contributions the URI of the contributions that provides the composites and related artifacts + * @return a new SCA node. + */ + public Node createNode(Reader compositeContent, Contribution... contributions) { + NodeConfiguration configuration = createConfiguration(contributions); + if (compositeContent != null && configuration.getContributions().size() > 0) { + configuration.getContributions().get(0).addDeploymentComposite(compositeContent); + } + return createNode(configuration); + } + + /** + * Creates a new SCA node using defaults for the contribution location and deployable composites. + * By default, it uses the Thread context classloader to find META-INF/sca-contribution.xml or + * META-INF/sca-contribution-generated.xml on the classpath. The locations that contain such resources + * are taken as the SCA contributions. + * + * @return a new SCA node. + */ + public Node createNode() { + List<String> locations = new ArrayList<String>(); + locations.addAll(getContributionLocations(null, SCA_CONTRIBUTION_META)); + locations.addAll(getContributionLocations(null, SCA_CONTRIBUTION_GENERATED_META)); + if (locations.isEmpty()) { + throw new ServiceRuntimeException("No SCA contributions are found on the classpath"); + } + Contribution[] contributions = getContributions(locations); + return createNode(contributions); + } + + private NodeConfiguration createConfiguration(Contribution... contributions) { + NodeConfigurationFactory factory = this; + NodeConfiguration configuration = factory.createNodeConfiguration(); + if (properties.getProperty("defaultDomainName") != null) { + configuration.setDomainURI(properties.getProperty("defaultDomainName")); + } + // Make sure a unique node URI is created for the same node factory + configuration.setURI(generateNodeURI()); + if (contributions != null) { + for (Contribution c : contributions) { + configuration.addContribution(c.getURI(), c.getLocation()); + } + } + return configuration; + } + + private static int count = 0; + + protected synchronized String generateNodeURI() { + return Node.DEFAULT_NODE_URI + (count++); + } + + private Contribution[] getContributions(List<String> locations) { + Contribution[] contributions = new Contribution[locations.size()]; + int index = 0; + for (String location : locations) { + contributions[index++] = new Contribution(location, location); + } + return contributions; + } + + private Contribution[] getContributions(List<String> uris, List<String> locations) { + if (uris.size() != locations.size()) { + throw new IllegalArgumentException("The number of URIs does not match the number of locations"); + } + Contribution[] contributions = new Contribution[locations.size()]; + for (int i = 0, n = locations.size(); i < n; i++) { + contributions[i] = new Contribution(uris.get(i), locations.get(i)); + } + return contributions; + } + + public void destroy() { + count = 0; + instance = null; + factories.remove(this); + } + + public static List<NodeFactory> getNodeFactories() { + return factories; + } + + /** + * Create a new SCA node based on the configuration + * @param configuration The configuration of a node + * @return The SCA node + */ + public abstract Node createNode(NodeConfiguration configuration); + + /** + * Create an SCA node from a list of pre-built o.a.t.sca.contribution.Contribution objects. + * Pass java.lang.Objects for now as this class doesn't have direct dependencies on + * o.a.t.sca.contribution.Contribution. + */ + public abstract Node createNode(List<?> contributions); + + /** + * Create the node configuration from the XML document + * @param configuration The input stream of the XML document + * @return The node configuration + */ + public abstract NodeConfiguration loadConfiguration(InputStream xml, URL base); + + public abstract <T> T getExtensionPointRegistry(); + + public abstract void init(); +} |