All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.camel.component.restlet.DefaultRestletBinding Maven / Gradle / Ivy

There is a newer version: 3.0.0-RC2
Show newest version
/**
 * 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.camel.component.restlet;

import java.io.File;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.xml.transform.dom.DOMSource;

import org.apache.camel.Exchange;
import org.apache.camel.Message;
import org.apache.camel.RuntimeCamelException;
import org.apache.camel.StringSource;
import org.apache.camel.TypeConverter;
import org.apache.camel.WrappedFile;
import org.apache.camel.component.file.GenericFile;
import org.apache.camel.spi.HeaderFilterStrategy;
import org.apache.camel.spi.HeaderFilterStrategyAware;
import org.apache.camel.util.IOHelper;
import org.apache.camel.util.MessageHelper;
import org.apache.camel.util.ObjectHelper;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.data.CacheDirective;
import org.restlet.data.ChallengeResponse;
import org.restlet.data.ChallengeScheme;
import org.restlet.data.CharacterSet;
import org.restlet.data.Form;
import org.restlet.data.Header;
import org.restlet.data.MediaType;
import org.restlet.data.Method;
import org.restlet.data.Preference;
import org.restlet.data.Status;
import org.restlet.engine.application.DecodeRepresentation;
import org.restlet.engine.header.HeaderConstants;
import org.restlet.representation.ByteArrayRepresentation;
import org.restlet.representation.EmptyRepresentation;
import org.restlet.representation.FileRepresentation;
import org.restlet.representation.InputRepresentation;
import org.restlet.representation.Representation;
import org.restlet.representation.StreamRepresentation;
import org.restlet.representation.StringRepresentation;
import org.restlet.util.Series;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Default Restlet binding implementation
 */
public class DefaultRestletBinding implements RestletBinding, HeaderFilterStrategyAware {
    private static final Logger LOG = LoggerFactory.getLogger(DefaultRestletBinding.class);
    private static final String RFC_2822_DATE_PATTERN = "EEE, dd MMM yyyy HH:mm:ss Z";
    private HeaderFilterStrategy headerFilterStrategy;
    private boolean streamRepresentation;
    private boolean autoCloseStream;

