001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.servicemix.http;
018    
019    import java.net.URI;
020    
021    import javax.jbi.management.DeploymentException;
022    import javax.jbi.messaging.MessageExchange.Role;
023    import javax.jbi.servicedesc.ServiceEndpoint;
024    import javax.wsdl.Binding;
025    import javax.wsdl.Definition;
026    import javax.wsdl.Port;
027    import javax.wsdl.PortType;
028    import javax.wsdl.Service;
029    import javax.wsdl.extensions.ExtensibilityElement;
030    import javax.wsdl.extensions.http.HTTPAddress;
031    import javax.xml.namespace.QName;
032    
033    import org.apache.servicemix.common.ExternalEndpoint;
034    import org.apache.servicemix.common.ManagementSupport;
035    import org.apache.servicemix.common.security.AuthenticationService;
036    import org.apache.servicemix.common.security.KeystoreManager;
037    import org.apache.servicemix.http.processors.ConsumerProcessor;
038    import org.apache.servicemix.http.processors.ProviderProcessor;
039    import org.apache.servicemix.http.tools.PortTypeDecorator;
040    import org.apache.servicemix.soap.SoapEndpoint;
041    import org.apache.servicemix.soap.SoapExchangeProcessor;
042    
043    import com.ibm.wsdl.extensions.http.HTTPAddressImpl;
044    
045    /**
046     * an HTTP endpoint. This is the base for all HTTP endpoints.
047     * 
048     * @author gnodet
049     * @version $Revision: 695841 $
050     * @org.apache.xbean.XBean element="endpoint" description="the base element for all HTTP endpoints"
051     */
052    public class HttpEndpoint extends SoapEndpoint implements HttpEndpointType {
053    
054        protected ExtensibilityElement binding;
055        protected String locationURI;
056        protected SslParameters ssl;
057        protected String authMethod;
058        protected String soapAction;
059        protected BasicAuthCredentials basicAuthentication;
060        protected ProxyParameters proxy;
061        protected boolean synchronous;
062        protected boolean wantContentTypeHeaderFromExchangeIntoHttpRequest;
063        protected int timeout;
064    
065        public HttpEndpoint() {
066        }
067    
068        public HttpEndpoint(boolean dynamic) {
069            super(dynamic);
070        }
071    
072        /**
073         * Determines if the HTTP provider processor copies the HTTP headers from the HTTP response into the JBI exchange.
074         * 
075         * @return <code>true</code> if the HTTP headers will be copied into the exchange
076         */
077        public boolean isWantContentTypeHeaderFromExchangeIntoHttpRequest() {
078            return wantContentTypeHeaderFromExchangeIntoHttpRequest;
079        }
080    
081        /**
082         * Specifies if the HTTP provider processor copies the HTTP headers from the HTTP response into the JBI exchange. If the headers
083         * will be used for a new HTTP reuquest, setting this to <code>true</code> leads to an error.
084         * 
085         * @param wantContentTypeHeaderFromExchangeIntoHttpRequest <code>true</code> if the HTTP headers will be copied into the exchange
086         * @org.apache.xbean.Property description="Specifies if the HTTP provider will copy the HTTP request headers into the JBI
087         *                            exchange. The default is <code>false</code>. This value overrides the value set for the component
088         *                            using the <code>configuration</code> element."
089         */
090        public void setWantContentTypeHeaderFromExchangeIntoHttpRequest(boolean wantContentTypeHeaderFromExchangeIntoHttpRequest) {
091            this.wantContentTypeHeaderFromExchangeIntoHttpRequest = wantContentTypeHeaderFromExchangeIntoHttpRequest;
092        }
093    
094        /**
095         * @return the synchronous
096         */
097        public boolean isSynchronous() {
098            return synchronous;
099        }
100    
101        /**
102         * @param synchronous the synchronous to set
103         */
104        public void setSynchronous(boolean synchronous) {
105            this.synchronous = synchronous;
106        }
107    
108        /**
109         * @return the soapAction
110         */
111        public String getSoapAction() {
112            return soapAction;
113        }
114    
115        /**
116         * @param soapAction the soapAction to set
117         */
118        public void setSoapAction(String soapAction) {
119            this.soapAction = soapAction;
120        }
121    
122        /**
123         * Returns a string describing the authentication scheme being used by an endpoint.
124         * 
125         * @return a string representing the authentication method used by an endpoint
126         */
127        public String getAuthMethod() {
128            return authMethod;
129        }
130    
131        /**
132         * Specifies the authentication method used by a secure endpoint. The authentication method is a string naming the scheme used
133         * for authenticating users.
134         * 
135         * @param authMethod a string naming the authentication scheme a secure endpoint should use
136         * @org.apache.xbean.Property description="a string naming the scheme used for authenticating users"
137         */
138        public void setAuthMethod(String authMethod) {
139            this.authMethod = authMethod;
140        }
141    
142        /**
143         * @return Returns the ssl.
144         */
145        public SslParameters getSsl() {
146            return ssl;
147        }
148    
149        /**
150         * Sets the properties used to configure SSL for the endpoint.
151         * 
152         * @param ssl an <code>SslParameters</code> object containing the SSL properties
153         * @org.apache.xbean.Property description="a bean containing the SSL configuration properties"
154         */
155        public void setSsl(SslParameters ssl) {
156            this.ssl = ssl;
157        }
158    
159        public ExtensibilityElement getBinding() {
160            return binding;
161        }
162    
163        public void setBinding(ExtensibilityElement binding) {
164            this.binding = binding;
165        }
166    
167        /**
168         * Returns the URI to which the endpoint sends requests.
169         * 
170         * @return a string representing the URI to which requests are sent
171         */
172        public String getLocationURI() {
173            return locationURI;
174        }
175    
176        /**
177         * Sets the URI to which an endpoint sends requests.
178         * 
179         * @param locationUri a string representing the URI
180         * @org.apache.xbean.Property description="the URI to which a provider endpoint sends requests"
181         */
182        public void setLocationURI(String locationUri) {
183            this.locationURI = locationUri;
184        }
185    
186        /**
187         * Sets the authentication data used for provider endpoints using basic authentication.
188         * 
189         * @param basicAuthCredentials the <code>BasicAuthCredentials</code> that will be used for authentication
190         * @org.apache.xbean.Property description="authentication data for using basic HTTP authentication."
191         */
192        public void setBasicAuthentication(BasicAuthCredentials basicAuthCredentials) {
193            this.basicAuthentication = basicAuthCredentials;
194        }
195    
196        /**
197         * Gets the authentication data used for provider endpoints using basic authentication.
198         * 
199         * @return the <code>BasicAuthCredentials</code> to use for authentication
200         */
201        public BasicAuthCredentials getBasicAuthentication() {
202            return basicAuthentication;
203        }
204    
205        /**
206         * @return Returns the proxy.
207         */
208        public ProxyParameters getProxy() {
209            return this.proxy;
210        }
211    
212        /**
213         * Sets the configuration information for the proxy used by a HTTP consumer endpoint. This configuration overrides the proxy
214         * configuraiton specified at the component level.
215         * 
216         * @param proxy The proxy to set.
217         * @org.apache.xbean.Property description="configuration used to establish a proxy for sending HTTP requests. This configuration overrides that which is set at the component level."
218         */
219        public void setProxy(ProxyParameters proxy) {
220            this.proxy = proxy;
221        }
222    
223        /**
224         * Sets the endpoint's role. HTTP endpoints can be either consumers or providers. Specifying <code>consumer</code> for the role
225         * defines the endpoint as a consumer which exposes an endpoint at a URL. Specifying <code>provider</code> for the role defines
226         * the endpoint as a provider that sends HTTP requests to a URL.
227         * 
228         * @param role a string specifying the role of the endpoint
229         * @org.apache.xbean.Property alias="role" description= "HTTP endpoints can be either consumers or providers. Specifying
230         *                            <code>consumer</code> for the role defines the endpoint as a consumer which exposes an endpoint at
231         *                            a URL. Specifying <code>provider</code> for the role defines the endpoint as a provider that sends
232         *                            HTTP requests to a URL."
233         */
234        public void setRoleAsString(String role) {
235            super.setRoleAsString(role);
236        }
237    
238        /**
239         * Specifies the timeout value for an HTTP endpoint. The timeout is specified in milliseconds. The default value is 0 which
240         * means that the endpoint will never timeout.
241         * 
242         * @org.apache.xbean.Property description=
243         *                            "the number of milliseconds before the endpoint times out. The default value is 0 which means that the endpoint will never timeout."
244         * @param timeout the length time, in milliseconds, to wait before timing out
245         */
246        public void setTimeout(int timeout) {
247            this.timeout = timeout;
248        }
249    
250        /**
251         * Returns the timeout value for an HTTP endpoint.
252         * 
253         * @return the timeout specified in milliseconds
254         */
255        public int getTimeout() {
256            return timeout;
257        }
258    
259        public void reloadWsdl() {
260            description = null;
261            definition = null;
262            loadWsdl();
263        }
264    
265        protected PortType getTargetPortType(Definition def) {
266            PortType portType = null;
267            // If the WSDL description only contain one PortType, use it
268            if (def.getServices().size() == 0 && def.getPortTypes().size() == 1) {
269                if (logger.isDebugEnabled()) {
270                    logger.debug("WSDL only defines a PortType, using this one");
271                }
272                portType = (PortType)def.getPortTypes().values().iterator().next();
273            } else if (targetInterfaceName != null) {
274                portType = def.getPortType(targetInterfaceName);
275                if (portType == null && logger.isDebugEnabled()) {
276                    logger.debug("PortType for targetInterfaceName could not be found");
277                }
278            } else if (targetService != null && targetEndpoint != null) {
279                Service svc = def.getService(targetService);
280                Port port = (svc != null) ? svc.getPort(targetEndpoint) : null;
281                portType = (port != null) ? port.getBinding().getPortType() : null;
282                if (portType == null && logger.isDebugEnabled()) {
283                    logger.debug("PortType for targetService/targetEndpoint could not be found");
284                }
285            } else if (targetService != null) {
286                Service svc = def.getService(targetService);
287                if (svc != null && svc.getPorts().size() == 1) {
288                    Port port = (Port)svc.getPorts().values().iterator().next();
289                    portType = (port != null) ? port.getBinding().getPortType() : null;
290                }
291                if (portType == null && logger.isDebugEnabled()) {
292                    logger.debug("Service for targetService could not be found");
293                }
294            } else if (interfaceName != null) {
295                portType = def.getPortType(interfaceName);
296                if (portType == null && logger.isDebugEnabled()) {
297                    logger.debug("Service for targetInterfaceName could not be found");
298                }
299            } else {
300                Service svc = def.getService(service);
301                Port port = (svc != null) ? svc.getPort(endpoint) : null;
302                portType = (port != null && port.getBinding() != null) ? port.getBinding().getPortType() : null;
303                if (portType == null && logger.isDebugEnabled()) {
304                    logger.debug("Port for service/endpoint could not be found");
305                }
306            }
307            return portType;
308        }
309    
310        protected void overrideDefinition(Definition def) throws Exception {
311            PortType portType = getTargetPortType(def);
312            if (portType != null) {
313                QName[] names = (QName[])def.getPortTypes().keySet().toArray(new QName[0]);
314                for (int i = 0; i < names.length; i++) {
315                    if (!names[i].equals(portType.getQName())) {
316                        def.removePortType(names[i]);
317                    }
318                }
319                names = (QName[])def.getServices().keySet().toArray(new QName[0]);
320                for (int i = 0; i < names.length; i++) {
321                    def.removeService(names[i]);
322                }
323                names = (QName[])def.getBindings().keySet().toArray(new QName[0]);
324                for (int i = 0; i < names.length; i++) {
325                    def.removeBinding(names[i]);
326                }
327                String location = getLocationURI();
328                if (!location.endsWith("/")) {
329                    location += "/";
330                }
331                HttpComponent comp = (HttpComponent)getServiceUnit().getComponent();
332                if (comp.getConfiguration().isManaged()) {
333                    // Save the path
334                    String path = new URI(location).getPath();
335                    location = "http://localhost";
336                    if (comp.getHost() != null) {
337                        if (comp.getProtocol() != null) {
338                            location = comp.getProtocol() + "://";
339                        } else {
340                            location = "http://";
341                        }
342                        location += comp.getHost();
343                        if (comp.getPort() != 80) {
344                            location += ":" + comp.getPort();
345                        }
346                        if (comp.getPath() != null) {
347                            location += comp.getPath();
348                        }
349                    }
350                    location += comp.getConfiguration().getMapping() + path;
351                }
352                if (portType.getQName().getNamespaceURI().equals(service.getNamespaceURI())) {
353                    if (isSoap()) {
354                        PortTypeDecorator.decorate(def, portType, location, endpoint + "Binding", service
355                            .getLocalPart(), endpoint, soapVersion);
356                        definition = def;
357                    } else {
358                        Binding bnd = def.createBinding();
359                        bnd.setPortType(portType);
360                        bnd.setQName(new QName(service.getNamespaceURI(), endpoint + "Binding"));
361                        bnd.setUndefined(false);
362                        def.addBinding(bnd);
363                        Port port = def.createPort();
364                        port.setName(endpoint);
365                        port.setBinding(bnd);
366                        HTTPAddress address = new HTTPAddressImpl();
367                        address.setLocationURI(location);
368                        port.addExtensibilityElement(address);
369                        def.addNamespace("http", "http://schemas.xmlsoap.org/wsdl/http/");
370                        Service svc = def.createService();
371                        svc.setQName(service);
372                        svc.addPort(port);
373                        def.addService(svc);
374                        definition = def;
375                    }
376                } else {
377                    definition = PortTypeDecorator.createImportDef(def, service.getNamespaceURI(),
378                                                                   "porttypedef.wsdl");
379                    PortTypeDecorator.decorate(definition, portType, location, endpoint + "Binding", service
380                        .getLocalPart(), endpoint, soapVersion);
381                }
382            }
383        }
384    
385        protected SoapExchangeProcessor createProviderProcessor() {
386            return new ProviderProcessor(this);
387        }
388    
389        protected SoapExchangeProcessor createConsumerProcessor() {
390            return new ConsumerProcessor(this);
391        }
392    
393        protected ServiceEndpoint createExternalEndpoint() {
394            return new ExternalEndpoint(getServiceUnit().getComponent().getEPRElementName(), getLocationURI(),
395                                        getService(), getEndpoint(), getInterfaceName());
396        }
397    
398        public AuthenticationService getAuthenticationService() {
399            HttpComponent comp = (HttpComponent)getServiceUnit().getComponent();
400            return AuthenticationService.Proxy.create(comp.getAuthenticationService());
401        }
402    
403        public KeystoreManager getKeystoreManager() {
404            HttpComponent comp = (HttpComponent)getServiceUnit().getComponent();
405            return KeystoreManager.Proxy.create(comp.getKeystoreManager());
406        }
407    
408        public void validate() throws DeploymentException {
409            if (getRole() == null) {
410                throw failure("deploy", "Endpoint must have a defined role", null);
411            }
412            if (getLocationURI() == null) {
413                throw failure("deploy", "Endpoint must have a defined locationURI", null);
414            }
415            if (!isSoap() && getRole() == Role.CONSUMER && getDefaultMep() == null) {
416                throw failure("deploy", "Non soap endpoints must have a defined defaultMep", null);
417            }
418        }
419    
420        protected DeploymentException failure(String task, String info, Throwable e) {
421            ManagementSupport.Message msg = new ManagementSupport.Message();
422            msg.setComponent(serviceUnit.getComponent().getComponentName());
423            msg.setTask(task);
424            msg.setResult("FAILED");
425            msg.setType("ERROR");
426            msg.setMessage(info);
427            msg.setException(e);
428            return new DeploymentException(ManagementSupport.createComponentMessage(msg));
429        }
430    
431    }