/* * BEGIN_HEADER - DO NOT EDIT * * The contents of this file are subject to the terms * of the Common Development and Distribution License * (the "License"). You may not use this file except * in compliance with the License. * * You can obtain a copy of the license at * https://open-jbi-components.dev.java.net/public/CDDLv1.0.html. * See the License for the specific language governing * permissions and limitations under the License. * * When distributing Covered Code, include this CDDL * HEADER in each file and include the License file at * https://open-jbi-components.dev.java.net/public/CDDLv1.0.html. * If applicable add the following below this CDDL HEADER, * with the fields enclosed by brackets "[]" replaced with * your own identifying information: Portions Copyright * [year] [name of copyright owner] */ /* * @(#)ServiceUnitImpl.java * * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. * * END_HEADER - DO NOT EDIT */ package com.sun.jbi.httpsoapbc; import com.ibm.wsdl.Constants; import com.ibm.wsdl.extensions.schema.SchemaConstants; import com.ibm.wsdl.util.xml.DOMUtils; import com.ibm.wsdl.util.xml.QNameUtils; // common-util and qos imports import com.sun.jbi.common.descriptor.EndpointInfo; import com.sun.jbi.common.qos.ServiceQuality; import com.sun.jbi.common.qos.messaging.MessagingChannel; import com.sun.jbi.common.qos.redelivery.RedeliveryConfig; import com.sun.jbi.common.qos.throttling.ThrottlingConfig; import com.sun.jbi.httpsoapbc.configuration.RuntimeConfigurationMBean; import com.sun.jbi.httpsoapbc.descriptors.HttpSoapEndpointIdentifier; import com.sun.jbi.httpsoapbc.descriptors.HttpSoapSUDescriptorParser; import com.sun.jbi.httpsoapbc.extensions.SOAPExtSerializer; import com.sun.jbi.httpsoapbc.security.api.ServiceUnitSecurityConfig; import com.sun.jbi.httpsoapbc.security.impl.ServiceUnitSecurityConfigImpl; import com.sun.jbi.httpsoapbc.security.impl.CredentialValidatorManager; import com.sun.jbi.httpsoapbc.validator.HttpSoapValidatorRegistry; import com.sun.jbi.httpsoapbc.util.HttpUrlResolverUtil; import com.sun.jbi.httpsoapbc.util.TransformerPool; import com.sun.jbi.httpsoapbc.validator.WSDLExtensionValidator; import com.sun.jbi.internationalization.Messages; import com.sun.jbi.wsdlvalidator.ValidatingWSDLReader; import com.sun.jbi.wsdlvalidator.factory.ValidatingWSDLFactory; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.net.URI; import java.net.URL; import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.logging.Logger; import java.util.logging.Level; import java.util.Map; import javax.jbi.component.ComponentContext; import javax.jbi.JBIException; import javax.jbi.management.DeploymentException; import javax.wsdl.Definition; import javax.wsdl.extensions.ExtensibilityElement; import javax.wsdl.extensions.ExtensionRegistry; import javax.wsdl.extensions.schema.Schema; import javax.wsdl.extensions.schema.SchemaReference; import javax.wsdl.factory.WSDLFactory; import javax.wsdl.Import; import javax.wsdl.Types; import javax.wsdl.xml.WSDLWriter; import javax.xml.namespace.QName; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.Transformer; import org.apache.xml.resolver.tools.CatalogResolver; import org.apache.xml.resolver.CatalogManager; import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * ServiceUnitImpl represents a ServiceUnit deployed in a ServiceUnitManager. *

* The initialization of a ServiceUnit and its respective Endpoints depends * on two main configuration items: the jbi.xml * file and the WSDL(s) representing the Service Unit. Initialization of the * ServiceUnit is done through the Observer pattern when parsing each of the * WSDL files. This class only cares about WSDLs containing the extensibility * elements pertinent to this Binding Component. *

* By encapsulating the initialization logic away from * model of ServiceUnitManagers, ServiceUnits, and Endpoints, we can more * easily change how models are initialized without breaking too much. *

