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.geronimo.cxf;
018
019 import java.io.IOException;
020 import java.io.OutputStream;
021 import java.io.Serializable;
022 import java.io.InputStream;
023 import java.net.HttpURLConnection;
024 import java.security.Principal;
025 import java.util.Iterator;
026 import java.util.ArrayList;
027 import java.util.List;
028 import java.util.Map;
029 import java.util.Enumeration;
030 import java.util.logging.Logger;
031 import javax.servlet.ServletContext;
032 import javax.servlet.http.HttpServletRequest;
033 import javax.servlet.http.HttpServletResponse;
034 import javax.xml.ws.handler.MessageContext;
035
036 import org.apache.cxf.Bus;
037 import org.apache.cxf.message.Message;
038 import org.apache.cxf.message.MessageImpl;
039 import org.apache.cxf.security.SecurityContext;
040 import org.apache.cxf.service.model.EndpointInfo;
041 import org.apache.cxf.transport.Conduit;
042 import org.apache.cxf.transport.ConduitInitiator;
043 import org.apache.cxf.transport.Destination;
044 import org.apache.cxf.transport.MessageObserver;
045 import org.apache.cxf.transport.http.AbstractHTTPDestination;
046 import org.apache.cxf.ws.addressing.EndpointReferenceType;
047 import org.apache.geronimo.webservices.WebServiceContainer;
048 import org.apache.geronimo.webservices.WebServiceContainer.Request;
049 import org.apache.geronimo.webservices.WebServiceContainer.Response;
050
051 public class GeronimoDestination extends AbstractHTTPDestination
052 implements Serializable {
053
054 private MessageObserver messageObserver;
055 private boolean passSecurityContext = false;
056
057 public GeronimoDestination(Bus bus,
058 ConduitInitiator conduitInitiator,
059 EndpointInfo endpointInfo) throws IOException {
060 super(bus, conduitInitiator, endpointInfo, true);
061 }
062
063 public void setPassSecurityContext(boolean passSecurityContext) {
064 this.passSecurityContext = passSecurityContext;
065 }
066
067 public boolean getPassSecurityContext() {
068 return this.passSecurityContext;
069 }
070
071 public EndpointInfo getEndpointInfo() {
072 return this.endpointInfo;
073 }
074
075 public void invoke(Request request, Response response) throws Exception {
076 MessageImpl message = new MessageImpl();
077 message.setContent(InputStream.class, request.getInputStream());
078 message.setDestination(this);
079
080 message.put(Request.class, request);
081 message.put(Response.class, response);
082
083 final HttpServletRequest servletRequest =
084 (HttpServletRequest)request.getAttribute(WebServiceContainer.SERVLET_REQUEST);
085 message.put(MessageContext.SERVLET_REQUEST, servletRequest);
086
087 HttpServletResponse servletResponse =
088 (HttpServletResponse)request.getAttribute(WebServiceContainer.SERVLET_RESPONSE);
089 message.put(MessageContext.SERVLET_RESPONSE, servletResponse);
090
091 ServletContext servletContext =
092 (ServletContext)request.getAttribute(WebServiceContainer.SERVLET_CONTEXT);
093 message.put(MessageContext.SERVLET_CONTEXT, servletContext);
094
095 if (this.passSecurityContext) {
096 message.put(SecurityContext.class, new SecurityContext() {
097 public Principal getUserPrincipal() {
098 return servletRequest.getUserPrincipal();
099 }
100 public boolean isUserInRole(String role) {
101 return servletRequest.isUserInRole(role);
102 }
103 });
104 }
105
106 // this calls copyRequestHeaders()
107 setHeaders(message);
108
109 message.put(Message.HTTP_REQUEST_METHOD, servletRequest.getMethod());
110 message.put(Message.PATH_INFO, servletRequest.getPathInfo());
111 message.put(Message.QUERY_STRING, servletRequest.getQueryString());
112 message.put(Message.CONTENT_TYPE, servletRequest.getContentType());
113 message.put(Message.ENCODING, getCharacterEncoding(servletRequest.getCharacterEncoding()));
114
115 messageObserver.onMessage(message);
116 }
117
118 private static String getCharacterEncoding(String encoding) {
119 if (encoding != null) {
120 encoding = encoding.trim();
121 // work around a bug with Jetty which results in the character
122 // encoding not being trimmed correctly:
123 // http://jira.codehaus.org/browse/JETTY-302
124 if (encoding.endsWith("\"")) {
125 encoding = encoding.substring(0, encoding.length() - 1);
126 }
127 }
128 return encoding;
129 }
130
131 protected void copyRequestHeaders(Message message, Map<String, List<String>> headers) {
132 HttpServletRequest servletRequest = (HttpServletRequest)message.get(MessageContext.SERVLET_REQUEST);
133 if (servletRequest != null) {
134 Enumeration names = servletRequest.getHeaderNames();
135 while(names.hasMoreElements()) {
136 String name = (String)names.nextElement();
137
138 List<String> headerValues = headers.get(name);
139 if (headerValues == null) {
140 headerValues = new ArrayList<String>();
141 headers.put(name, headerValues);
142 }
143
144 Enumeration values = servletRequest.getHeaders(name);
145 while(values.hasMoreElements()) {
146 String value = (String)values.nextElement();
147 headerValues.add(value);
148 }
149 }
150 }
151 }
152
153 public Logger getLogger() {
154 return Logger.getLogger(GeronimoDestination.class.getName());
155 }
156
157 public Conduit getInbuiltBackChannel(Message inMessage) {
158 return new BackChannelConduit(null, inMessage);
159 }
160
161 public Conduit getBackChannel(Message inMessage,
162 Message partialResponse,
163 EndpointReferenceType address) throws IOException {
164 Conduit backChannel = null;
165 if (address == null) {
166 backChannel = new BackChannelConduit(address, inMessage);
167 } else {
168 if (partialResponse != null) {
169 // setup the outbound message to for 202 Accepted
170 partialResponse.put(Message.RESPONSE_CODE,
171 HttpURLConnection.HTTP_ACCEPTED);
172 backChannel = new BackChannelConduit(address, inMessage);
173 } else {
174 backChannel = conduitInitiator.getConduit(endpointInfo, address);
175 // ensure decoupled back channel input stream is closed
176 backChannel.setMessageObserver(new MessageObserver() {
177 public void onMessage(Message m) {
178 if (m.getContentFormats().contains(InputStream.class)) {
179 InputStream is = m.getContent(InputStream.class);
180 try {
181 is.close();
182 } catch (Exception e) {
183 // ignore
184 }
185 }
186 }
187 });
188 }
189 }
190 return backChannel;
191 }
192
193 public void shutdown() {
194 }
195
196 public void setMessageObserver(MessageObserver messageObserver) {
197 this.messageObserver = messageObserver;
198 }
199
200 protected class BackChannelConduit implements Conduit {
201
202 protected Message request;
203 protected EndpointReferenceType target;
204
205 BackChannelConduit(EndpointReferenceType target, Message request) {
206 this.target = target;
207 this.request = request;
208 }
209
210 public void close(Message msg) throws IOException {
211 msg.getContent(OutputStream.class).close();
212 }
213
214 /**
215 * Register a message observer for incoming messages.
216 *
217 * @param observer the observer to notify on receipt of incoming
218 */
219 public void setMessageObserver(MessageObserver observer) {
220 // shouldn't be called for a back channel conduit
221 }
222
223 public void prepare(Message message) throws IOException {
224 send(message);
225 }
226
227 /**
228 * Send an outbound message, assumed to contain all the name-value
229 * mappings of the corresponding input message (if any).
230 *
231 * @param message the message to be sent.
232 */
233 public void send(Message message) throws IOException {
234 Response response = (Response)request.get(Response.class);
235
236 // handle response headers
237 updateResponseHeaders(message);
238
239 Map<String, List<String>> protocolHeaders =
240 (Map<String, List<String>>) message.get(Message.PROTOCOL_HEADERS);
241
242 // set headers of the HTTP response object
243 Iterator headers = protocolHeaders.entrySet().iterator();
244 while (headers.hasNext()) {
245 Map.Entry entry = (Map.Entry) headers.next();
246 String headerName = (String) entry.getKey();
247 String headerValue = getHeaderValue((List) entry.getValue());
248 response.setHeader(headerName, headerValue);
249 }
250
251 message.setContent(OutputStream.class, new WrappedOutputStream(message, response));
252 }
253
254 /**
255 * @return the reference associated with the target Destination
256 */
257 public EndpointReferenceType getTarget() {
258 return target;
259 }
260
261 /**
262 * Retreive the back-channel Destination.
263 *
264 * @return the backchannel Destination (or null if the backchannel is
265 * built-in)
266 */
267 public Destination getBackChannel() {
268 return null;
269 }
270
271 /**
272 * Close the conduit
273 */
274 public void close() {
275 }
276 }
277
278 private String getHeaderValue(List<String> values) {
279 Iterator iter = values.iterator();
280 StringBuffer buf = new StringBuffer();
281 while(iter.hasNext()) {
282 buf.append(iter.next());
283 if (iter.hasNext()) {
284 buf.append(", ");
285 }
286 }
287 return buf.toString();
288 }
289
290 protected void setContentType(Message message, Response response) {
291 Map<String, List<String>> protocolHeaders =
292 (Map<String, List<String>>)message.get(Message.PROTOCOL_HEADERS);
293
294 if (protocolHeaders == null || !protocolHeaders.containsKey(Message.CONTENT_TYPE)) {
295 String ct = (String) message.get(Message.CONTENT_TYPE);
296 String enc = (String) message.get(Message.ENCODING);
297
298 if (null != ct) {
299 if (enc != null && ct.indexOf("charset=") == -1) {
300 ct = ct + "; charset=" + enc;
301 }
302 response.setContentType(ct);
303 } else if (enc != null) {
304 response.setContentType("text/xml; charset=" + enc);
305 }
306 }
307 }
308
309 private class WrappedOutputStream extends OutputStream {
310
311 private Message message;
312 private Response response;
313 private OutputStream rawOutputStream;
314
315 WrappedOutputStream(Message message, Response response) {
316 this.message = message;
317 this.response = response;
318 }
319
320 public void write(int b) throws IOException {
321 flushHeaders();
322 this.rawOutputStream.write(b);
323 }
324
325 public void write(byte b[]) throws IOException {
326 flushHeaders();
327 this.rawOutputStream.write(b);
328 }
329
330 public void write(byte b[], int off, int len) throws IOException {
331 flushHeaders();
332 this.rawOutputStream.write(b, off, len);
333 }
334
335 public void flush() throws IOException {
336 flushHeaders();
337 this.rawOutputStream.flush();
338 }
339
340 public void close() throws IOException {
341 flushHeaders();
342 this.rawOutputStream.close();
343 }
344
345 protected void flushHeaders() throws IOException {
346 if (this.rawOutputStream != null) {
347 return;
348 }
349
350 // set response code
351 Integer i = (Integer) this.message.get(Message.RESPONSE_CODE);
352 if (i != null) {
353 this.response.setStatusCode(i.intValue());
354 }
355
356 // set content-type
357 setContentType(this.message, this.response);
358
359 this.rawOutputStream = this.response.getOutputStream();
360 }
361
362 }
363
364 }