/**
* 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.InputStream;
import java.util.List;
import java.util.Map;
import org.apache.camel.AsyncEndpoint;
import org.apache.camel.Consumer;
import org.apache.camel.Exchange;
import org.apache.camel.ExchangePattern;
import org.apache.camel.Message;
import org.apache.camel.Processor;
import org.apache.camel.Producer;
import org.apache.camel.http.common.cookie.CookieHandler;
import org.apache.camel.impl.DefaultEndpoint;
import org.apache.camel.spi.HeaderFilterStrategy;
import org.apache.camel.spi.HeaderFilterStrategyAware;
import org.apache.camel.spi.Metadata;
import org.apache.camel.spi.UriEndpoint;
import org.apache.camel.spi.UriParam;
import org.apache.camel.spi.UriPath;
import org.apache.camel.util.CollectionStringBuffer;
import org.apache.camel.util.jsse.SSLContextParameters;
import org.restlet.data.Method;
/**
* Component for consuming and producing Restful resources using Restlet.
*/
@UriEndpoint(firstVersion = "2.0.0", scheme = "restlet", title = "Restlet", syntax = "restlet:protocol:host:port/uriPattern",
consumerClass = RestletConsumer.class, label = "rest", lenientProperties = true)
public class RestletEndpoint extends DefaultEndpoint implements AsyncEndpoint, HeaderFilterStrategyAware {
private static final int DEFAULT_PORT = 80;
private static final String DEFAULT_PROTOCOL = "http";
private static final String DEFAULT_HOST = "localhost";
private static final int DEFAULT_SOCKET_TIMEOUT = 30000;
private static final int DEFAULT_CONNECT_TIMEOUT = 30000;
@UriPath(enums = "http,https") @Metadata(required = "true")
private String protocol = DEFAULT_PROTOCOL;
@UriPath @Metadata(required = "true")
private String host = DEFAULT_HOST;
@UriPath(defaultValue = "80") @Metadata(required = "true")
private int port = DEFAULT_PORT;
@UriPath
private String uriPattern;
@UriParam(label = "producer", defaultValue = "" + DEFAULT_SOCKET_TIMEOUT)
private int socketTimeout = DEFAULT_SOCKET_TIMEOUT;
@UriParam(label = "producer", defaultValue = "" + DEFAULT_CONNECT_TIMEOUT)
private int connectTimeout = DEFAULT_CONNECT_TIMEOUT;
@UriParam(defaultValue = "GET", enums = "ALL,CONNECT,DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,TRACE")
private Method restletMethod = Method.GET;
@UriParam(label = "consumer", javaType = "java.lang.String")
private Method[] restletMethods;
@UriParam(label = "consumer")
private List<String> restletUriPatterns;
@UriParam(label = "security")
private Map<String, String> restletRealm;
@UriParam(label = "advanced")
private HeaderFilterStrategy headerFilterStrategy;
@UriParam(label = "advanced")
private RestletBinding restletBinding;
@UriParam(label = "producer", defaultValue = "true")
private boolean throwExceptionOnFailure = true;
@UriParam(label = "consumer,advanced")
private boolean disableStreamCache;
@UriParam(label = "security")
private SSLContextParameters sslContextParameters;
@UriParam(label = "producer,advanced")
private boolean streamRepresentation;
@UriParam(label = "producer,advanced")
private boolean autoCloseStream;
@UriParam(label = "producer")
private CookieHandler cookieHandler;
public RestletEndpoint(RestletComponent component, String remaining) throws Exception {
super(remaining, component);
}
public boolean isSingleton() {
return true;
}
@Override
public boolean isLenientProperties() {
// true to allow dynamic URI options to be configured and passed to external system.
return true;
}
@Override
public Exchange createExchange() {
Exchange exchange = super.createExchange();
if (isDisableStreamCache()) {
exchange.setProperty(Exchange.DISABLE_HTTP_STREAM_CACHE, Boolean.TRUE);
}
return exchange;
}
public Consumer createConsumer(Processor processor) throws Exception {
RestletConsumer answer = new RestletConsumer(this, processor);
configureConsumer(answer);
return answer;
}
public Producer createProducer() throws Exception {
return new RestletProducer(this);
}
public void connect(RestletConsumer restletConsumer) throws Exception {
((RestletComponent) getComponent()).connect(restletConsumer);
}
public void disconnect(RestletConsumer restletConsumer) throws Exception {
((RestletComponent) getComponent()).disconnect(restletConsumer);
}
public Method getRestletMethod() {
return restletMethod;
}
/**
* On a producer endpoint, specifies the request method to use.
* On a consumer endpoint, specifies that the endpoint consumes only restletMethod requests.
*/
public void setRestletMethod(Method restletMethod) {
this.restletMethod = restletMethod;
}
public String getProtocol() {
return protocol;
}
/**
* The protocol to use which is http or https
*/
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public String getHost() {
return host;
}
/**
* The hostname of the restlet service
*/
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
/**
* The port number of the restlet service
*/
public void setPort(int port) {
this.port = port;
}
public int getSocketTimeout() {
return socketTimeout;
}
/**
* The Client socket receive timeout, 0 for unlimited wait.
*/
public void setSocketTimeout(int socketTimeout) {
this.socketTimeout = socketTimeout;
}
public int getConnectTimeout() {
return connectTimeout;
}
/**
* The Client will give up connection if the connection is timeout, 0 for unlimited wait.
*/
public void setConnectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
}
public String getUriPattern() {
return uriPattern;
}
/**
* The resource pattern such as /customer/{id}
*/
public void setUriPattern(String uriPattern) {
this.uriPattern = uriPattern;
}
public RestletBinding getRestletBinding() {
return restletBinding;
}
/**
* To use a custom RestletBinding to bind between Restlet and Camel message.
*/
public void setRestletBinding(RestletBinding restletBinding) {
this.restletBinding = restletBinding;
}
/**
* To use a custom HeaderFilterStrategy to filter header to and from Camel message.
*/
public void setHeaderFilterStrategy(HeaderFilterStrategy headerFilterStrategy) {
this.headerFilterStrategy = headerFilterStrategy;
if (restletBinding instanceof HeaderFilterStrategyAware) {
((HeaderFilterStrategyAware) restletBinding).setHeaderFilterStrategy(headerFilterStrategy);
}
}
public HeaderFilterStrategy getHeaderFilterStrategy() {
return headerFilterStrategy;
}
/**
* To configure the security realms of restlet as a map.
*/
public void setRestletRealm(Map<String, String> restletRealm) {
this.restletRealm = restletRealm;
}
public Map<String, String> getRestletRealm() {
return restletRealm;
}
@Override
public ExchangePattern getExchangePattern() {
// should always use in out for restlet
return ExchangePattern.InOut;
}
/**
* Specify one or more methods separated by commas (e.g. restletMethods=post,put) to be serviced by a restlet consumer endpoint.
* If both restletMethod and restletMethods options are specified, the restletMethod setting is ignored.
* The possible methods are: ALL,CONNECT,DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,TRACE
*/
public void setRestletMethods(Method[] restletMethods) {
this.restletMethods = restletMethods;
}
public Method[] getRestletMethods() {
return restletMethods;
}
/**
* Specify one ore more URI templates to be serviced by a restlet consumer endpoint, using the # notation to
* reference a List<String> in the Camel Registry.
* If a URI pattern has been defined in the endpoint URI, both the URI pattern defined in the endpoint and the restletUriPatterns option will be honored.
*/
public void setRestletUriPatterns(List<String> restletUriPatterns) {
this.restletUriPatterns = restletUriPatterns;
}
public List<String> getRestletUriPatterns() {
return restletUriPatterns;
}
public boolean isThrowExceptionOnFailure() {
return throwExceptionOnFailure;
}
/**
* Whether to throw exception on a producer failure. If this option is false then the http status code is set as a message header which
* can be checked if it has an error value.
*/
public void setThrowExceptionOnFailure(boolean throwExceptionOnFailure) {
this.throwExceptionOnFailure = throwExceptionOnFailure;
}
public boolean isDisableStreamCache() {
return disableStreamCache;
}
/**
* Determines whether or not the raw input stream from Restlet is cached or not
* (Camel will read the stream into a in memory/overflow to file, Stream caching) cache.
* By default Camel will cache the Restlet input stream to support reading it multiple times to ensure Camel
* can retrieve all data from the stream. However you can set this option to true when you for example need
* to access the raw stream, such as streaming it directly to a file or other persistent store.
* DefaultRestletBinding will copy the request input stream into a stream cache and put it into message body
* if this option is false to support reading the stream multiple times.
*/
public void setDisableStreamCache(boolean disableStreamCache) {
this.disableStreamCache = disableStreamCache;
}
public SSLContextParameters getSslContextParameters() {
return sslContextParameters;
}
/**
* To configure security using SSLContextParameters.
*/
public void setSslContextParameters(SSLContextParameters scp) {
this.sslContextParameters = scp;
}
public boolean isStreamRepresentation() {
return streamRepresentation;
}
/**
* Whether to support stream representation as response from calling a REST service using the restlet producer.
* If the response is streaming then this option can be enabled to use an {@link java.io.InputStream} as the
* message body on the Camel {@link Message} body. If using this option you may want to enable the
* autoCloseStream option as well to ensure the input stream is closed when the Camel {@link Exchange}
* is done being routed. However if you need to read the stream outside a Camel route, you may need
* to not auto close the stream.
*/
public void setStreamRepresentation(boolean streamRepresentation) {
this.streamRepresentation = streamRepresentation;
}
public boolean isAutoCloseStream() {
return autoCloseStream;
}
/**
* Whether to auto close the stream representation as response from calling a REST service using the restlet producer.
* If the response is streaming and the option streamRepresentation is enabled then you may want to auto close
* the {@link InputStream} from the streaming response to ensure the input stream is closed when the Camel {@link Exchange}
* is done being routed. However if you need to read the stream outside a Camel route, you may need
* to not auto close the stream.
*/
public void setAutoCloseStream(boolean autoCloseStream) {
this.autoCloseStream = autoCloseStream;
}
public CookieHandler getCookieHandler() {
return cookieHandler;
}
/**
* Configure a cookie handler to maintain a HTTP session
*/
public void setCookieHandler(CookieHandler cookieHandler) {
this.cookieHandler = cookieHandler;
}
@Override
protected void doStart() throws Exception {
if (headerFilterStrategy == null) {
headerFilterStrategy = new RestletHeaderFilterStrategy();
}
if (restletBinding == null) {
restletBinding = new DefaultRestletBinding();
}
if (restletBinding instanceof HeaderFilterStrategyAware) {
((HeaderFilterStrategyAware) restletBinding).setHeaderFilterStrategy(getHeaderFilterStrategy());
}
if (restletBinding instanceof DefaultRestletBinding) {
((DefaultRestletBinding) restletBinding).setStreamRepresentation(isStreamRepresentation());
((DefaultRestletBinding) restletBinding).setAutoCloseStream(isAutoCloseStream());
}
}
@Override
protected void doStop() throws Exception {
// noop
}
}