* */ public class ServiceUnitImpl implements ServiceUnit { private static final Logger mLogger = Messages.getLogger(ServiceUnitImpl.class); private static final Messages mMessages = Messages.getMessages(ServiceUnitImpl.class); private String mId; private String mComponentName; private ComponentContext mContext; private RuntimeConfigurationMBean mRuntimeConfig; private boolean endpointsCreated; private String mRootPath; private Map mEndpoints; private Collection mEndpointChangeListeners; private Map mModifiedWSDLs; private Map mModifiedSchemas; private Map mDefs; private TransformerPool mTransformerPool; private CredentialValidatorManager mCredValidatorMgr; private MessagingChannel mChannel; private CatalogManager mCatalogManager; private EntityResolver mResolver; public ServiceUnitImpl(String id, ComponentContext context, RuntimeConfigurationMBean runtimeConfig, Collection endpointChangeListeners, CredentialValidatorManager credValidatorMgr) { mId = id; mContext = context; mRuntimeConfig = runtimeConfig; mEndpoints = new HashMap(); mEndpointChangeListeners = endpointChangeListeners; mModifiedWSDLs = new HashMap(); mModifiedSchemas = new HashMap(); mTransformerPool = new TransformerPool(); mComponentName = context.getComponentName(); mCredValidatorMgr = credValidatorMgr; mChannel = HttpSoapComponentContext.getInstance().getBindingChannel(); } ServiceUnitImpl(String id, ComponentContext context, RuntimeConfigurationMBean runtimeConfig, Collection endpointChangeListeners, CredentialValidatorManager credValidatorMgr, Map endpoints) { this (id, context, runtimeConfig, endpointChangeListeners, credValidatorMgr); mEndpoints = endpoints; } ServiceUnitImpl(String id, ComponentContext context, RuntimeConfigurationMBean runtimeConfig, Collection endpointChangeListeners, CredentialValidatorManager credValidatorMgr, Map endpoints, MessagingChannel channel) { this (id, context, runtimeConfig, endpointChangeListeners, credValidatorMgr); mChannel = channel; mEndpoints = endpoints; } //////// // // ServiceUnit Interface Methods // //////// /** * Retrieves the Id of this ServiceUnit. * * @return the name of the Service as a QName */ public String getServiceUnitId() { return mId; } /** * Deploy the ServiceUnit. Useful for validating the ServiceUnit during * the deployment phase * * @param serviceUnitRootPath path to the ServiceUnit * @exception JBIException if unable to initialize this ServiceUnit */ public void deploy(String serviceUnitRootPath) throws JBIException { createEndpoints(serviceUnitRootPath, false); } /** * Initializes the ServiceUnit. Parses the serviceUnitRootPath * to create the Endpointobjects. A ServiceUnit may have * mutiple WSDLs, each with multiple endpoints that we care about. *

* This method will initialize the WSDLs, transforming it into a * set of Endpoints * * @param serviceUnitRootPath path to the ServiceUnit * @exception JBIException if unable to initialize this ServiceUnit */ public void init(String serviceUnitRootPath) throws JBIException { // get the map of QoS attributes associated with the endpoints defined // in the service unit mRootPath = serviceUnitRootPath; // look up and install the QoS configurations mChannel.installServiceQualities(mId, serviceUnitRootPath); // set up the entity resolver here File catalog = new File(mRootPath + File.separator + "META-INF" + File.separator + "catalog.xml"); if (catalog.exists()) { mCatalogManager = new CatalogManager(); mCatalogManager.setIgnoreMissingProperties(true); mCatalogManager.setCatalogFiles(catalog.getAbsolutePath()); mCatalogManager.setRelativeCatalogs(true); mCatalogManager.setUseStaticCatalog(false); mResolver = new CatalogResolver(mCatalogManager); } //init can be called during the reboot of the appserver or //during the deploy/start/stop/shutdown of the SU. //To optimize performance, make sure endpoints are created //and validated only once during the life cycle of the SU. if (!endpointsCreated) { createEndpoints(serviceUnitRootPath, true); } Iterator it = mEndpoints.values().iterator(); Endpoint currEndpoint = null; EndpointChangeListener listener; Iterator it2 = null; try { while (it.hasNext()) { currEndpoint = (Endpoint)it.next(); // start provisioning currEndpoint.activate(mContext); // Let all our EndpointChangeListeners know what's happening it2 = mEndpointChangeListeners.iterator(); while (it2.hasNext()) { listener = (EndpointChangeListener)it2.next(); listener.endpointInitialized(currEndpoint); } } } catch (Exception ex) { throw new JBIException(ex); } } /** * Starts this ServiceUnit. This involves activating * all Endpoints that are part of this ServiceUnit. *

* TODO: What should happen if not all the Endpoints * can be activated? Should I deactivate them or just leave * them? For now, I'm going to assume that this method is * transactional. Either all the Endpoints activate or none. * If any one fails to activate, the other activated Endpoints * will be deactivated. * * @exception JBIException if a any Endpoint fails * to activate */ public void start() throws JBIException { HashSet activatedEndpoints = new HashSet(); Iterator it = mEndpoints.values().iterator(); Endpoint currEndpoint = null; try { while (it.hasNext()) { currEndpoint = (Endpoint)it.next(); //currEndpoint.activate(mContext); /* List wsdlNamespaces = currEndpoint.getImportedWSDLNamespaces(); List xsdNamespaces = currEndpoint.getImportedXSDNamespaces(); for (int ii = 0; ii < wsdlNamespaces.size(); ii++) { String aNamespace = (String)wsdlNamespaces.get(ii); if (mModifiedWSDLNamespacesMap.get(aNamespace) != null) { currEndpoint.addImportedWSDLDefinition(aNamespace, (Definition)mModifiedWSDLNamespacesMap.get(aNamespace)); } } for (int ii = 0; ii < xsdNamespaces.size(); ii++) { String aNamespace = (String)xsdNamespaces.get(ii); if (mModifiedXSDNamespacesMap.get(aNamespace) != null) { currEndpoint.addImportedXSDSchema(aNamespace, (Element)mModifiedXSDNamespacesMap.get(aNamespace)); } }*/ activatedEndpoints.add(currEndpoint); // Let all our EndpointChangeListeners know what's happening Iterator it2 = mEndpointChangeListeners.iterator(); while (it2.hasNext()) { EndpointChangeListener listener = (EndpointChangeListener)it2.next(); listener.endpointActivated(currEndpoint); } } } catch (Exception ex) { if (currEndpoint != null) { String text = mMessages.getString("HTTPBC-E00301.Endpoint_activate_failed", new Object[] { currEndpoint.getUniqueName(), ex.getLocalizedMessage() }); mLogger.log(Level.SEVERE, text, ex); // remove the "bad" endpoint from the list mEndpoints.remove(currEndpoint.getUniqueName()); } if (mLogger.isLoggable(Level.FINE)) { mLogger.log(Level.FINE, "HTTPBC-R00305.Deactivating_endpoints_incomplete_activation"); } Iterator it2 = activatedEndpoints.iterator(); while (it2.hasNext()) { ((Endpoint)it2.next()).deactivate(mContext); } throw new JBIException(ex); } } /** * Stops this ServiceUnit. This involves deactivating * all Endpoints that are part of this ServiceUnit; *

* TODO: What should happen if not all Endpoints deactivate? * Unlike the activate() method, I'm NOT going to assume * this is transactional. It seems silly to deactivate a number of * Endpoint, and if one fails, re-activate them. I'll just throw * an error, and have the user decide how to deal with it. * * @exception JBIException if any Endpoint fails * to deactivate */ public void stop() throws JBIException { try { Iterator it = mEndpoints.values().iterator(); while (it.hasNext()) { Endpoint endpoint = (Endpoint)it.next(); //endpoint.deactivate(mContext); // Let all our EndpointChangeListeners know what's happening Iterator it2 = mEndpointChangeListeners.iterator(); while (it2.hasNext()) { EndpointChangeListener listener = (EndpointChangeListener)it2.next(); listener.endpointDeactivated(endpoint); } } } catch (Exception ex) { throw new JBIException(ex); } } /** * Shuts down this ServiceUnit * * @exception JBIException if the ServiceUnit fails to shutdown */ public void shutdown() throws JBIException { try { // remove the QoS attributes for the undeployed endpoints mChannel.uninstallServiceQualities(mId); Iterator it = mEndpoints.values().iterator(); Endpoint currEndpoint; while (it.hasNext()) { currEndpoint = (Endpoint)it.next(); // stop provisioning currEndpoint.deactivate(mContext); // Let all our EndpointChangeListeners know what's happening Iterator it2 = mEndpointChangeListeners.iterator(); while (it2.hasNext()) { EndpointChangeListener listener = (EndpointChangeListener)it2.next(); listener.endpointShutdown(currEndpoint); } } } catch (Exception ex) { throw new JBIException(ex); } finally { endpointsCreated = false; // clean up all the cached endpoints mEndpoints.clear(); // clean up all the modified WSDLs mModifiedWSDLs.clear(); } } /** * Undeploy the ServiceUnit. Useful for cleaning up any lingering state. * * @param serviceUnitRootPath path to the ServiceUnit * @exception JBIException if unable to initialize this ServiceUnit */ public void undeploy(String serviceUnitRootPath) throws JBIException { } /** * Retrieves the Endpoints handled by this ServiceUnit. * * @return the map of Endpoints */ public Map getEndpoints() { return mEndpoints; } private void createEndpoints(String serviceUnitRootPath, boolean resolveTokens) throws JBIException { // Initialize the endpoints for this ServiceUnit. Basically sets // up the static configuration information for each Endpoint. try { HttpSoapSUDescriptorParser descSupport = new HttpSoapSUDescriptorParser(serviceUnitRootPath); HttpSoapEndpointIdentifier[] svcs = descSupport.getServices(); if (svcs.length == 0) { mLogger.log(Level.WARNING, "HTTPBC-W00306.No_endpoints_to_create", new Object[] { mComponentName, mId } ); return; } Map appConfigObjectMap = mRuntimeConfig.retrieveApplicationConfigurationsMap(); // Read our WSDLS File dir = new File(serviceUnitRootPath); ValidatingWSDLFactory wsdlFactory = new ValidatingWSDLFactory(); ValidatingWSDLReader reader = (ValidatingWSDLReader)wsdlFactory.newWSDLReader(dir); // Suppress WSDL4j System.out logs and control the logging based on the current // logger logging level setting if (mLogger.isLoggable(Level.FINE)) { reader.setFeature(com.ibm.wsdl.Constants.FEATURE_VERBOSE, true); } else { reader.setFeature(com.ibm.wsdl.Constants.FEATURE_VERBOSE, false); } // Populate our reader with the right extensionRegistry ExtensionRegistry registry = wsdlFactory.newPopulatedExtensionRegistry(); SOAPExtSerializer serializer = new SOAPExtSerializer(mRuntimeConfig, resolveTokens); serializer.registerSerializer(registry); reader.setExtensionRegistry(registry); reader.setValidatorRegistry(new HttpSoapValidatorRegistry(mRuntimeConfig, resolveTokens)); mDefs = reader.readWSDL(dir); WSDLExtensionValidator extValidator = new WSDLExtensionValidator(mDefs, svcs); extValidator.validate(); if (!resolveTokens) { // we are done with loading the tokens // Now we will add the application configuration names defined // in jbi.xml to the application configuration composite data // structure in the MBean, if they are not already there. for (int ii = 0; ii < svcs.length; ii++) { HttpSoapEndpointIdentifier epDesc = svcs[ii]; String configName = epDesc.getApplicationConfigurationName(); // this application configuration name is not yet defined in the config MBean, // add it to the application configuration data structure if (configName != null && !"".equals(configName) && !appConfigObjectMap.containsKey(configName)) { appConfigObjectMap.put(configName, ""); } } return; } //Load the security configuration ServiceUnitSecurityConfig suSecurityConfig = new ServiceUnitSecurityConfigImpl(serviceUnitRootPath, mId); HttpUrlResolverUtil urlResolverUtil = new HttpUrlResolverUtil(mRuntimeConfig.retrieveApplicationVariablesMap()); Iterator it = mDefs.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry)it.next(); String path = (String)entry.getKey(); Definition def = (Definition)entry.getValue(); for (int ii = 0; ii < svcs.length; ii++) { boolean hasAppObject = false; HttpSoapEndpointIdentifier epDesc = svcs[ii]; if (epDesc == null) continue; String configName = epDesc.getApplicationConfigurationName(); URL urlLocation = null; if (configName != null && !"".equals(configName)) { // there is a config object defined for the endpoint // use the URL defined for the config object instead // of the one defined in the WSDL hasAppObject = true; String location = (String) appConfigObjectMap.get(configName); if (location == null) { throw new JBIException(mMessages.getString("HTTPBC-E00360.Application_config_object_not_defined", configName)); } try { String newLocation = urlResolverUtil.resolveEmbeddedTokensInURL( location, mRuntimeConfig.getHttpDefaultPort(), mRuntimeConfig.getHttpsDefaultPort()); // resolve any tokens in the URL location newLocation = urlResolverUtil.resolveHostNameInUrl(newLocation); // resolve "localhost" in the URL location urlResolverUtil.validateLocationURI(newLocation); urlLocation = new URL(newLocation); } catch (Exception e) { throw new JBIException(mMessages.getString("HTTPBC-E00361.Invalid_url_defined_in_application_config_object", new Object[] { configName, location, e.getLocalizedMessage() })); } } Endpoint bcEndpoint = EndpointFactory.createEndpoint(def, epDesc.getServiceName(), epDesc.getEndpointName(), epDesc.getInterfaceName(), epDesc.isProvider(), new HttpClientConnectionProperties(epDesc.getHostnameVerification(), epDesc.getConnectTimeout(), epDesc.getReadTimeout()), epDesc.getPropagateSoapHeader(), path, suSecurityConfig.getEndpointSecurityConfig(epDesc.getEndpointName()), mId, serviceUnitRootPath, mCredValidatorMgr); if (bcEndpoint != null) { getQOSConfigurations (bcEndpoint, epDesc); bcEndpoint.setValidHostnames(mRuntimeConfig.getValidHostnames()); bcEndpoint.init(); // add the handler list bcEndpoint.setHandlers(epDesc.getHandlers()); // save the handler libraries and dependencies bcEndpoint.setHandlerLibPaths(listResourceFiles(new File(serviceUnitRootPath), ".jar")); // enable ?wsdl ? bcEndpoint.setEnableWsdlQuery(epDesc.getEnableWsdlQuery()); //Let the listeners perform the one time deployment //specific logic such as endpoint validations. Iterator it2 = mEndpointChangeListeners.iterator(); while (it2.hasNext()) { EndpointChangeListener listener = (EndpointChangeListener)it2.next(); listener.endpointDeployed(bcEndpoint); } bcEndpoint.setOriginalWSDL(new File(serviceUnitRootPath + File.separatorChar + path)); if (hasAppObject) { bcEndpoint.setEndpointUrl(urlLocation); } mEndpoints.put(bcEndpoint.getUniqueName(), bcEndpoint); if (!epDesc.isProvider() && mModifiedWSDLs.get(path) == null) { // First convert path to a valid context string path = path.replace(File.separatorChar, '/'); String dirPath = ""; if (path.contains("/")) { dirPath = path.substring(0, path.lastIndexOf("/")); } modifyWSDL(bcEndpoint.getEndpointUrl().getProtocol(), bcEndpoint.getEndpointUrl().getPort(), dirPath, def, bcEndpoint); //mModifiedWSDLs.put(path, def); mModifiedWSDLs.put(path, new File(serviceUnitRootPath + File.separatorChar + path).getCanonicalPath()); } svcs[ii] = null; } } } endpointsCreated = true; } catch (Exception ex) { throw new JBIException(ex); } } public ByteBuffer getResource(String resourcePath, Endpoint endpoint) { Transformer transformer = null; ByteBuffer result = null; byte[] data = null; try { Object defRef = mModifiedWSDLs.get(resourcePath); if (defRef != null && defRef instanceof String) { File defFile = new File ((String) defRef); if (defFile.exists()) { FileInputStream fileStream = new FileInputStream(defFile); data = new byte[ (int) defFile.length() ]; int size = fileStream.read(data); fileStream.close(); if (size < 0) { return null; } result = ByteBuffer.wrap(data); } } Object schemaRef = mModifiedSchemas.get(resourcePath); if (schemaRef != null) { if (schemaRef instanceof String) { // read the schema file content File schemaFile = new File((String)schemaRef); if (schemaFile.exists()) { FileInputStream fileStream = new FileInputStream(schemaFile); data = new byte[ (int) schemaFile.length() ]; int size = fileStream.read(data); fileStream.close(); if (size < 0) { return null; } } else { // a location URI to be resolved by catalog.xml entry if (mResolver == null) { return null; } InputSource inputSource = mResolver.resolveEntity (null, ((String) schemaRef)); InputStream schemaStream = inputSource.getByteStream(); StreamSource schemaSource = new StreamSource(schemaStream); transformer = mTransformerPool.retrieve(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); StreamResult dest = new StreamResult(baos); transformer.transform(schemaSource, dest); data = baos.toByteArray(); } } if (data == null) { return null; } result = ByteBuffer.wrap(data); } } catch (Exception ex) { // fall-thru to account for new 'finally' block //return null; } finally { mTransformerPool.relinquish(transformer); } return result; } /** * Modifies the WSDL definition to use absolute paths based on the * URL. This method will change an imported XSDs and WSDL * locations to absolute URLs. *

* The change follows the following format: *

* http://<endpoint url>?WSDL=<XSD or WSDL path> *

* where *