1 /*
2 * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26 package com.sun.xml.internal.ws.transport.http.server;
27
28 import com.sun.istack.internal.Nullable;
29 import com.sun.xml.internal.stream.buffer.XMLStreamBufferResult;
30 import com.sun.xml.internal.ws.api.WSBinding;
31 import com.sun.xml.internal.ws.api.BindingID;
32 import com.sun.xml.internal.ws.api.server.WSEndpoint;
33 import com.sun.xml.internal.ws.binding.BindingImpl;
34 import com.sun.xml.internal.ws.api.server.InstanceResolver;
35 import com.sun.xml.internal.ws.api.server.SDDocumentSource;
36 import com.sun.xml.internal.ws.server.EndpointFactory;
37 import com.sun.xml.internal.ws.server.ServerRtException;
38 import com.sun.xml.internal.ws.util.xml.XmlUtil;
39 import com.sun.istack.internal.NotNull;
40
41 import java.net.MalformedURLException;
42
43 import javax.xml.namespace.QName;
44 import javax.xml.transform.Source;
45 import javax.xml.transform.Transformer;
46 import javax.xml.transform.TransformerException;
47 import javax.xml.ws;
48 import javax.xml.ws.wsaddressing.W3CEndpointReference;
49 import javax.xml.parsers.ParserConfigurationException;
50
51 import java.io.IOException;
52 import java.net.URL;
53 import java.util.ArrayList;
54 import java.util.Collections;
55 import java.util.HashMap;
56 import java.util.List;
57 import java.util.Map;
58 import java.util.concurrent.Executor;
59
60 import org.xml.sax.EntityResolver;
61 import org.xml.sax.SAXException;
62 import org.w3c.dom.Element;
63
64
65 /**
66 * Implements {@link Endpoint}.
67 * <p/>
68 * <p/>
69 * This class accumulates the information necessary to create
70 * {@link WSEndpoint}, and then when {@link #publish} method
71 * is called it will be created.
72 * <p/>
73 * <p/>
74 * This object also allows accumulated information to be retrieved.
75 *
76 * @author Jitendra Kotamraju
77 */
78 public class EndpointImpl extends Endpoint {
79
80 private static final WebServicePermission ENDPOINT_PUBLISH_PERMISSION =
81 new WebServicePermission("publishEndpoint");
82
83 /**
84 * Once the service is published, this field will
85 * be set to the {@link HttpEndpoint} instance.
86 * <p/>
87 * But don't declare the type as {@link HttpEndpoint}
88 * to avoid static type dependency that cause the class loading to
89 * fail if the LW HTTP server doesn't exist.
90 */
91 private Object actualEndpoint;
92
93 // information accumulated for creating WSEndpoint
94 private final WSBinding binding;
95 private final Object implementor;
96 private List<Source> metadata;
97 private Executor executor;
98 private Map<String, Object> properties = Collections.emptyMap(); // always non-null
99 private boolean stopped;
100
101
102 public EndpointImpl(@NotNull BindingID bindingId, @NotNull Object impl) {
103 binding = BindingImpl.create(bindingId);
104 implementor = impl;
105 }
106
107 /**
108 * Wraps an already created {@link WSEndpoint} into an {@link EndpointImpl},
109 * and immediately publishes it with the given context.
110 *
111 * @deprecated This is a backdoor method. Don't use it unless you know what you are doing.
112 */
113 public EndpointImpl(WSEndpoint wse, Object serverContext) {
114 actualEndpoint = new HttpEndpoint(wse, executor);
115 ((HttpEndpoint) actualEndpoint).publish(serverContext);
116 binding = wse.getBinding();
117 implementor = null; // this violates the semantics, but hey, this is a backdoor.
118 }
119
120 public Binding getBinding() {
121 return binding;
122 }
123
124 public Object getImplementor() {
125 return implementor;
126 }
127
128 public void publish(String address) {
129 canPublish();
130 URL url;
131 try {
132 url = new URL(address);
133 } catch (MalformedURLException ex) {
134 throw new IllegalArgumentException("Cannot create URL for this address " + address);
135 }
136 if (!url.getProtocol().equals("http")) {
137 throw new IllegalArgumentException(url.getProtocol() + " protocol based address is not supported");
138 }
139 if (!url.getPath().startsWith("/")) {
140 throw new IllegalArgumentException("Incorrect WebService address=" + address +
141 ". The address's path should start with /");
142 }
143 createEndpoint();
144 ((HttpEndpoint) actualEndpoint).publish(address);
145 }
146
147 public void publish(Object serverContext) {
148 canPublish();
149 if (!com.sun.net.httpserver.HttpContext.class.isAssignableFrom(serverContext.getClass())) {
150 throw new IllegalArgumentException(serverContext.getClass() + " is not a supported context.");
151 }
152 createEndpoint();
153 ((HttpEndpoint) actualEndpoint).publish(serverContext);
154 }
155
156 public void stop() {
157 if (isPublished()) {
158 ((HttpEndpoint) actualEndpoint).stop();
159 actualEndpoint = null;
160 stopped = true;
161 }
162 }
163
164 public boolean isPublished() {
165 return actualEndpoint != null;
166 }
167
168 public List<Source> getMetadata() {
169 return metadata;
170 }
171
172 public void setMetadata(java.util.List<Source> metadata) {
173 if (isPublished()) {
174 throw new IllegalStateException("Cannot set Metadata. Endpoint is already published");
175 }
176 this.metadata = metadata;
177 }
178
179 public Executor getExecutor() {
180 return executor;
181 }
182
183 public void setExecutor(Executor executor) {
184 this.executor = executor;
185 }
186
187 public Map<String, Object> getProperties() {
188 return new HashMap<String, Object>(properties);
189 }
190
191 public void setProperties(Map<String, Object> map) {
192 this.properties = new HashMap<String, Object>(map);
193 }
194
195 /*
196 * Checks the permission of "publishEndpoint" before accessing HTTP classes.
197 * Also it checks if there is an available HTTP server implementation.
198 */
199 private void createEndpoint() {
200 // Checks permission for "publishEndpoint"
201 SecurityManager sm = System.getSecurityManager();
202 if (sm != null) {
203 sm.checkPermission(ENDPOINT_PUBLISH_PERMISSION);
204 }
205
206 // See if HttpServer implementation is available
207 try {
208 Class.forName("com.sun.net.httpserver.HttpServer");
209 } catch (Exception e) {
210 throw new UnsupportedOperationException("Couldn't load light weight http server", e);
211 }
212
213 WSEndpoint wse = WSEndpoint.create(
214 (Class<?>) implementor.getClass(), true,
215 InstanceResolver.createSingleton(implementor).createInvoker(),
216 getProperty(QName.class, Endpoint.WSDL_SERVICE),
217 getProperty(QName.class, Endpoint.WSDL_PORT),
218 null /* no container */,
219 binding,
220 getPrimaryWsdl(),
221 buildDocList(),
222 (EntityResolver) null
223 );
224 // Don't load HttpEndpoint class before as it may load HttpServer classes
225 actualEndpoint = new HttpEndpoint(wse, executor);
226 }
227
228 private <T> T getProperty(Class<T> type, String key) {
229 Object o = properties.get(key);
230 if (o == null) return null;
231 if (type.isInstance(o))
232 return type.cast(o);
233 else
234 throw new IllegalArgumentException("Property " + key + " has to be of type " + type); // i18n
235 }
236
237 /**
238 * Convert metadata sources using identity transform. So that we can
239 * reuse the Source object multiple times.
240 */
241 private List<SDDocumentSource> buildDocList() {
242 List<SDDocumentSource> r = new ArrayList<SDDocumentSource>();
243
244 if (metadata != null) {
245 Transformer transformer = XmlUtil.newTransformer();
246 for (Source source : metadata) {
247 try {
248 XMLStreamBufferResult xsbr = XmlUtil.identityTransform(source, new XMLStreamBufferResult());
249 String systemId = source.getSystemId();
250
251 r.add(SDDocumentSource.create(new URL(systemId), xsbr.getXMLStreamBuffer()));
252 } catch (TransformerException te) {
253 throw new ServerRtException("server.rt.err", te);
254 } catch (IOException te) {
255 throw new ServerRtException("server.rt.err", te);
256 } catch (SAXException e) {
257 throw new ServerRtException("server.rt.err", e);
258 } catch (ParserConfigurationException e) {
259 throw new ServerRtException("server.rt.err", e);
260 }
261 }
262 }
263
264 return r;
265 }
266
267 /**
268 * Gets wsdl from @WebService or @WebServiceProvider
269 */
270 private @Nullable SDDocumentSource getPrimaryWsdl() {
271 Class implType = implementor.getClass();
272 // Takes care of @WebService, @WebServiceProvider's wsdlLocation
273 EndpointFactory.verifyImplementorClass(implType);
274 String wsdlLocation = EndpointFactory.getWsdlLocation(implType);
275 if (wsdlLocation != null) {
276 ClassLoader cl = implType.getClassLoader();
277 URL url = cl.getResource(wsdlLocation);
278 if (url != null) {
279 return SDDocumentSource.create(url);
280 }
281 throw new ServerRtException("cannot.load.wsdl", wsdlLocation);
282 }
283 return null;
284 }
285
286 private void canPublish() {
287 if (isPublished()) {
288 throw new IllegalStateException(
289 "Cannot publish this endpoint. Endpoint has been already published.");
290 }
291 if (stopped) {
292 throw new IllegalStateException(
293 "Cannot publish this endpoint. Endpoint has been already stopped.");
294 }
295 }
296
297 public EndpointReference getEndpointReference(Element...referenceParameters) {
298 return getEndpointReference(W3CEndpointReference.class, referenceParameters);
299 }
300
301 public <T extends EndpointReference> T getEndpointReference(Class<T> clazz, Element...referenceParameters) {
302 if (!isPublished()) {
303 throw new WebServiceException("Endpoint is not published yet");
304 }
305 return ((HttpEndpoint)actualEndpoint).getEndpointReference(clazz,referenceParameters);
306 }
307 }