/* * 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.impl; import java.io.IOException; import java.io.InputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.URI; import java.net.URL; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.xml.namespace.QName; import org.apache.tuscany.sca.assembly.AssemblyFactory; import org.apache.tuscany.sca.assembly.Binding; import org.apache.tuscany.sca.assembly.Component; import org.apache.tuscany.sca.assembly.ComponentReference; import org.apache.tuscany.sca.assembly.ComponentService; import org.apache.tuscany.sca.assembly.Composite; import org.apache.tuscany.sca.assembly.CompositeService; import org.apache.tuscany.sca.assembly.SCABinding; import org.apache.tuscany.sca.assembly.SCABindingFactory; import org.apache.tuscany.sca.assembly.xml.Constants; import org.apache.tuscany.sca.contribution.Contribution; import org.apache.tuscany.sca.contribution.ModelFactoryExtensionPoint; import org.apache.tuscany.sca.contribution.resolver.impl.ModelResolverImpl; import org.apache.tuscany.sca.contribution.service.ContributionService; import org.apache.tuscany.sca.core.assembly.ActivationException; import org.apache.tuscany.sca.core.context.ServiceReferenceImpl; import org.apache.tuscany.sca.node.ComponentManager; import org.apache.tuscany.sca.node.ContributionManager; import org.apache.tuscany.sca.node.SCANode; import org.apache.tuscany.sca.node.NodeManagerInitService; import org.apache.tuscany.sca.domain.SCADomain; import org.apache.tuscany.sca.domain.DomainManagerService; import org.apache.tuscany.sca.domain.SCADomainService; import org.apache.tuscany.sca.host.embedded.impl.EmbeddedSCADomain; import org.apache.tuscany.sca.host.embedded.impl.ReallySmallRuntime; import org.apache.tuscany.sca.interfacedef.InterfaceContract; import org.apache.tuscany.sca.interfacedef.java.JavaInterfaceFactory; import org.apache.tuscany.sca.runtime.RuntimeComponent; import org.apache.tuscany.sca.runtime.RuntimeComponentContext; import org.apache.tuscany.sca.runtime.RuntimeComponentReference; import org.osoa.sca.CallableReference; import org.osoa.sca.ServiceReference; import org.osoa.sca.ServiceRuntimeException; /** * A local representation of the sca domain running on a single node * * @version $Rev: 552343 $ $Date: 2007-09-09 23:54:46 +0100 (Sun, 09 Sep 2007) $ */ public class SCANodeImpl extends SCADomain implements SCANode { private final static Logger logger = Logger.getLogger(SCANodeImpl.class.getName()); public final static String LOCAL_DOMAIN_URI = "standalonedomain"; public final static String LOCAL_NODE_URI = "standalonenode"; private boolean isStandalone = false; // the domain used by the node to talk to the rest of the domain private EmbeddedSCADomain managementRuntime; // class loader used to get the runtime going private ClassLoader domainClassLoader; // class loader used to get application resources private ClassLoader applicationClassLoader; // representation of the private state of the node that the domain is running on private String domainUri; private URL domainUrl; private String nodeUri; private URL nodeUrl; private ReallySmallRuntime nodeRuntime; private Composite nodeComposite; // the managers used to control the domain node private ComponentManagerServiceImpl componentManager; private ContributionManagerImpl contributionManager; // the proxies to the domain private SCADomainService scaDomain; private DomainManagerService domainManager; private NodeManagerInitService nodeManagerInit; // methods defined on the implementation only /** * Default constructor creates a standalone node with no connectivity to a wider * domain and no local web page. */ public SCANodeImpl() throws ActivationException { this.domainUri = LOCAL_DOMAIN_URI ; this.nodeUri = LOCAL_NODE_URI; this.domainClassLoader = SCANodeImpl.class.getClassLoader(); this.applicationClassLoader = Thread.currentThread().getContextClassLoader(); this.isStandalone = true; init(); } /** * Creates a node connected to a wider domain. To find its place in the domain * node and domain identifiers must be provided. * * @param domainUri - identifies what host and port the domain service is running on, e.g. http://localhost:8081 * @param nodeUri - if this is a url it is assumed that this will be used as root url for management components, e.g. http://localhost:8082 * @throws ActivationException */ public SCANodeImpl(String domainUri, String nodeUri) throws ActivationException { this.domainUri = domainUri; this.nodeUri = nodeUri; this.domainClassLoader = SCANodeImpl.class.getClassLoader(); this.applicationClassLoader = Thread.currentThread().getContextClassLoader(); this.isStandalone = LOCAL_DOMAIN_URI.equals(domainUri); init(); } /** * Creates a node connected to a wider domain and allows a classpath to be specified. * To find its place in the domain node and domain identifiers must be provided. * * @param domainUri - identifies what host and port the domain service is running on, e.g. http://localhost:8081 * @param nodeUri - if this is a url it is assumed that this will be used as root url for management components, e.g. http://localhost:8082 * @param cl - the ClassLoader to use for loading system resources for the node * @throws ActivationException */ public SCANodeImpl(String domainUri, String nodeUri, ClassLoader cl) throws ActivationException { this.domainUri = domainUri; this.nodeUri = nodeUri; this.domainClassLoader = cl; this.applicationClassLoader = Thread.currentThread().getContextClassLoader(); this.isStandalone = LOCAL_DOMAIN_URI.equals(domainUri); init(); } /** * Creates a node connected to a wider domain and allows a classpath to be specified. * To find its place in the domain node and domain identifiers must be provided. * * @param domainUri - identifies what host and port the domain service is running on, e.g. http://localhost:8081 * @param nodeUri - if this is a url it is assumed that this will be used as root url for management components, e.g. http://localhost:8082 * @param cl - the ClassLoader to use for loading system resources for the node * @param applicationCl - the ClassLoader to use for loading application resources for the node * @throws ActivationException */ public SCANodeImpl(String domainUri, String nodeUri, ClassLoader cl, ClassLoader applicationCl, String contributionPath, String[] composites) throws ActivationException { this.domainUri = domainUri; this.nodeUri = nodeUri; this.domainClassLoader = cl; this.applicationClassLoader = applicationCl; this.isStandalone = LOCAL_DOMAIN_URI.equals(domainUri); init(); start(); try { URL contributionURL = SCANodeUtil.findContributionURLFromCompositeNameOrPath(applicationClassLoader, contributionPath, composites); contributionManager.addContribution(contributionURL); if (composites.length > 0 ){ for(int i = 0; i < composites.length; i++) { contributionManager.addComposite(composites[i]); contributionManager.startComposite(composites[i]); } } else { contributionManager.addAllComposites(contributionURL); contributionManager.startAllComposites(contributionURL); } } catch(Exception ex) { throw new ActivationException(ex); } } /** * Work out if we are representing a domain in memory or can go out to the network to * get domain information. This all depends on whether there is a management * composite on the classpath */ private void init() throws ActivationException { try { // create a node runtime for the domain contributions to run on nodeRuntime = new ReallySmallRuntime(domainClassLoader); // Check if node has been given a domain name to connect to if (isStandalone) { logger.log(Level.INFO, "Domain node will be started stand-alone as node and domain URIs are not provided"); managementRuntime = null; scaDomain = null; } else { // check where domain and node uris are urls, they will be used to configure various // endpoints if they are URI tmpURI; try { tmpURI = new URI(domainUri); if (tmpURI.isAbsolute()){ domainUrl = tmpURI.toURL(); } } catch(Exception ex) { throw new ActivationException("domain uri " + domainUri + "must be a valid url"); } try { tmpURI = new URI(nodeUri); if (tmpURI.isAbsolute()){ nodeUrl = tmpURI.toURL(); } } catch(Exception ex) { nodeUrl = null; } createManagementNode(); } } catch(ActivationException ex) { throw ex; } catch(Exception ex) { throw new ActivationException(ex); } } private void createManagementNode() throws ActivationException { try { // create a runtime for components to run on that will be used for talking to the // rest of the domain. The components are defined in the node.composite file String nodeCompositeName = "node.composite"; URL contributionURL = SCANodeUtil.findContributionURLFromCompositeNameOrPath(domainClassLoader, null, new String[]{nodeCompositeName} ); if ( contributionURL != null ){ logger.log(Level.INFO, "Node management configured from " + contributionURL); // start a local domain in order to talk to the logical domain managementRuntime = new EmbeddedSCADomain(domainClassLoader, "node"); managementRuntime.start(); // add node composite to the management domain ContributionService contributionService = managementRuntime.getContributionService(); Contribution contribution = null; contribution = contributionService.contribute(nodeUri, contributionURL, false); if (contribution.getDeployables().size() != 0) { Composite composite = contribution.getDeployables().get(0); managementRuntime.getDomainComposite().getIncludes().add(composite); managementRuntime.getCompositeBuilder().build(composite); // deal with the special case of registering the node manager service // in service discovery. It's not on an SCA binding. // TODO - really want to be able to hand out service references but they // don't serialize out over web services yet. SCANodeUtil.fixUpNodeServiceUrls(managementRuntime.getDomainComposite().getIncludes().get(0).getComponents(), nodeUrl); SCANodeUtil.fixUpNodeReferenceUrls(managementRuntime.getDomainComposite().getIncludes().get(0).getComponents(), domainUrl); managementRuntime.getCompositeActivator().activate(composite); managementRuntime.getCompositeActivator().start(composite); // get the management components out of the domain so that they // can be configured/used. scaDomain = managementRuntime.getService(SCADomainService.class, "SCADomainComponent"); domainManager = managementRuntime.getService(DomainManagerService.class, "DomainManagerComponent"); nodeManagerInit = managementRuntime.getService(NodeManagerInitService.class, "NodeManagerComponent/NodeManagerInitService"); // Now get the uri back out of the component now it has been built and started // TODO - this doesn't pick up the url from external hosting environments String nodeManagerUrl = SCANodeUtil.getNodeManagerServiceUrl(managementRuntime.getDomainComposite().getIncludes().get(0).getComponents()); if (nodeManagerUrl != null) { if (isStandalone == false){ try { scaDomain.registerServiceEndpoint(domainUri, nodeUri, nodeUri + "NodeManagerService", "", nodeManagerUrl); } catch(Exception ex) { logger.log(Level.SEVERE, "Can't connect to domain manager at: " + domainUrl); throw new ActivationException(ex); } } } } else { throw new ActivationException("Node management contribution " + contributionURL + " found but could not be loaded"); } } else { throw new ActivationException("Node management contribution " + nodeCompositeName + " not found on the classpath"); } } catch(ActivationException ex) { throw ex; } catch(Exception ex) { throw new ActivationException(ex); } } // methods that implement interfaces public void start() throws ActivationException { // Start the runtime nodeRuntime.start(); // Create an in-memory domain level composite AssemblyFactory assemblyFactory = nodeRuntime.getAssemblyFactory(); nodeComposite = assemblyFactory.createComposite(); nodeComposite.setName(new QName(Constants.SCA10_NS, "domain")); nodeComposite.setURI(domainUri); // add the top level composite into the composite activator nodeRuntime.getCompositeActivator().setDomainComposite(nodeComposite); // make the domain available to the model. // TODO - No sure how this should be done properly. As a nod to this though // I have a domain factory which always returns the same domain // object. I.e. this node ModelFactoryExtensionPoint factories = nodeRuntime.getExtensionPointRegistry().getExtensionPoint(ModelFactoryExtensionPoint.class); NodeFactoryImpl domainFactory = new NodeFactoryImpl(this); factories.addFactory(domainFactory); // create the domain node managers componentManager = new ComponentManagerServiceImpl(domainUri, nodeUri, nodeComposite, nodeRuntime); contributionManager = new ContributionManagerImpl(domainUri, nodeUri, nodeComposite, nodeRuntime, applicationClassLoader, null); if (isStandalone == false){ // pass this object into the node manager nodeManagerInit.setNode((SCANode)this); try { // go out and add this node to the wider domain domainManager.registerNode(domainUri, nodeUri); } catch(Exception ex) { logger.log(Level.SEVERE, "Can't connect to domain manager at: " + domainUrl); throw new ActivationException(ex); } } } @Override public void close() { try { stop(); } catch (Exception ex) { throw new ServiceRuntimeException(ex); } } public void stop() throws ActivationException { // stop the components // remove contributions // Stop the node nodeRuntime.stop(); // Cleanup the top level composite nodeComposite = null; // remove the manager objects // go out and remove this node from the wider domain if (isStandalone == false){ try { domainManager.removeNode(domainUri, nodeUri); } catch(Exception ex) { logger.log(Level.SEVERE, "Can't connect to domain manager at: " + domainUrl); throw new ActivationException(ex); } } } public String getURI(){ return domainUri; } public String getDomainURI(){ return domainUri; } public String getNodeURI(){ return nodeUri; } public URL getDomainURL(){ return domainUrl; } public URL getNodeURL(){ return nodeUrl; } public ComponentManager getComponentManager() { return componentManager; } public ContributionManager getContributionManager() { return contributionManager; } /** * Return an interface for registering local services and for * finding remote services * * @return The service discovery interface */ public SCADomainService getDomainService(){ return scaDomain; } public > R cast(B target) throws IllegalArgumentException { return (R)nodeRuntime.getProxyFactory().cast(target); } public B getService(Class businessInterface, String serviceName) { ServiceReference serviceReference = getServiceReference(businessInterface, serviceName); if (serviceReference == null) { throw new ServiceRuntimeException("Service not found: " + serviceName); } return serviceReference.getService(); } private ServiceReference createServiceReference(Class businessInterface, String targetURI) { try { AssemblyFactory assemblyFactory = nodeRuntime.getAssemblyFactory(); Composite composite = assemblyFactory.createComposite(); composite.setName(new QName(Constants.SCA10_TUSCANY_NS, "default")); RuntimeComponent component = (RuntimeComponent)assemblyFactory.createComponent(); component.setName("default"); component.setURI("default"); nodeRuntime.getCompositeActivator().configureComponentContext(component); composite.getComponents().add(component); RuntimeComponentReference reference = (RuntimeComponentReference)assemblyFactory.createComponentReference(); reference.setName("default"); ModelFactoryExtensionPoint factories = nodeRuntime.getExtensionPointRegistry().getExtensionPoint(ModelFactoryExtensionPoint.class); JavaInterfaceFactory javaInterfaceFactory = factories.getFactory(JavaInterfaceFactory.class); InterfaceContract interfaceContract = javaInterfaceFactory.createJavaInterfaceContract(); interfaceContract.setInterface(javaInterfaceFactory.createJavaInterface(businessInterface)); reference.setInterfaceContract(interfaceContract); component.getReferences().add(reference); reference.setComponent(component); SCABindingFactory scaBindingFactory = factories.getFactory(SCABindingFactory.class); SCABinding binding = scaBindingFactory.createSCABinding(); binding.setURI(targetURI); reference.getBindings().add(binding); return new ServiceReferenceImpl(businessInterface, component, reference, binding, nodeRuntime .getProxyFactory(), nodeRuntime.getCompositeActivator()); } catch (Exception e) { throw new ServiceRuntimeException(e); } } public ServiceReference getServiceReference(Class businessInterface, String name) { // Extract the component name String componentName; String serviceName; int i = name.indexOf('/'); if (i != -1) { componentName = name.substring(0, i); serviceName = name.substring(i + 1); } else { componentName = name; serviceName = null; } // Lookup the component in the domain Component component = componentManager.getComponent(componentName); if (component == null) { // The component is not local in the partition, try to create a remote service ref return createServiceReference(businessInterface, name); } RuntimeComponentContext componentContext = null; // If the component is a composite, then we need to find the // non-composite component that provides the requested service if (component.getImplementation() instanceof Composite) { for (ComponentService componentService : component.getServices()) { if (serviceName == null || serviceName.equals(componentService.getName())) { CompositeService compositeService = (CompositeService)componentService.getService(); if (compositeService != null) { if (serviceName != null) { serviceName = "$promoted$." + serviceName; } componentContext = ((RuntimeComponent)compositeService.getPromotedComponent()).getComponentContext(); return componentContext.createSelfReference(businessInterface, compositeService .getPromotedService()); } break; } } // No matching service is found throw new ServiceRuntimeException("Composite service not found: " + name); } else { componentContext = ((RuntimeComponent)component).getComponentContext(); if (serviceName != null) { return componentContext.createSelfReference(businessInterface, serviceName); } else { return componentContext.createSelfReference(businessInterface); } } } }