Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CAMEL-21461: Add features to Platform Http SB Starter #483

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions components-starter/camel-platform-http-starter/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,24 @@
<version>${rest-assured-version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>${spring-boot-version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-hazelcast</artifactId>
<version>${spring-session-hazelcast-version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
<version>${hazelcast-version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
Expand All @@ -65,6 +83,27 @@
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-jackson-starter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-http-starter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.wiremock.integrations</groupId>
<artifactId>wiremock-spring-boot</artifactId>
<version>${wiremock-spring-boot-version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-jsonpath-starter</artifactId>
<scope>test</scope>
</dependency>
<!--START OF GENERATED CODE-->
<dependency>
<groupId>org.apache.camel.springboot</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,13 @@
"name": "camel.component.platform-http.autowired-enabled",
"type": "java.lang.Boolean",
"description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which then gets configured on the component. This can be used for automatic configuring JDBC data sources, JMS connection factories, AWS Clients, etc.",
"sourceType": "org.apache.camel.component.platform.http.springboot.PlatformHttpComponentConfiguration",
"defaultValue": true
"sourceType": "org.apache.camel.component.platform.http.springboot.PlatformHttpComponentConfiguration"
},
{
"name": "camel.component.platform-http.bridge-error-handler",
"type": "java.lang.Boolean",
"description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions (if possible) occurred while the Camel consumer is trying to pickup incoming messages, or the likes, will now be processed as a message and handled by the routing Error Handler. Important: This is only possible if the 3rd party component allows Camel to be alerted if an exception was thrown. Some components handle this internally only, and therefore bridgeErrorHandler is not possible. In other situations we may improve the Camel component to hook into the 3rd party component and make this possible for future releases. By default the consumer will use the org.apache.camel.spi.ExceptionHandler to deal with exceptions, that will be logged at WARN or ERROR level and ignored.",
"sourceType": "org.apache.camel.component.platform.http.springboot.PlatformHttpComponentConfiguration",
"defaultValue": false
"sourceType": "org.apache.camel.component.platform.http.springboot.PlatformHttpComponentConfiguration"
},
{
"name": "camel.component.platform-http.customizer.enabled",
Expand All @@ -48,8 +46,7 @@
"name": "camel.component.platform-http.handle-write-response-error",
"type": "java.lang.Boolean",
"description": "When Camel is complete processing the message, and the HTTP server is writing response. This option controls whether Camel should catch any failure during writing response and store this on the Exchange, which allows onCompletion\/UnitOfWork to regard the Exchange as failed and have access to the caused exception from the HTTP server.",
"sourceType": "org.apache.camel.component.platform.http.springboot.PlatformHttpComponentConfiguration",
"defaultValue": false
"sourceType": "org.apache.camel.component.platform.http.springboot.PlatformHttpComponentConfiguration"
},
{
"name": "camel.component.platform-http.header-filter-strategy",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,19 @@ private RequestMappingInfo asRequestMappingInfo(HttpEndpointModel model) {
}
}

RequestMappingInfo info = RequestMappingInfo.paths(model.getUri())
.methods(methods.toArray(new RequestMethod[0])).options(this.getBuilderConfiguration()).build();
return info;
RequestMappingInfo.Builder info = RequestMappingInfo
.paths(model.getUri())
.methods(methods.toArray(new RequestMethod[0]))
.options(this.getBuilderConfiguration());

if (model.getConsumes() != null) {
info.consumes(model.getConsumes().split(","));
}
if (model.getProduces() != null) {
info.produces(model.getProduces().split(","));
}

return info.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import jakarta.servlet.http.HttpServletResponse;
import org.apache.camel.Exchange;
import org.apache.camel.RuntimeCamelException;
import org.apache.camel.StreamCache;
import org.apache.camel.http.common.HttpBinding;
import org.apache.camel.support.DefaultMessage;
import org.apache.camel.util.ObjectHelper;
Expand Down Expand Up @@ -60,7 +61,12 @@ public void init(Exchange exchange, HttpBinding binding, HttpServletRequest requ
if (flag != null && flag) {
this.setHeader("CamelSkipWwwFormUrlEncoding", Boolean.TRUE);
}

// we need to favor using stream cache so the body can be re-read later when routing the message
StreamCache newBody = camelContext.getTypeConverter().tryConvertTo(StreamCache.class,
exchange, getBody());
if (newBody != null) {
setBody(newBody);
}
binding.readRequest(request, this);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,37 @@

import jakarta.activation.DataHandler;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.camel.Exchange;
import org.apache.camel.InvalidPayloadException;
import org.apache.camel.Message;
import org.apache.camel.NoTypeConversionAvailableException;
import org.apache.camel.RuntimeCamelException;
import org.apache.camel.TypeConverter;
import org.apache.camel.attachment.AttachmentMessage;
import org.apache.camel.attachment.CamelFileDataSource;
import org.apache.camel.component.platform.http.PlatformHttpEndpoint;
import org.apache.camel.converter.stream.CachedOutputStream;
import org.apache.camel.http.base.HttpHelper;
import org.apache.camel.http.common.DefaultHttpBinding;
import org.apache.camel.support.ExchangeHelper;
import org.apache.camel.util.FileUtil;
import org.apache.camel.util.IOHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.support.StandardMultipartHttpServletRequest;

import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Locale;
Expand Down Expand Up @@ -103,4 +120,109 @@ protected void populateAttachments(HttpServletRequest request, Message message)
}
}

public Object parseBody(HttpServletRequest request, Message message) throws IOException {
if (request instanceof StandardMultipartHttpServletRequest) {
return null;
}

return super.parseBody(request, message);
}

protected void doWriteDirectResponse(Message message, HttpServletResponse response, Exchange exchange) throws IOException {
String contentType = (String)message.getHeader("Content-Type", String.class);
if ("application/x-java-serialized-object".equals(contentType)) {
if (!isAllowJavaSerializedObject() && !this.isTransferException()) {
throw new RuntimeCamelException("Content-type application/x-java-serialized-object is not allowed");
} else {
try {
Object object = message.getMandatoryBody(Serializable.class);
org.apache.camel.http.common.HttpHelper.writeObjectToServletResponse(response, object);
} catch (InvalidPayloadException var19) {
InvalidPayloadException e = var19;
throw new IOException(e);
}
}
} else {
InputStream is = null;
if (this.checkChunked(message, exchange)) {
is = message.getBody(InputStream.class);
} else if (!this.isText(contentType)) {
is = exchange.getContext().getTypeConverter().tryConvertTo(InputStream.class, message.getBody());
}

int len;
if (is != null) {
ServletOutputStream os = response.getOutputStream();
if (!this.checkChunked(message, exchange)) {
CachedOutputStream stream = new CachedOutputStream(exchange);

try {
len = this.copyStream(is, stream, response.getBufferSize());
response.setContentLength(len);
OutputStream current = stream.getCurrentStream();
if (current instanceof ByteArrayOutputStream) {
ByteArrayOutputStream bos = (ByteArrayOutputStream)current;
if (LOG.isDebugEnabled()) {
LOG.debug("Streaming (direct) response in non-chunked mode with content-length {}", len);
}

bos.writeTo(os);
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("Streaming response in non-chunked mode with content-length {} and buffer size: {}", len, len);
}

this.copyStream(stream.getInputStream(), os, len);
}
} finally {
IOHelper.close(new Closeable[]{is, os});
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("Streaming response in chunked mode with buffer size {}", response.getBufferSize());
}

this.copyStream(is, os, response.getBufferSize());
}
} else {
Object body = message.getBody();
if (body instanceof String) {
String data = message.getBody(String.class);

if (data != null) {
String charset = ExchangeHelper.getCharsetName(exchange, true);
len = data.getBytes(charset).length;
response.setCharacterEncoding(charset);
response.setContentLength(len);
if (LOG.isDebugEnabled()) {
LOG.debug("Writing response in non-chunked mode as plain text with content-length {} and buffer size: {}", len, response.getBufferSize());
}

try {
response.getWriter().print(data);
} finally {
response.getWriter().flush();
}
}
} else if (body instanceof InputStream) {
InputStream bodyIS = message.getBody(InputStream.class);
bodyIS.transferTo(response.getOutputStream());
} else {
final TypeConverter tc = exchange.getContext().getTypeConverter();
// Try to convert to ByteBuffer for performance reason
final ByteBuffer bb = tc.tryConvertTo(ByteBuffer.class, exchange, body);
if (bb != null) {
response.getOutputStream().write(bb.array());
} else {
try {
final InputStream bodyIS = tc.mandatoryConvertTo(InputStream.class, exchange, body);
bodyIS.transferTo(response.getOutputStream());
} catch (NoTypeConversionAvailableException e) {
throw new RuntimeException(e);
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
Expand All @@ -39,7 +41,8 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@EnableAutoConfiguration

@EnableAutoConfiguration(exclude = {OAuth2ClientAutoConfiguration.class, SecurityAutoConfiguration.class})
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
@CamelSpringBootTest
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = { CamelAutoConfiguration.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
*/
package org.apache.camel.component.platform.http.springboot;

import java.util.concurrent.TimeUnit;
import org.apache.camel.CamelContext;
import org.apache.camel.ServiceStatus;
import org.assertj.core.api.Assertions;
Expand All @@ -25,6 +24,8 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.web.client.TestRestTemplate;

import java.util.concurrent.TimeUnit;

import static org.junit.jupiter.api.Assertions.assertEquals;

abstract class PlatformHttpBase {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
import org.apache.camel.util.IOHelper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.context.annotation.Bean;
Expand All @@ -42,7 +44,7 @@
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertEquals;

@SpringBootApplication
@EnableAutoConfiguration(exclude = {OAuth2ClientAutoConfiguration.class, SecurityAutoConfiguration.class})
@DirtiesContext
@CamelSpringBootTest
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = { CamelAutoConfiguration.class,
Expand Down
Loading