You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by se...@apache.org on 2009/01/28 19:54:22 UTC
svn commit: r738583 - in /cxf/trunk:
rt/core/src/main/java/org/apache/cxf/attachment/
rt/core/src/main/java/org/apache/cxf/interceptor/
rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/
rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/ rt/f...
Author: sergeyb
Date: Wed Jan 28 18:54:21 2009
New Revision: 738583
URL: http://svn.apache.org/viewvc?rev=738583&view=rev
Log:
JAXRS : improving the support for multiparts
Added:
cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/
cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/Attachment.java (with props)
cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/ContentDisposition.java (with props)
cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/InputStreamDataSource.java (with props)
cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/Multipart.java (with props)
cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/MultipartBody.java (with props)
cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/interceptor/AttachmentInputInterceptor.java (with props)
cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/multipart/
cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/multipart/AttachmentTest.java (with props)
cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/multipart/ContentDispositionTest.java (with props)
cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/multipart/MultipartBodyTest.java (with props)
cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/attachmentData3
Removed:
cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/MultipartID.java
cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/multipart/MultipartInfo.java
Modified:
cxf/trunk/rt/core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializer.java
cxf/trunk/rt/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java
cxf/trunk/rt/core/src/main/java/org/apache/cxf/interceptor/AttachmentInInterceptor.java
cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/JAXRSBindingFactory.java
cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/MessageContext.java
cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/MessageContextImpl.java
cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/HttpHeadersImpl.java
cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/ActivationProvider.java
cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/multipart/AttachmentUtils.java
cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultipartTest.java
cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/MultipartServer.java
cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/MultipartStore.java
Modified: cxf/trunk/rt/core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializer.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializer.java?rev=738583&r1=738582&r2=738583&view=diff
==============================================================================
--- cxf/trunk/rt/core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializer.java (original)
+++ cxf/trunk/rt/core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializer.java Wed Jan 28 18:54:21 2009
@@ -23,16 +23,14 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
-import java.net.URLDecoder;
-import java.util.Enumeration;
+import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import javax.activation.DataHandler;
import javax.activation.DataSource;
-import javax.mail.Header;
import javax.mail.MessagingException;
import javax.mail.internet.InternetHeaders;
@@ -72,11 +70,17 @@
private InputStream body;
private Set<DelegatingInputStream> loaded = new HashSet<DelegatingInputStream>();
+ private List<String> supportedTypes;
public AttachmentDeserializer(Message message) {
- this.message = message;
+ this(message, Collections.singletonList("multipart/related"));
}
+ public AttachmentDeserializer(Message message, List<String> supportedTypes) {
+ this.message = message;
+ this.supportedTypes = supportedTypes;
+ }
+
public void initializeAttachments() throws IOException {
initializeRootMessage();
@@ -95,7 +99,7 @@
throw new IllegalStateException("An InputStream must be provided!");
}
- if (contentType.toLowerCase().indexOf("multipart/related") != -1) {
+ if (AttachmentUtil.isTypeSupported(contentType.toLowerCase(), supportedTypes)) {
String boundaryString = findBoundaryFromContentType(contentType);
if (null == boundaryString) {
boundaryString = findBoundaryFromInputStream();
@@ -113,8 +117,7 @@
}
try {
- // TODO: Do we need to copy these headers somewhere?
- new InternetHeaders(stream);
+ message.put(InternetHeaders.class.getName(), new InternetHeaders(stream));
} catch (MessagingException e) {
throw new RuntimeException(e);
}
@@ -188,19 +191,7 @@
throw new RuntimeException(e);
}
- String id = headers.getHeader("Content-ID", null);
- if (id != null && id.startsWith("<")) {
- id = id.substring(1, id.length() - 1);
- } else {
- //no Content-ID, set cxf default ID
- id = "Content-ID: <root.message@cxf.apache.org";
- }
-
- id = URLDecoder.decode(id.startsWith("cid:") ? id.substring(4) : id, "UTF-8");
-
- AttachmentImpl att = new AttachmentImpl(id);
- setupAttachment(att, headers);
- return att;
+ return (AttachmentImpl)createAttachment(headers);
}
private void cacheStreamedAttachments() throws IOException {
@@ -286,33 +277,10 @@
* @return
* @throws IOException
*/
- private void setupAttachment(AttachmentImpl att, InternetHeaders headers) throws IOException {
- MimeBodyPartInputStream partStream = new MimeBodyPartInputStream(stream, boundary, pbAmount);
-
- final String ct = headers.getHeader("Content-Type", null);
-
- boolean quotedPrintable = false;
-
- for (Enumeration<?> e = headers.getAllHeaders(); e.hasMoreElements();) {
- Header header = (Header) e.nextElement();
- if (header.getName().equalsIgnoreCase("Content-Transfer-Encoding")) {
- if (header.getValue().equalsIgnoreCase("binary")) {
- att.setXOP(true);
- } else if (header.getValue().equalsIgnoreCase("quoted-printable")) {
- quotedPrintable = true;
- }
- }
- att.setHeader(header.getName(), header.getValue());
- }
-
- DelegatingInputStream is = new DelegatingInputStream(partStream);
- if (quotedPrintable) {
- DataSource source = new AttachmentDataSource(ct, new QuotedPrintableDecoderStream(is));
- att.setDataHandler(new DataHandler(source));
- } else {
- DataSource source = new AttachmentDataSource(ct, is);
- att.setDataHandler(new DataHandler(source));
- }
+ private Attachment createAttachment(InternetHeaders headers) throws IOException {
+ InputStream partStream =
+ new DelegatingInputStream(new MimeBodyPartInputStream(stream, boundary, pbAmount));
+ return AttachmentUtil.createAttachment(partStream, headers);
}
public boolean isLazyLoading() {
Modified: cxf/trunk/rt/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java?rev=738583&r1=738582&r2=738583&view=diff
==============================================================================
--- cxf/trunk/rt/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java (original)
+++ cxf/trunk/rt/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java Wed Jan 28 18:54:21 2009
@@ -19,17 +19,25 @@
package org.apache.cxf.attachment;
+import java.io.IOException;
+import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
+import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Collection;
+import java.util.Enumeration;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.activation.DataHandler;
+import javax.activation.DataSource;
+import javax.mail.Header;
+import javax.mail.internet.InternetHeaders;
import org.apache.cxf.helpers.HttpHeaderHelper;
import org.apache.cxf.message.Attachment;
@@ -99,4 +107,59 @@
}
return dataHandlers == null ? new LinkedHashMap<String, DataHandler>() : dataHandlers;
}
+
+ public static Attachment createAttachment(InputStream stream, InternetHeaders headers)
+ throws IOException {
+
+ String id = headers.getHeader("Content-ID", null);
+ if (id != null && id.startsWith("<")) {
+ id = id.substring(1, id.length() - 1);
+ } else {
+ //no Content-ID, set cxf default ID
+ id = "Content-ID: <root.message@cxf.apache.org";
+ }
+
+ id = URLDecoder.decode(id.startsWith("cid:") ? id.substring(4) : id, "UTF-8");
+
+ AttachmentImpl att = new AttachmentImpl(id);
+
+ final String ct = headers.getHeader("Content-Type", null);
+
+ boolean quotedPrintable = false;
+
+ for (Enumeration<?> e = headers.getAllHeaders(); e.hasMoreElements();) {
+ Header header = (Header) e.nextElement();
+ if (header.getName().equalsIgnoreCase("Content-Transfer-Encoding")) {
+ if (header.getValue().equalsIgnoreCase("binary")) {
+ att.setXOP(true);
+ } else if (header.getValue().equalsIgnoreCase("quoted-printable")) {
+ quotedPrintable = true;
+ }
+ }
+ att.setHeader(header.getName(), header.getValue());
+ }
+
+ if (quotedPrintable) {
+ DataSource source = new AttachmentDataSource(ct, new QuotedPrintableDecoderStream(stream));
+ att.setDataHandler(new DataHandler(source));
+ } else {
+ DataSource source = new AttachmentDataSource(ct, stream);
+ att.setDataHandler(new DataHandler(source));
+ }
+
+ return att;
+ }
+
+ public static boolean isTypeSupported(String contentType, List<String> types) {
+ if (contentType == null) {
+ return false;
+ }
+ for (String s : types) {
+ if (contentType.indexOf(s) != -1) {
+ return true;
+ }
+ }
+ return false;
+ }
+
}
Modified: cxf/trunk/rt/core/src/main/java/org/apache/cxf/interceptor/AttachmentInInterceptor.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/core/src/main/java/org/apache/cxf/interceptor/AttachmentInInterceptor.java?rev=738583&r1=738582&r2=738583&view=diff
==============================================================================
--- cxf/trunk/rt/core/src/main/java/org/apache/cxf/interceptor/AttachmentInInterceptor.java (original)
+++ cxf/trunk/rt/core/src/main/java/org/apache/cxf/interceptor/AttachmentInInterceptor.java Wed Jan 28 18:54:21 2009
@@ -20,9 +20,12 @@
package org.apache.cxf.interceptor;
import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
import java.util.logging.Logger;
import org.apache.cxf.attachment.AttachmentDeserializer;
+import org.apache.cxf.attachment.AttachmentUtil;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
@@ -32,6 +35,8 @@
private static final Logger LOG = LogUtils.getL7dLogger(AttachmentInInterceptor.class);
+ private static final List<String> TYPES = Collections.singletonList("multipart/related");
+
/**
* contruct the soap message with attachments from mime input stream
*
@@ -49,8 +54,8 @@
}
String contentType = (String) message.get(Message.CONTENT_TYPE);
- if (contentType != null && contentType.toLowerCase().indexOf("multipart/related") != -1) {
- AttachmentDeserializer ad = new AttachmentDeserializer(message);
+ if (AttachmentUtil.isTypeSupported(contentType, getSupportedTypes())) {
+ AttachmentDeserializer ad = new AttachmentDeserializer(message, getSupportedTypes());
try {
ad.initializeAttachments();
} catch (IOException e) {
@@ -62,4 +67,7 @@
public void handleFault(Message messageParam) {
}
+ protected List<String> getSupportedTypes() {
+ return TYPES;
+ }
}
Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/JAXRSBindingFactory.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/JAXRSBindingFactory.java?rev=738583&r1=738582&r2=738583&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/JAXRSBindingFactory.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/JAXRSBindingFactory.java Wed Jan 28 18:54:21 2009
@@ -23,7 +23,6 @@
import org.apache.cxf.binding.Binding;
import org.apache.cxf.binding.xml.XMLBinding;
import org.apache.cxf.binding.xml.interceptor.XMLFaultOutInterceptor;
-import org.apache.cxf.interceptor.AttachmentInInterceptor;
import org.apache.cxf.interceptor.StaxOutInterceptor;
import org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor;
import org.apache.cxf.jaxrs.interceptor.JAXRSOutInterceptor;
@@ -40,8 +39,6 @@
public Binding createBinding(BindingInfo bi) {
XMLBinding binding = new XMLBinding(bi);
-
- binding.getInInterceptors().add(new AttachmentInInterceptor());
binding.getInInterceptors().add(new JAXRSInInterceptor());
binding.getOutInterceptors().add(new JAXRSOutInterceptor());
Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/MessageContext.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/MessageContext.java?rev=738583&r1=738582&r2=738583&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/MessageContext.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/MessageContext.java Wed Jan 28 18:54:21 2009
@@ -35,9 +35,6 @@
*/
public interface MessageContext {
- String INBOUND_MESSAGE_ATTACHMENTS = "org.apache.cxf.jaxrs.attachments.inbound";
- String OUTBOUND_MESSAGE_ATTACHMENTS = "org.apache.cxf.jaxrs.attachments.outbound";
-
Object get(Object key);
void put(Object key, Object value);
Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/MessageContextImpl.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/MessageContextImpl.java?rev=738583&r1=738582&r2=738583&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/MessageContextImpl.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/MessageContextImpl.java Wed Jan 28 18:54:21 2009
@@ -18,16 +18,20 @@
*/
package org.apache.cxf.jaxrs.ext;
+import java.io.IOException;
+import java.io.InputStream;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Collections;
-import java.util.Map;
+import java.util.LinkedList;
+import java.util.List;
-import javax.activation.DataHandler;
+import javax.mail.internet.InternetHeaders;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.SecurityContext;
@@ -36,9 +40,10 @@
import javax.ws.rs.ext.Providers;
import org.apache.cxf.attachment.AttachmentUtil;
-import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.jaxrs.ext.multipart.Attachment;
+import org.apache.cxf.jaxrs.ext.multipart.MultipartBody;
+import org.apache.cxf.jaxrs.interceptor.AttachmentInputInterceptor;
import org.apache.cxf.jaxrs.utils.JAXRSUtils;
-import org.apache.cxf.message.Attachment;
import org.apache.cxf.message.Message;
public class MessageContextImpl implements MessageContext {
@@ -50,8 +55,8 @@
}
public Object get(Object key) {
- if (MessageContext.INBOUND_MESSAGE_ATTACHMENTS.equals(key.toString())) {
- return createAttachments(MessageContext.INBOUND_MESSAGE_ATTACHMENTS);
+ if (MultipartBody.INBOUND_MESSAGE_ATTACHMENTS.equals(key.toString())) {
+ return createAttachments(MultipartBody.INBOUND_MESSAGE_ATTACHMENTS);
}
return m.get(key);
}
@@ -111,19 +116,34 @@
throw new UnsupportedOperationException("MessageContext.put() is not supported yet");
}
- private Map<String, DataHandler> createAttachments(String propertyName) {
+ private MultipartBody createAttachments(String propertyName) {
Object o = m.get(propertyName);
if (o != null) {
- return CastUtils.cast((Map)o);
+ return (MultipartBody)o;
}
- Collection<Attachment> attachments = m.getAttachments();
- if (attachments == null) {
- return Collections.emptyMap();
- }
- attachments.size();
- Map<String, DataHandler> dataHandlers = AttachmentUtil.getDHMap(attachments);
- m.put(propertyName, dataHandlers);
- return dataHandlers;
+ new AttachmentInputInterceptor().handleMessage(m);
+
+ List<Attachment> newAttachments = new LinkedList<Attachment>();
+ try {
+ Attachment first = new Attachment(AttachmentUtil.createAttachment(
+ m.getContent(InputStream.class),
+ (InternetHeaders)m.get(InternetHeaders.class.getName())));
+ newAttachments.add(first);
+ } catch (IOException ex) {
+ throw new WebApplicationException(500);
+ }
+
+ Collection<org.apache.cxf.message.Attachment> childAttachments = m.getAttachments();
+ if (childAttachments == null) {
+ childAttachments = Collections.emptyList();
+ }
+ childAttachments.size();
+ for (org.apache.cxf.message.Attachment a : childAttachments) {
+ newAttachments.add(new Attachment(a));
+ }
+ MultipartBody body = new MultipartBody(newAttachments, getHttpHeaders().getMediaType(), false);
+ m.put(propertyName, body);
+ return body;
}
}
Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/Attachment.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/Attachment.java?rev=738583&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/Attachment.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/Attachment.java Wed Jan 28 18:54:21 2009
@@ -0,0 +1,109 @@
+/**
+ * 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.cxf.jaxrs.ext.multipart;
+
+import java.io.InputStream;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.activation.DataHandler;
+import javax.activation.DataSource;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+
+import org.apache.cxf.jaxrs.impl.MetadataMap;
+
+public class Attachment {
+
+ private DataHandler handler;
+ private MultivaluedMap<String, String> headers = new MetadataMap<String, String>();
+ private String contentId;
+
+ public Attachment(org.apache.cxf.message.Attachment a) {
+ handler = a.getDataHandler();
+ contentId = a.getId();
+ for (Iterator<String> i = a.getHeaderNames(); i.hasNext();) {
+ String name = i.next();
+ headers.add(name, a.getHeader(name));
+ }
+ }
+
+ public Attachment(String id, DataHandler dh, MultivaluedMap<String, String> headers) {
+ handler = dh;
+ contentId = id;
+ this.headers = new MetadataMap<String, String>(headers);
+ }
+
+ public Attachment(String id, DataSource ds, MultivaluedMap<String, String> headers) {
+ this(id, new DataHandler(ds), headers);
+ }
+
+ public Attachment(InputStream is, MultivaluedMap<String, String> headers) {
+ this(headers.getFirst("Content-ID"),
+ new DataHandler(new InputStreamDataSource(is, headers.getFirst("Content-Type"))),
+ headers);
+ }
+
+ public ContentDisposition getContentDisposition() {
+ String header = headers.getFirst("Content-Disposition");
+ return header == null ? null : new ContentDisposition(header);
+ }
+
+ public String getContentId() {
+ return contentId;
+ }
+
+ public MediaType getContentType() {
+ String value = handler.getContentType();
+ return value == null ? MediaType.TEXT_PLAIN_TYPE : MediaType.valueOf(value);
+ }
+
+ public DataHandler getDataHandler() {
+ return handler;
+ }
+
+ public String getHeader(String name) {
+ return headers.getFirst(name);
+ }
+
+ public List<String> getHeaderAsList(String name) {
+ return headers.get(name);
+ }
+
+ public MultivaluedMap<String, String> getHeaders() {
+ return new MetadataMap<String, String>(headers);
+ }
+
+ @Override
+ public int hashCode() {
+ return contentId.hashCode() + 37 * headers.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Attachment)) {
+ return false;
+ }
+
+ Attachment other = (Attachment)o;
+ return contentId.equals(other.contentId) && headers.equals(other.headers);
+ }
+
+
+}
Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/Attachment.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/Attachment.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/ContentDisposition.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/ContentDisposition.java?rev=738583&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/ContentDisposition.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/ContentDisposition.java Wed Jan 28 18:54:21 2009
@@ -0,0 +1,59 @@
+/**
+ * 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.cxf.jaxrs.ext.multipart;
+
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ContentDisposition {
+
+ private List<String> values;
+
+ public ContentDisposition(String value) {
+ values = Arrays.asList(value.split(";"));
+ }
+
+ public String getType() {
+ return values.get(0).trim();
+ }
+
+ public String getParameter(String name) {
+ for (int i = 1; i < values.size(); i++) {
+ String v = values.get(i).trim();
+ if (v.startsWith(name)) {
+ String[] parts = v.split("=");
+ return parts.length == 2 ? parts[1].trim() : "";
+ }
+ }
+ return null;
+ }
+
+ public Map<String, String> getParameters() {
+ Map<String, String> map = new LinkedHashMap<String, String>();
+ for (int i = 1; i < values.size(); i++) {
+ String[] parts = values.get(i).split("=");
+ map.put(parts[0].trim(), parts.length == 2 ? parts[1].trim() : "");
+ }
+ return map;
+ }
+
+}
Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/ContentDisposition.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/ContentDisposition.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/InputStreamDataSource.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/InputStreamDataSource.java?rev=738583&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/InputStreamDataSource.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/InputStreamDataSource.java Wed Jan 28 18:54:21 2009
@@ -0,0 +1,61 @@
+/**
+ * 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.cxf.jaxrs.ext.multipart;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import javax.activation.DataSource;
+
+public class InputStreamDataSource implements DataSource {
+
+ private InputStream is;
+ private String contentType;
+ private String name;
+
+ public InputStreamDataSource(InputStream is, String contentType) {
+ this.is = is;
+ this.contentType = contentType;
+ }
+
+ public InputStreamDataSource(InputStream is, String contentType, String name) {
+ this.is = is;
+ this.contentType = contentType;
+ this.name = name;
+ }
+
+ public String getContentType() {
+ return contentType;
+ }
+
+ public InputStream getInputStream() throws IOException {
+ return is;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public OutputStream getOutputStream() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+}
Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/InputStreamDataSource.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/InputStreamDataSource.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/Multipart.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/Multipart.java?rev=738583&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/Multipart.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/Multipart.java Wed Jan 28 18:54:21 2009
@@ -0,0 +1,32 @@
+/**
+ * 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.cxf.jaxrs.ext.multipart;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.PARAMETER, ElementType.FIELD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Multipart {
+ String value() default "root";
+ String type() default "*/*";
+}
Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/Multipart.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/Multipart.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/MultipartBody.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/MultipartBody.java?rev=738583&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/MultipartBody.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/MultipartBody.java Wed Jan 28 18:54:21 2009
@@ -0,0 +1,77 @@
+/**
+ * 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.cxf.jaxrs.ext.multipart;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.ws.rs.core.MediaType;
+
+public class MultipartBody {
+
+ public static final String INBOUND_MESSAGE_ATTACHMENTS = "org.apache.cxf.jaxrs.attachments.inbound";
+ public static final String OUTBOUND_MESSAGE_ATTACHMENTS = "org.apache.cxf.jaxrs.attachments.outbound";
+
+ private static final MediaType MULTIPART_RELATED_TYPE = MediaType.valueOf("multipart/related");
+ private boolean outbound;
+ private List<Attachment> atts;
+ private MediaType mt;
+
+ public MultipartBody(List<Attachment> atts, MediaType mt, boolean outbound) {
+ this.atts = atts;
+ this.outbound = outbound;
+ this.mt = mt == null ? MULTIPART_RELATED_TYPE : mt;
+ }
+
+ public MultipartBody(List<Attachment> atts, boolean outbound) {
+ this(atts, MULTIPART_RELATED_TYPE, outbound);
+ }
+
+ public MultipartBody(List<Attachment> atts) {
+ this(atts, MULTIPART_RELATED_TYPE, false);
+ }
+
+ public MultipartBody(boolean outbound) {
+ this(new ArrayList<Attachment>(), MULTIPART_RELATED_TYPE, outbound);
+ }
+
+ public MediaType getType() {
+ return mt;
+ }
+
+ public List<Attachment> getAllAttachments() {
+
+ return outbound ? atts : Collections.unmodifiableList(atts);
+ }
+
+ public List<Attachment> getChildAttachments() {
+ List<Attachment> childAtts = new ArrayList<Attachment>();
+ for (int i = 1; i < atts.size(); i++) {
+ childAtts.add(atts.get(i));
+ }
+ return childAtts;
+ }
+
+ public Attachment getRootAttachment() {
+ return atts.size() > 0 ? atts.get(0) : null;
+ }
+
+
+}
Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/MultipartBody.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/MultipartBody.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/HttpHeadersImpl.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/HttpHeadersImpl.java?rev=738583&r1=738582&r2=738583&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/HttpHeadersImpl.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/HttpHeadersImpl.java Wed Jan 28 18:54:21 2009
@@ -68,7 +68,9 @@
}
public MediaType getMediaType() {
- return MediaType.valueOf((String)m.get(Message.CONTENT_TYPE));
+ String value = (String)m.get(Message.CONTENT_TYPE);
+
+ return value == null ? null : MediaType.valueOf(value);
}
public MultivaluedMap<String, String> getRequestHeaders() {
Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/interceptor/AttachmentInputInterceptor.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/interceptor/AttachmentInputInterceptor.java?rev=738583&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/interceptor/AttachmentInputInterceptor.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/interceptor/AttachmentInputInterceptor.java Wed Jan 28 18:54:21 2009
@@ -0,0 +1,45 @@
+/**
+ * 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.cxf.jaxrs.interceptor;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.annotation.Resource;
+
+import org.apache.cxf.interceptor.AttachmentInInterceptor;
+
+public class AttachmentInputInterceptor extends AttachmentInInterceptor {
+
+ private static final List<String> DEFAULT_TYPES =
+ Arrays.asList(new String[]{"multipart/related", "multipart/mixed"});
+
+ private List<String> types = DEFAULT_TYPES;
+
+ @Override
+ protected List<String> getSupportedTypes() {
+ return types;
+ }
+
+ @Resource
+ public void setSupportedTypes(List<String> sTypes) {
+ types = sTypes;
+ }
+}
Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/interceptor/AttachmentInputInterceptor.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/interceptor/AttachmentInputInterceptor.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/ActivationProvider.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/ActivationProvider.java?rev=738583&r1=738582&r2=738583&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/ActivationProvider.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/ActivationProvider.java Wed Jan 28 18:54:21 2009
@@ -23,6 +23,8 @@
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
import javax.activation.DataHandler;
import javax.activation.DataSource;
@@ -35,11 +37,12 @@
import javax.ws.rs.ext.Provider;
import org.apache.cxf.jaxrs.ext.MessageContext;
+import org.apache.cxf.jaxrs.ext.multipart.Attachment;
+import org.apache.cxf.jaxrs.utils.InjectionUtils;
import org.apache.cxf.jaxrs.utils.multipart.AttachmentUtils;
-import org.apache.cxf.jaxrs.utils.multipart.MultipartInfo;
@Provider
-@Consumes("multipart/related")
+@Consumes({"multipart/related", "multipart/mixed" })
public class ActivationProvider implements MessageBodyReader<Object> {
@Context
@@ -47,8 +50,11 @@
public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations,
MediaType mt) {
- if (DataHandler.class.isAssignableFrom(type) || DataSource.class.isAssignableFrom(type)
- || (mt.getType().equals("multipart") && mt.getSubtype().equals("related"))) {
+ if (DataHandler.class.isAssignableFrom(type)
+ || DataSource.class.isAssignableFrom(type)
+ || Attachment.class.isAssignableFrom(type)
+ || (mt.getType().equals("multipart")
+ && (mt.getSubtype().equals("related") || mt.getSubtype().equals("mixed")))) {
return true;
}
return false;
@@ -57,21 +63,44 @@
public Object readFrom(Class<Object> c, Type t, Annotation[] anns, MediaType mt,
MultivaluedMap<String, String> headers, InputStream is)
throws IOException, WebApplicationException {
- MultipartInfo multipart = AttachmentUtils.getMultipart(c, anns, mt, mc, is);
- if (multipart != null) {
- if (InputStream.class.isAssignableFrom(multipart.getPart().getClass())) {
- MessageBodyReader<Object> r =
- mc.getProviders().getMessageBodyReader(c, t, anns, multipart.getType());
- if (r != null) {
- return r.readFrom(c, t, anns, multipart.getType(), headers,
- (InputStream)multipart.getPart());
- }
- } else {
- // it's either DataSource or DataHandler
- return multipart.getPart();
+
+ if (List.class.isAssignableFrom(c)) {
+ Class<?> actual = InjectionUtils.getActualType(t);
+ List<Attachment> infos = AttachmentUtils.getAttachments(mc);
+ if (actual.isAssignableFrom(Attachment.class)) {
+ return infos;
+ }
+ List<Object> objects = new ArrayList<Object>();
+ for (Attachment a : infos) {
+ objects.add(fromAttachment(a, actual, actual, anns));
}
+ return objects;
+ }
+
+ Attachment multipart = AttachmentUtils.getMultipart(c, anns, mt, mc);
+ if (multipart != null) {
+ return fromAttachment(multipart, c, t, anns);
}
throw new WebApplicationException(404);
}
+ @SuppressWarnings("unchecked")
+ private Object fromAttachment(Attachment multipart, Class<?> c, Type t, Annotation anns[])
+ throws IOException {
+ if (DataHandler.class.isAssignableFrom(c)) {
+ return multipart.getDataHandler();
+ } else if (DataSource.class.isAssignableFrom(c)) {
+ return multipart.getDataHandler().getDataSource();
+ } else if (Attachment.class.isAssignableFrom(c)) {
+ return multipart;
+ } else {
+ MessageBodyReader<Object> r =
+ mc.getProviders().getMessageBodyReader((Class)c, t, anns, multipart.getContentType());
+ if (r != null) {
+ return r.readFrom((Class)c, t, anns, multipart.getContentType(), multipart.getHeaders(),
+ multipart.getDataHandler().getInputStream());
+ }
+ }
+ return null;
+ }
}
Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/multipart/AttachmentUtils.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/multipart/AttachmentUtils.java?rev=738583&r1=738582&r2=738583&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/multipart/AttachmentUtils.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/multipart/AttachmentUtils.java Wed Jan 28 18:54:21 2009
@@ -20,23 +20,22 @@
package org.apache.cxf.jaxrs.utils.multipart;
import java.io.IOException;
-import java.io.InputStream;
import java.lang.annotation.Annotation;
+import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.logging.Logger;
-import javax.activation.DataHandler;
-import javax.activation.DataSource;
-import javax.mail.util.ByteArrayDataSource;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import org.apache.cxf.common.i18n.BundleUtils;
import org.apache.cxf.common.logging.LogUtils;
-import org.apache.cxf.helpers.CastUtils;
import org.apache.cxf.jaxrs.ext.MessageContext;
-import org.apache.cxf.jaxrs.ext.MultipartID;
+import org.apache.cxf.jaxrs.ext.multipart.Attachment;
+import org.apache.cxf.jaxrs.ext.multipart.Multipart;
+import org.apache.cxf.jaxrs.ext.multipart.MultipartBody;
import org.apache.cxf.jaxrs.utils.AnnotationUtils;
import org.apache.cxf.jaxrs.utils.JAXRSUtils;
@@ -47,67 +46,51 @@
private AttachmentUtils() {
}
- public static Map<String, DataHandler> getAttachments(MessageContext mc) {
- return CastUtils.cast((Map)mc.get(MessageContext.INBOUND_MESSAGE_ATTACHMENTS));
+ public static Map<String, Attachment> getChildAttachmentsMap(MessageContext mc) {
+ return fromListToMap(getChildAttachments(mc));
}
- // TODO : refactor this 'piece'
- public static MultipartInfo getMultipart(Class<Object> c, Annotation[] anns,
- MediaType mt, MessageContext mc, InputStream is) throws IOException {
- InputStream stream = null;
- MultipartID id = AnnotationUtils.getAnnotation(anns, MultipartID.class);
+ public static List<Attachment> getChildAttachments(MessageContext mc) {
+ return ((MultipartBody)mc.get(MultipartBody.INBOUND_MESSAGE_ATTACHMENTS)).getChildAttachments();
+ }
+
+ public static Map<String, Attachment> getAttachmentsMap(MessageContext mc) {
+ return fromListToMap(getAttachments(mc));
+ }
+
+ public static List<Attachment> getAttachments(MessageContext mc) {
+ return ((MultipartBody)mc.get(MultipartBody.INBOUND_MESSAGE_ATTACHMENTS)).getAllAttachments();
+ }
+
+ public static Attachment getMultipart(Class<Object> c, Annotation[] anns,
+ MediaType mt, MessageContext mc) throws IOException {
+ List<Attachment> infos = AttachmentUtils.getAttachments(mc);
+ Multipart id = AnnotationUtils.getAnnotation(anns, Multipart.class);
if (id != null) {
- String contentId = id.value();
- String rootId = mt.getParameters().get("start");
- if (rootId != null) {
- rootId = rootId.replace("\"", "").replace("'", "");
- if (rootId.equalsIgnoreCase(contentId)) {
- stream = is;
+ for (Attachment a : getAttachments(mc)) {
+ if (a.getContentId().equals(id.value())) {
+ checkMediaTypes(a.getContentType(), id.type());
+ return a;
}
}
- if (stream == null) {
- // TODO: looks like the lazy attachments collection can only be accessed this way
- for (Map.Entry<String, DataHandler> entry : getAttachments(mc).entrySet()) {
- if (entry.getKey().equals(contentId)) {
- DataHandler dh = entry.getValue();
- MediaType handlerType = dh.getContentType() == null
- ? MediaType.WILDCARD_TYPE : MediaType.valueOf(dh.getContentType());
- checkMediaTypes(handlerType, id.type());
- Object o = DataHandler.class.isAssignableFrom(c) ? dh
- : DataSource.class.isAssignableFrom(c) ? dh.getDataSource()
- : dh.getInputStream();
- return new MultipartInfo(o, handlerType);
- }
- }
- org.apache.cxf.common.i18n.Message errorMsg =
- new org.apache.cxf.common.i18n.Message("MULTTIPART_ID_NOT_FOUND",
- BUNDLE,
- contentId,
- mt.toString());
- LOG.warning(errorMsg.toString());
- }
- } else {
- stream = is;
+ org.apache.cxf.common.i18n.Message errorMsg =
+ new org.apache.cxf.common.i18n.Message("MULTTIPART_ID_NOT_FOUND",
+ BUNDLE,
+ id.value(),
+ mt.toString());
+ LOG.warning(errorMsg.toString());
+
}
- if (stream != null) {
- MediaType partType = MediaType.WILDCARD_TYPE;
- String ct = mt.getParameters().get("type");
- if (ct != null) {
- partType = MediaType.valueOf(ct.replace("\"", "").replace("'", ""));
- }
- if (id != null) {
- checkMediaTypes(partType, id.type());
- }
- if (DataSource.class.isAssignableFrom(c)) {
- return new MultipartInfo(new ByteArrayDataSource(stream, mt.toString()), partType);
- } else if (DataHandler.class.isAssignableFrom(c)) {
- return new MultipartInfo(new DataHandler(new ByteArrayDataSource(stream, mt.toString())),
- partType);
- } else {
- return new MultipartInfo(stream, partType);
- }
+
+ return infos.size() > 0 ? infos.get(0) : null;
+ }
+
+ private static Map<String, Attachment> fromListToMap(List<Attachment> atts) {
+ Map<String, Attachment> map = new LinkedHashMap<String, Attachment>();
+ for (Attachment a : atts) {
+ map.put(a.getContentId(), a);
}
- return null;
+ return map;
}
private static void checkMediaTypes(MediaType mt1, String mt2) {
Added: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/multipart/AttachmentTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/multipart/AttachmentTest.java?rev=738583&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/multipart/AttachmentTest.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/multipart/AttachmentTest.java Wed Jan 28 18:54:21 2009
@@ -0,0 +1,46 @@
+/**
+ * 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.cxf.jaxrs.ext.multipart;
+
+import javax.activation.DataHandler;
+import javax.mail.util.ByteArrayDataSource;
+
+import org.apache.cxf.jaxrs.impl.MetadataMap;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class AttachmentTest extends Assert {
+
+
+ @Test
+ public void testGetHeaders() {
+ Attachment a = createAttachment("p1");
+ assertEquals("bar", a.getHeader("foo"));
+ }
+
+ private Attachment createAttachment(String id) {
+ MetadataMap<String, String> map = new MetadataMap<String, String>();
+ map.add("foo", "bar");
+ return new Attachment(id,
+ new DataHandler(new ByteArrayDataSource(new byte[]{1}, "application/octet-stream")),
+ map);
+ }
+}
Propchange: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/multipart/AttachmentTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/multipart/AttachmentTest.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/multipart/ContentDispositionTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/multipart/ContentDispositionTest.java?rev=738583&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/multipart/ContentDispositionTest.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/multipart/ContentDispositionTest.java Wed Jan 28 18:54:21 2009
@@ -0,0 +1,36 @@
+/**
+ * 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.cxf.jaxrs.ext.multipart;
+
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ContentDispositionTest extends Assert {
+
+ @Test
+ public void testContentDisposition() {
+ ContentDisposition cd = new ContentDisposition(" attachment ; bar=foo ; baz = baz1");
+ assertEquals("attachment", cd.getType());
+ assertEquals("foo", cd.getParameter("bar"));
+ assertEquals("baz1", cd.getParameter("baz"));
+ }
+
+}
Propchange: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/multipart/ContentDispositionTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/multipart/ContentDispositionTest.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/multipart/MultipartBodyTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/multipart/MultipartBodyTest.java?rev=738583&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/multipart/MultipartBodyTest.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/multipart/MultipartBodyTest.java Wed Jan 28 18:54:21 2009
@@ -0,0 +1,51 @@
+/**
+ * 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.cxf.jaxrs.ext.multipart;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.activation.DataHandler;
+import javax.mail.util.ByteArrayDataSource;
+
+import org.apache.cxf.jaxrs.impl.MetadataMap;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class MultipartBodyTest extends Assert {
+
+ @Test
+ public void testGetAttachments() {
+ List<Attachment> atts = new ArrayList<Attachment>();
+ atts.add(createAttachment("p1"));
+ atts.add(createAttachment("p2"));
+ MultipartBody b = new MultipartBody(atts);
+ assertEquals(atts, b.getAllAttachments());
+ assertEquals(atts.get(0), b.getRootAttachment());
+ assertEquals(atts.get(1), b.getChildAttachments().get(0));
+ }
+
+ private Attachment createAttachment(String id) {
+ return new Attachment(id,
+ new DataHandler(new ByteArrayDataSource(new byte[]{1}, "application/octet-stream")),
+ new MetadataMap<String, String>());
+ }
+}
Propchange: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/multipart/MultipartBodyTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/multipart/MultipartBodyTest.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Modified: cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultipartTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultipartTest.java?rev=738583&r1=738582&r2=738583&view=diff
==============================================================================
--- cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultipartTest.java (original)
+++ cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultipartTest.java Wed Jan 28 18:54:21 2009
@@ -37,79 +37,119 @@
@BeforeClass
public static void startServers() throws Exception {
assertTrue("server did not launch correctly",
- launchServer(MultipartServer.class));
+ launchServer(MultipartServer.class, true));
}
@Test
public void testBookAsRootAttachmentStreamSource() throws Exception {
- String address = "http://localhost:9080/bookstore/books/stream";
+ String address = "http://localhost:9085/bookstore/books/stream";
doAddBook(address, "attachmentData", 200);
}
@Test
+ public void testBookAsRootAttachmentStreamSourceNoContentId() throws Exception {
+ String address = "http://localhost:9085/bookstore/books/stream";
+ doAddBook(address, "attachmentData3", 200);
+ }
+
+ @Test
public void testBookAsRootAttachmentInputStream() throws Exception {
- String address = "http://localhost:9080/bookstore/books/istream";
+ String address = "http://localhost:9085/bookstore/books/istream";
doAddBook(address, "attachmentData", 200);
}
@Test
public void testBookAsMessageContextDataHandler() throws Exception {
- String address = "http://localhost:9080/bookstore/books/mchandlers";
+ String address = "http://localhost:9085/bookstore/books/mchandlers";
+ doAddBook(address, "attachmentData", 200);
+ }
+
+ @Test
+ public void testBookAsMessageContextAttachments() throws Exception {
+ String address = "http://localhost:9085/bookstore/books/attachments";
+ doAddBook(address, "attachmentData", 200);
+ }
+
+ @Test
+ public void testBookAsMessageContextAttachment() throws Exception {
+ String address = "http://localhost:9085/bookstore/books/attachment";
doAddBook(address, "attachmentData", 200);
}
@Test
public void testAddBookAsRootAttachmentJAXB() throws Exception {
- String address = "http://localhost:9080/bookstore/books/jaxb";
+ String address = "http://localhost:9085/bookstore/books/jaxb";
doAddBook(address, "attachmentData", 200);
}
@Test
public void testAddBookAsDataSource() throws Exception {
- String address = "http://localhost:9080/bookstore/books/dsource";
+ String address = "http://localhost:9085/bookstore/books/dsource";
doAddBook(address, "attachmentData", 200);
}
@Test
public void testAddBookAsDataSource2() throws Exception {
- String address = "http://localhost:9080/bookstore/books/dsource2";
+ String address = "http://localhost:9085/bookstore/books/dsource2";
doAddBook(address, "attachmentData", 200);
}
@Test
public void testAddBookAsJAXB2() throws Exception {
- String address = "http://localhost:9080/bookstore/books/jaxb2";
+ String address = "http://localhost:9085/bookstore/books/jaxb2";
+ doAddBook(address, "attachmentData", 200);
+ }
+
+ @Test
+ public void testAddBookAsListOfAttachments() throws Exception {
+ String address = "http://localhost:9085/bookstore/books/listattachments";
+ doAddBook(address, "attachmentData", 200);
+ }
+
+ @Test
+ public void testAddBookAsListOfStreams() throws Exception {
+ String address = "http://localhost:9085/bookstore/books/lististreams";
doAddBook(address, "attachmentData", 200);
}
@Test
public void testAddBookAsJAXBJSON() throws Exception {
- String address = "http://localhost:9080/bookstore/books/jaxbjson";
+ String address = "http://localhost:9085/bookstore/books/jaxbjson";
doAddBook(address, "attachmentData2", 200);
}
@Test
+ public void testAddBookAsJAXBJSONMixed() throws Exception {
+ String address = "http://localhost:9085/bookstore/books/jaxbjson";
+ doAddBook("multipart/mixed", address, "attachmentData2", 200);
+ }
+
+ @Test
public void testConsumesMismatch() throws Exception {
- String address = "http://localhost:9080/bookstore/books/mismatch1";
+ String address = "http://localhost:9085/bookstore/books/mismatch1";
doAddBook(address, "attachmentData2", 415);
}
@Test
public void testConsumesMismatch2() throws Exception {
- String address = "http://localhost:9080/bookstore/books/mismatch2";
+ String address = "http://localhost:9085/bookstore/books/mismatch2";
doAddBook(address, "attachmentData2", 415);
}
@Test
public void testAddBookAsDataHandler() throws Exception {
- String address = "http://localhost:9080/bookstore/books/dhandler";
+ String address = "http://localhost:9085/bookstore/books/dhandler";
doAddBook(address, "attachmentData", 200);
}
private void doAddBook(String address, String resourceName, int status) throws Exception {
+ doAddBook("multipart/related", address, resourceName, status);
+ }
+
+ private void doAddBook(String type, String address, String resourceName, int status) throws Exception {
PostMethod post = new PostMethod(address);
- String ct = "multipart/related; type=\"text/xml\"; " + "start=\"rootPart\"; "
+ String ct = type + "; type=\"text/xml\"; " + "start=\"rootPart\"; "
+ "boundary=\"----=_Part_4_701508.1145579811786\"";
post.setRequestHeader("Content-Type", ct);
InputStream is =
Modified: cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/MultipartServer.java
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/MultipartServer.java?rev=738583&r1=738582&r2=738583&view=diff
==============================================================================
--- cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/MultipartServer.java (original)
+++ cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/MultipartServer.java Wed Jan 28 18:54:21 2009
@@ -31,7 +31,7 @@
//default lifecycle is per-request, change it to singleton
sf.setResourceProvider(MultipartStore.class,
new SingletonResourceProvider(new MultipartStore()));
- sf.setAddress("http://localhost:9080/");
+ sf.setAddress("http://localhost:9085/");
sf.create();
}
Modified: cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/MultipartStore.java
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/MultipartStore.java?rev=738583&r1=738582&r2=738583&view=diff
==============================================================================
--- cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/MultipartStore.java (original)
+++ cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/MultipartStore.java Wed Jan 28 18:54:21 2009
@@ -21,6 +21,8 @@
import java.io.InputStream;
+import java.util.Collection;
+import java.util.List;
import java.util.Map;
import javax.activation.DataHandler;
@@ -37,7 +39,8 @@
import javax.xml.transform.stream.StreamSource;
import org.apache.cxf.jaxrs.ext.MessageContext;
-import org.apache.cxf.jaxrs.ext.MultipartID;
+import org.apache.cxf.jaxrs.ext.multipart.Attachment;
+import org.apache.cxf.jaxrs.ext.multipart.Multipart;
import org.apache.cxf.jaxrs.utils.multipart.AttachmentUtils;
@Path("/bookstore")
@@ -78,8 +81,8 @@
@Path("/books/jaxb2")
@Consumes("multipart/related;type=\"text/xml\"")
@Produces("text/xml")
- public Response addBookParts(@MultipartID("rootPart") Book b1,
- @MultipartID("book2") Book b2)
+ public Response addBookParts(@Multipart("rootPart") Book b1,
+ @Multipart("book2") Book b2)
throws Exception {
if (b1.equals(b2)) {
throw new WebApplicationException();
@@ -95,8 +98,8 @@
@Path("/books/jaxbjson")
@Produces("text/xml")
public Response addBookJaxbJson(
- @MultipartID(value = "rootPart", type = "text/xml") Book2 b1,
- @MultipartID(value = "book2", type = "application/json") Book b2)
+ @Multipart(value = "rootPart", type = "text/xml") Book2 b1,
+ @Multipart(value = "book2", type = "application/json") Book b2)
throws Exception {
if (!"CXF in Action".equals(b1.getName())
|| !"CXF in Action - 2".equals(b2.getName())) {
@@ -109,8 +112,8 @@
@POST
@Path("/books/dsource2")
@Produces("text/xml")
- public Response addBookFromDataSource2(@MultipartID("rootPart") DataSource ds1,
- @MultipartID("book2") DataSource ds2)
+ public Response addBookFromDataSource2(@Multipart("rootPart") DataSource ds1,
+ @Multipart("book2") DataSource ds2)
throws Exception {
Response r1 = readBookFromInputStream(ds1.getInputStream());
Response r2 = readBookFromInputStream(ds2.getInputStream());
@@ -126,6 +129,42 @@
}
@POST
+ @Path("/books/listattachments")
+ @Produces("text/xml")
+ public Response addBookFromListOfAttachments(List<Attachment> atts)
+ throws Exception {
+ Response r1 = readBookFromInputStream(atts.get(0).getDataHandler().getInputStream());
+ Response r2 = readBookFromInputStream(atts.get(2).getDataHandler().getInputStream());
+ Book b1 = (Book)r1.getEntity();
+ Book b2 = (Book)r2.getEntity();
+ if (b1.equals(b2)) {
+ throw new WebApplicationException();
+ }
+ if (!b1.getName().equals(b2.getName())) {
+ throw new WebApplicationException();
+ }
+ return r1;
+ }
+
+ @POST
+ @Path("/books/lististreams")
+ @Produces("text/xml")
+ public Response addBookFromListOfStreams(List<InputStream> atts)
+ throws Exception {
+ Response r1 = readBookFromInputStream(atts.get(0));
+ Response r2 = readBookFromInputStream(atts.get(2));
+ Book b1 = (Book)r1.getEntity();
+ Book b2 = (Book)r2.getEntity();
+ if (b1.equals(b2)) {
+ throw new WebApplicationException();
+ }
+ if (!b1.getName().equals(b2.getName())) {
+ throw new WebApplicationException();
+ }
+ return r1;
+ }
+
+ @POST
@Path("/books/dhandler")
@Produces("text/xml")
public Response addBookFromDataHandler(DataHandler dh) throws Exception {
@@ -133,13 +172,33 @@
}
@POST
+ @Path("/books/attachment")
+ @Produces("text/xml")
+ public Response addBookFromAttachment(Attachment a) throws Exception {
+ return readBookFromInputStream(a.getDataHandler().getInputStream());
+ }
+
+ @POST
@Path("/books/mchandlers")
@Produces("text/xml")
public Response addBookFromMessageContext() throws Exception {
- Map<String, DataHandler> handlers = AttachmentUtils.getAttachments(context);
- for (Map.Entry<String, DataHandler> entry : handlers.entrySet()) {
+ Map<String, Attachment> handlers = AttachmentUtils.getAttachmentsMap(context);
+ for (Map.Entry<String, Attachment> entry : handlers.entrySet()) {
if (entry.getKey().equals("book2")) {
- return readBookFromInputStream(entry.getValue().getInputStream());
+ return readBookFromInputStream(entry.getValue().getDataHandler().getInputStream());
+ }
+ }
+ throw new WebApplicationException(500);
+ }
+
+ @POST
+ @Path("/books/attachments")
+ @Produces("text/xml")
+ public Response addBookFromAttachments() throws Exception {
+ Collection<Attachment> handlers = AttachmentUtils.getChildAttachments(context);
+ for (Attachment a : handlers) {
+ if (a.getContentId().equals("book2")) {
+ return readBookFromInputStream(a.getDataHandler().getInputStream());
}
}
throw new WebApplicationException(500);
@@ -164,7 +223,7 @@
@POST
@Path("/books/mismatch2")
@Produces("text/xml")
- public Response addBookMismatched2(@MultipartID(value = "rootPart", type = "f/b") Book b) {
+ public Response addBookMismatched2(@Multipart(value = "rootPart", type = "f/b") Book b) {
throw new WebApplicationException();
}
Added: cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/attachmentData3
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/attachmentData3?rev=738583&view=auto
==============================================================================
--- cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/attachmentData3 (added)
+++ cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/attachmentData3 Wed Jan 28 18:54:21 2009
@@ -0,0 +1,15 @@
+
+------=_Part_4_701508.1145579811786
+Content-Type: text/xml
+
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Book><name>CXF in Action - 2</name></Book>
+------=_Part_4_701508.1145579811786
+Content-Type: image/jpeg
+Content-Transfer-Encoding: binary
+
+foobar
+------=_Part_4_701508.1145579811786--
+Content-Type: application/xml
+
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Book><name>CXF in Action - 2</name></Book>
+------=_Part_4_701508.1145579811786--
\ No newline at end of file