    public void populateExchangeFromRestletRequest(Request request, Response response, Exchange exchange) throws Exception {
        Message inMessage = exchange.getIn();

        inMessage.setHeader(RestletConstants.RESTLET_REQUEST, request);
        inMessage.setHeader(RestletConstants.RESTLET_RESPONSE, response);

        // extract headers from restlet
        for (Map.Entry entry : request.getAttributes().entrySet()) {
            if (!headerFilterStrategy.applyFilterToExternalHeaders(entry.getKey(), entry.getValue(), exchange)) {
                String key = entry.getKey();
                Object value = entry.getValue();
                if (HeaderConstants.ATTRIBUTE_HEADERS.equalsIgnoreCase(key)) {
                    Series
series = (Series
) value; for (Header header: series) { if (!headerFilterStrategy.applyFilterToExternalHeaders(header.getName(), header.getValue(), exchange)) { inMessage.setHeader(header.getName(), header.getValue()); } } } else { inMessage.setHeader(key, value); } LOG.debug("Populate exchange from Restlet request header: {} value: {}", key, value); } } // copy query string to header String query = request.getResourceRef().getQuery(); if (query != null) { inMessage.setHeader(Exchange.HTTP_QUERY, query); } // copy URI to header inMessage.setHeader(Exchange.HTTP_URI, request.getResourceRef().getIdentifier(true)); // copy HTTP method to header inMessage.setHeader(Exchange.HTTP_METHOD, request.getMethod().toString()); if (!request.isEntityAvailable()) { return; } // only deal with the form if the content type is "application/x-www-form-urlencoded" if (request.getEntity().getMediaType() != null && request.getEntity().getMediaType().equals(MediaType.APPLICATION_WWW_FORM, true)) { Form form = new Form(request.getEntity()); for (String paramName : form.getValuesMap().keySet()) { String[] values = form.getValuesArray(paramName); Object value = null; if (values != null && values.length > 0) { if (values.length == 1) { value = values[0]; } else { value = values; } } if (value == null) { inMessage.setBody(paramName); LOG.debug("Populate exchange from Restlet request body: {}", paramName); } else { if (!headerFilterStrategy.applyFilterToExternalHeaders(paramName, value, exchange)) { inMessage.setHeader(paramName, value); LOG.debug("Populate exchange from Restlet request user header: {} value: {}", paramName, value); } } } } else { InputStream is = request.getEntity().getStream(); Object body = RestletHelper.readResponseBodyFromInputStream(is, exchange); inMessage.setBody(body); } } public void populateRestletRequestFromExchange(Request request, Exchange exchange) { request.setReferrerRef("camel-restlet"); final Method method = request.getMethod(); MediaType mediaType = exchange.getIn().getHeader(Exchange.CONTENT_TYPE, MediaType.class); if (mediaType == null) { mediaType = MediaType.APPLICATION_WWW_FORM; } Form form = null; // Use forms only for PUT, POST and x-www-form-urlencoded if ((Method.PUT == method || Method.POST == method) && MediaType.APPLICATION_WWW_FORM.equals(mediaType, true)) { form = new Form(); if (exchange.getIn().getBody() instanceof Map) { //Body is key value pairs try { Map pairs = exchange.getIn().getBody(Map.class); for (Object key: pairs.keySet()) { Object value = pairs.get(key); form.add(key.toString(), value != null ? value.toString() : null); } } catch (Exception e) { throw new RuntimeCamelException("body for " + MediaType.APPLICATION_WWW_FORM + " request must be Map or string format like name=bob&password=secRet", e); } } else { // use string based for forms String body = exchange.getIn().getBody(String.class); if (body != null) { List pairs = URLEncodedUtils.parse(body, Charset.forName(IOHelper.getCharsetName(exchange, true))); for (NameValuePair p : pairs) { form.add(p.getName(), p.getValue()); } } } } // get outgoing custom http headers from the exchange if they exists Series
restletHeaders = exchange.getIn().getHeader(HeaderConstants.ATTRIBUTE_HEADERS, Series.class); if (restletHeaders == null) { restletHeaders = new Series
(Header.class); request.getAttributes().put(HeaderConstants.ATTRIBUTE_HEADERS, restletHeaders); } else { // if the restlet headers already exists on the exchange, we need to filter them for (String name : restletHeaders.getNames()) { if (headerFilterStrategy.applyFilterToCamelHeaders(name, restletHeaders.getValues(name), exchange)) { restletHeaders.removeAll(name); } } request.getAttributes().put(HeaderConstants.ATTRIBUTE_HEADERS, restletHeaders); // since the restlet headers already exists remove them from the exchange so they don't get added again below // we will get a new set of restlet headers on the response exchange.getIn().removeHeader(HeaderConstants.ATTRIBUTE_HEADERS); } // login and password are filtered by header filter strategy String login = exchange.getIn().getHeader(RestletConstants.RESTLET_LOGIN, String.class); String password = exchange.getIn().getHeader(RestletConstants.RESTLET_PASSWORD, String.class); if (login != null && password != null) { ChallengeResponse authentication = new ChallengeResponse(ChallengeScheme.HTTP_BASIC, login, password); request.setChallengeResponse(authentication); LOG.debug("Basic HTTP Authentication has been applied"); } for (Map.Entry entry : exchange.getIn().getHeaders().entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); if (!headerFilterStrategy.applyFilterToCamelHeaders(key, value, exchange)) { // Use forms only for PUT, POST and x-www-form-urlencoded if (form != null) { if (key.startsWith("org.restlet.")) { // put the org.restlet headers in attributes request.getAttributes().put(key, value); } else { // put the user stuff in the form if (value instanceof Collection) { for (Object v : (Collection) value) { form.add(key, v.toString()); if (!headerFilterStrategy.applyFilterToCamelHeaders(key, value, exchange)) { restletHeaders.set(key, value.toString()); } } } else { //Add headers to headers and to body form.add(key, value.toString()); if (!headerFilterStrategy.applyFilterToCamelHeaders(key, value, exchange)) { restletHeaders.set(key, value.toString()); } } } } else { // For non-form post put all the headers in custom headers if (!headerFilterStrategy.applyFilterToCamelHeaders(key, value, exchange)) { restletHeaders.set(key, value.toString()); } } LOG.debug("Populate Restlet request from exchange header: {} value: {}", key, value); } } if (form != null) { request.setEntity(form.getWebRepresentation()); LOG.debug("Populate Restlet {} request from exchange body as form using media type {}", method, mediaType); } else { // include body if PUT or POST if (request.getMethod() == Method.PUT || request.getMethod() == Method.POST) { Representation body = createRepresentationFromBody(exchange, mediaType); request.setEntity(body); LOG.debug("Populate Restlet {} request from exchange body: {} using media type {}", method, body, mediaType); } else { // no body LOG.debug("Populate Restlet {} request from exchange using media type {}", method, mediaType); request.setEntity(new EmptyRepresentation()); } } // accept String accept = exchange.getIn().getHeader("Accept", String.class); if (accept != null) { MediaType acceptedMediaType = exchange.getContext().getTypeConverter().tryConvertTo(MediaType.class, exchange, accept); if (acceptedMediaType != null) { request.getClientInfo().getAcceptedMediaTypes().add(new Preference(acceptedMediaType)); } } MediaType acceptedMediaType = exchange.getIn().getHeader(Exchange.ACCEPT_CONTENT_TYPE, MediaType.class); if (acceptedMediaType != null) { request.getClientInfo().getAcceptedMediaTypes().add(new Preference(acceptedMediaType)); } } public void populateRestletResponseFromExchange(Exchange exchange, Response response) throws Exception { Message out; if (exchange.isFailed()) { // 500 for internal server error which can be overridden by response code in header response.setStatus(Status.valueOf(500)); Message msg = exchange.hasOut() ? exchange.getOut() : exchange.getIn(); if (msg.isFault()) { out = msg; } else { // print exception as message and stacktrace Exception t = exchange.getException(); StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); t.printStackTrace(pw); response.setEntity(sw.toString(), MediaType.TEXT_PLAIN); return; } } else { out = exchange.hasOut() ? exchange.getOut() : exchange.getIn(); } // get content type MediaType mediaType = out.getHeader(Exchange.CONTENT_TYPE, MediaType.class); if (mediaType == null) { Object body = out.getBody(); mediaType = MediaType.TEXT_PLAIN; if (body instanceof String) { mediaType = MediaType.TEXT_PLAIN; } else if (body instanceof StringSource || body instanceof DOMSource) { mediaType = MediaType.TEXT_XML; } } // get response code Integer responseCode = out.getHeader(Exchange.HTTP_RESPONSE_CODE, Integer.class); if (responseCode != null) { response.setStatus(Status.valueOf(responseCode)); } // set response body according to the message body Object body = out.getBody(); if (body instanceof WrappedFile) { // grab body from generic file holder GenericFile gf = (GenericFile) body; body = gf.getBody(); } if (body == null) { // empty response response.setEntity("", MediaType.TEXT_PLAIN); } else if (body instanceof Response) { // its already a restlet response, so dont do anything LOG.debug("Using existing Restlet Response from exchange body: {}", body); } else if (body instanceof Representation) { response.setEntity(out.getBody(Representation.class)); } else if (body instanceof InputStream) { response.setEntity(new InputRepresentation(out.getBody(InputStream.class), mediaType)); } else if (body instanceof File) { response.setEntity(new FileRepresentation(out.getBody(File.class), mediaType)); } else if (body instanceof byte[]) { byte[] bytes = out.getBody(byte[].class); response.setEntity(new ByteArrayRepresentation(bytes, mediaType, bytes.length)); } else { // fallback and use string String text = out.getBody(String.class); response.setEntity(text, mediaType); } LOG.debug("Populate Restlet response from exchange body: {}", body); if (exchange.getProperty(Exchange.CHARSET_NAME) != null) { CharacterSet cs = CharacterSet.valueOf(exchange.getProperty(Exchange.CHARSET_NAME, String.class)); response.getEntity().setCharacterSet(cs); } // set headers at the end, as the entity must be set first // NOTE: setting HTTP headers on restlet is cumbersome and its API is "weird" and has some flaws // so we need to headers two times, and the 2nd time we add the non-internal headers once more Series
series = new Series
(Header.class); for (Map.Entry entry : out.getHeaders().entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); if (!headerFilterStrategy.applyFilterToCamelHeaders(key, value, exchange)) { boolean added = setResponseHeader(exchange, response, key, value); if (!added) { // we only want non internal headers if (!key.startsWith("Camel") && !key.startsWith("org.restlet")) { String text = exchange.getContext().getTypeConverter().tryConvertTo(String.class, exchange, value); if (text != null) { series.add(key, text); } } } } } // set HTTP headers so we return these in the response if (!series.isEmpty()) { response.getAttributes().put(HeaderConstants.ATTRIBUTE_HEADERS, series); } } public void populateExchangeFromRestletResponse(Exchange exchange, Response response) throws Exception { for (Map.Entry entry : response.getAttributes().entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); if (!headerFilterStrategy.applyFilterToExternalHeaders(key, value, exchange)) { exchange.getOut().setHeader(key, value); LOG.debug("Populate exchange from Restlet response header: {} value: {}", key, value); } } // set response code int responseCode = response.getStatus().getCode(); exchange.getOut().setHeader(Exchange.HTTP_RESPONSE_CODE, responseCode); // set restlet response as header so end user have access to it if needed exchange.getOut().setHeader(RestletConstants.RESTLET_RESPONSE, response); if (response.getEntity() != null) { // get content type MediaType mediaType = response.getEntity().getMediaType(); if (mediaType != null) { LOG.debug("Setting the Content-Type to be {}", mediaType.toString()); exchange.getOut().setHeader(Exchange.CONTENT_TYPE, mediaType.toString()); } if (streamRepresentation && response.getEntity() instanceof StreamRepresentation) { Representation representationDecoded = new DecodeRepresentation(response.getEntity()); InputStream is = representationDecoded.getStream(); exchange.getOut().setBody(is); if (autoCloseStream) { // ensure the input stream is closed when we are done routing exchange.addOnCompletion(new RestletOnCompletion(is)); } } else if (response.getEntity() instanceof Representation) { Representation representationDecoded = new DecodeRepresentation(response.getEntity()); exchange.getOut().setBody(representationDecoded.getText()); } else { // get content text by default String text = response.getEntity().getText(); LOG.debug("Populate exchange from Restlet response: {}", text); exchange.getOut().setBody(text); } } // preserve headers from in by copying any non existing headers // to avoid overriding existing headers with old values MessageHelper.copyHeaders(exchange.getIn(), exchange.getOut(), false); } @SuppressWarnings("unchecked") protected boolean setResponseHeader(Exchange exchange, org.restlet.Response message, String header, Object value) { // there must be a value going forward if (value == null) { return true; } // must put to attributes message.getAttributes().put(header, value); // special for certain headers if (message.getEntity() != null) { // arfg darn restlet you make using your api harder for end users with all this trick just to set those ACL headers if (header.equalsIgnoreCase(HeaderConstants.HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS)) { Boolean bool = exchange.getContext().getTypeConverter().tryConvertTo(Boolean.class, value); if (bool != null) { message.setAccessControlAllowCredentials(bool); } return true; } if (header.equalsIgnoreCase(HeaderConstants.HEADER_ACCESS_CONTROL_ALLOW_HEADERS)) { Set set = convertToStringSet(value, exchange.getContext().getTypeConverter()); message.setAccessControlAllowHeaders(set); return true; } if (header.equalsIgnoreCase(HeaderConstants.HEADER_ACCESS_CONTROL_ALLOW_METHODS)) { Set set = convertToMethodSet(value, exchange.getContext().getTypeConverter()); message.setAccessControlAllowMethods(set); return true; } if (header.equalsIgnoreCase(HeaderConstants.HEADER_ACCESS_CONTROL_ALLOW_ORIGIN)) { String text = exchange.getContext().getTypeConverter().tryConvertTo(String.class, value); if (text != null) { message.setAccessControlAllowOrigin(text); } return true; } if (header.equalsIgnoreCase(HeaderConstants.HEADER_ACCESS_CONTROL_EXPOSE_HEADERS)) { Set set = convertToStringSet(value, exchange.getContext().getTypeConverter()); message.setAccessControlExposeHeaders(set); return true; } if (header.equalsIgnoreCase(HeaderConstants.HEADER_CACHE_CONTROL)) { if (value instanceof List) { message.setCacheDirectives((List) value); } if (value instanceof String) { List list = new ArrayList(); // set the cache control value directive list.add(new CacheDirective((String) value)); message.setCacheDirectives(list); } return true; } if (header.equalsIgnoreCase(HeaderConstants.HEADER_LOCATION)) { String text = exchange.getContext().getTypeConverter().tryConvertTo(String.class, value); if (text != null) { message.setLocationRef(text); } return true; } if (header.equalsIgnoreCase(HeaderConstants.HEADER_EXPIRES)) { if (value instanceof Calendar) { message.getEntity().setExpirationDate(((Calendar) value).getTime()); } else if (value instanceof Date) { message.getEntity().setExpirationDate((Date) value); } else if (value instanceof String) { SimpleDateFormat format = new SimpleDateFormat(RFC_2822_DATE_PATTERN, Locale.ENGLISH); try { Date date = format.parse((String) value); message.getEntity().setExpirationDate(date); } catch (ParseException e) { LOG.debug("Header {} with value {} cannot be converted as a Date. The value will be ignored.", HeaderConstants.HEADER_EXPIRES, value); } } return true; } if (header.equalsIgnoreCase(HeaderConstants.HEADER_LAST_MODIFIED)) { if (value instanceof Calendar) { message.getEntity().setModificationDate(((Calendar) value).getTime()); } else if (value instanceof Date) { message.getEntity().setModificationDate((Date) value); } else if (value instanceof String) { SimpleDateFormat format = new SimpleDateFormat(RFC_2822_DATE_PATTERN, Locale.ENGLISH); try { Date date = format.parse((String) value); message.getEntity().setModificationDate(date); } catch (ParseException e) { LOG.debug("Header {} with value {} cannot be converted as a Date. The value will be ignored.", HeaderConstants.HEADER_LAST_MODIFIED, value); } } return true; } if (header.equalsIgnoreCase(HeaderConstants.HEADER_CONTENT_LENGTH)) { if (value instanceof Long) { message.getEntity().setSize((Long) value); } else if (value instanceof Integer) { message.getEntity().setSize((Integer) value); } else { Long num = exchange.getContext().getTypeConverter().tryConvertTo(Long.class, value); if (num != null) { message.getEntity().setSize(num); } else { LOG.debug("Header {} with value {} cannot be converted as a Long. The value will be ignored.", HeaderConstants.HEADER_CONTENT_LENGTH, value); } } return true; } if (header.equalsIgnoreCase(HeaderConstants.HEADER_CONTENT_TYPE)) { if (value instanceof MediaType) { message.getEntity().setMediaType((MediaType) value); } else { String type = value.toString(); MediaType media = MediaType.valueOf(type); if (media != null) { message.getEntity().setMediaType(media); } else { LOG.debug("Header {} with value {} cannot be converted as a MediaType. The value will be ignored.", HeaderConstants.HEADER_CONTENT_TYPE, value); } } return true; } } return false; } @SuppressWarnings("unchecked") private Set convertToStringSet(Object value, TypeConverter typeConverter) { if (value instanceof Set) { return (Set) value; } Set set = new LinkedHashSet<>(); Iterator it = ObjectHelper.createIterator(value); while (it.hasNext()) { Object next = it.next(); String text = typeConverter.tryConvertTo(String.class, next); if (text != null) { set.add(text.trim()); } } return set; } @SuppressWarnings("unchecked") private Set convertToMethodSet(Object value, TypeConverter typeConverter) { if (value instanceof Set) { return (Set) value; } Set set = new LinkedHashSet<>(); Iterator it = ObjectHelper.createIterator(value); while (it.hasNext()) { Object next = it.next(); String text = typeConverter.tryConvertTo(String.class, next); if (text != null) { Method method = Method.valueOf(text.trim()); // creates new instance only if no matching instance exists set.add(method); } } return set; } protected Representation createRepresentationFromBody(Exchange exchange, MediaType mediaType) { Object body = exchange.getIn().getBody(); if (body == null) { return new EmptyRepresentation(); } // unwrap file if (body instanceof WrappedFile) { body = ((WrappedFile) body).getFile(); } if (body instanceof InputStream) { return new InputRepresentation((InputStream) body, mediaType); } else if (body instanceof File) { return new FileRepresentation((File) body, mediaType); } else if (body instanceof byte[]) { return new ByteArrayRepresentation((byte[]) body, mediaType); } else if (body instanceof String) { return new StringRepresentation((CharSequence) body, mediaType); } // fallback as string body = exchange.getIn().getBody(String.class); if (body != null) { return new StringRepresentation((CharSequence) body, mediaType); } else { return new EmptyRepresentation(); } } public HeaderFilterStrategy getHeaderFilterStrategy() { return headerFilterStrategy; } public void setHeaderFilterStrategy(HeaderFilterStrategy strategy) { headerFilterStrategy = strategy; } public boolean isStreamRepresentation() { return streamRepresentation; } public void setStreamRepresentation(boolean streamRepresentation) { this.streamRepresentation = streamRepresentation; } public boolean isAutoCloseStream() { return autoCloseStream; } public void setAutoCloseStream(boolean autoCloseStream) { this.autoCloseStream = autoCloseStream; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy