Camel saves full http request but I want only attached file - java

I have the following code base:
#Component
public class DummyRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
rest("/upload").post().to("file://rest_files");
}
#Bean
public ServletRegistrationBean servletRegistrationBean() {
SpringServerServlet serverServlet = new SpringServerServlet();
ServletRegistrationBean regBean = new ServletRegistrationBean( serverServlet, "/rest/*");
Map<String,String> params = new HashMap<>();
params.put("org.restlet.component", "restletComponent");
regBean.setInitParameters(params);
return regBean;
}
#Bean
public org.restlet.Component restletComponent() {
return new org.restlet.Component();
}
#Bean
public RestletComponent restletComponentService() {
return new RestletComponent(restletComponent());
}
}
I upload file using postman:
It is actually usual csv.
But when I open file my application stored - I see file with following content:
Obvious that file contains full request information.
How can I save only file without other data from http request?
P.S.
I tried to register callback:
#Override
public void process(Exchange exchange) throws Exception {
System.out.println(exchange);
final MultipartFile mandatoryBody = exchange.getIn().getBody(MultipartFile.class);
but it returns null

Related

Apache Camel: best practice how to consume data from multiple REST endpoints

I am consuming data from REST endpoints (order of 1000+) which all have the same structure:
<server uri>/v1/source/<some ID>
I am using RouteBuilder components like this connecting to the individual endpoint <ID>:
#Component
public class Route_to_<ID> extends RouteBuilder {
#Override
public void configure() throws Exception {
from("timer:mytimer?repeatCount=1") //
.setBody(simple("${null}")) //
.setHeader(Exchange.CONTENT_TYPE, simple("text/event-stream"))
.setHeader("CamelHttpMethod", simple("GET"))
.to(
<server uri>/v1/source/<ID>
+ _deviceName + "::" + _deviceProperty //
+ "?disableStreamCache=true" //
) //
.process(data -> {
... do same stuff for all endpoints ...
});
}
}
The corresponding SpringBootApplicationlooks like this:
#SpringBootApplication
#ComponentScan(basePackages = "my.package.where.components.reside")
public class MyRouteHandler {
}
Is there an elegant way to start all the individual routes to endpoints <ID>in one go using one single SpringBootApplication? Or does every route need an individual SpringBootApplication which is to be started individually?
You could use toD with dynamic uri that gets the version, source and id from message body, headers or exchange properties. You can also use property-placeholders to define host, port and other configs.
Since the REST endpoints all use the same structure you can change the version, source and id and use the same URI for most if not all the REST API calls.
Example:
public class ExampleTest extends CamelTestSupport {
static final String API_DYNAMIC_URI = "https://{{api.uri}}:{{api.port}}/{{api.version}}"
+ "/${exchangeProperty.source}/${exchangeProperty.id}"
+ "?disableStreamCache=true";
#Test
public void exampleTest() throws Exception {
context.adviceWith(context.getRouteDefinition("exampleRoute"),
new AdviceWithRouteBuilder(){
#Override
public void configure() throws Exception {
weaveById("apiEndpoint")
.replace()
.toD("mock:${exchangeProperty.source}/${exchangeProperty.id}")
.setBody().simple("Source: ${exchangeProperty.source} id: ${exchangeProperty.id}");
}
});
Map<String, Object> body1 = new HashMap<>();
body1.put("source", "source1");
body1.put("id", "A");
Map<String, Object> body2 = new HashMap<>();
body2.put("source", "source2");
body2.put("id", "B");
MockEndpoint source1MockEndpoint = getMockEndpoint("mock:source1/A");
source1MockEndpoint.expectedMessageCount(1);
MockEndpoint source2MockEndpoint = getMockEndpoint("mock:source2/B");
source2MockEndpoint.expectedMessageCount(2);
startCamelContext();
template.sendBody("direct:start", body1);
template.sendBody("direct:start", body2);
template.sendBody("direct:start", body2);
source1MockEndpoint.assertIsSatisfied();
source2MockEndpoint.assertIsSatisfied();
}
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:start")
.routeId("exampleRoute")
.setProperty("source").simple("${body['source']}")
.setProperty("id").simple("${body['id']}")
.toD(API_DYNAMIC_URI).id("apiEndpoint")
.log("Received: ${body}");
}
};
}
#Override
protected Properties useOverridePropertiesWithPropertiesComponent() {
Properties properties = new Properties();
properties.put("api.uri", "localhost");
properties.put("api.port", "3000");
properties.put("api.version", "v1");
return properties;
}
#Override
public boolean isUseAdviceWith() {
return true;
}
}

The resource handler is not disabling cache?

I have created a Spring boot 2 and angular application (Book Store application - the admin can add new books or edit existing books). I want to serve images which are kept in resources/static/images path. When I add a new images through api call, it works perfectly, but when I try to replace the image with different image (when trying to edit the book), the image gets replaced with new image when I check the folder through file-explorer, but when I visit the link http://localhost:8181/images/16.png it shows the old image. In eclipse, if I right click the project and click refresh then http://localhost:8181/images/16.png shows correct image.
For preventing this issue, I have written the below code so it prevents caching of static/images, but it's not working.
--resource handler
#Configuration
public class WebConfig implements WebMvcConfigurer{
#Override public void addResourceHandlers(ResourceHandlerRegistry registry) {
// Register resource handler for images
System.out.println("indisde cache images");
registry.addResourceHandler("/images/**").addResourceLocations("classpath:/static/images/").setCachePeriod(0);
}
}
--spring application
#SpringBootApplication
public class BookStoreApplication implements CommandLineRunner {
#Autowired
UserService userService;
public static void main(String[] args) {
SpringApplication.run(BookStoreApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
User user2=new User();
user2.setUserName("admin");
user2.setPassword(SecurityUtility.passwordEncoder().encode("admin"));
user2.setEmail("admin#admin.com");
user2.setEnabled(true);
user2.setFirstName("adminFirstName");
user2.setLastName("adminLastName");
user2.setPhone("223456789");
Role role2=new Role();
role2.setRoleId((long) 2);
role2.setRoleName("ROLE_ADMIN");
UserRole userRole2=new UserRole(user2,role2);
Set<UserRole> userRoles2=new HashSet<UserRole>();
userRoles2.add(userRole2);
userService.CreateUser(user2, userRoles2);
}
}
--controller
#RequestMapping(value = "/add/image", method = RequestMethod.POST)
public ResponseEntity uploadImage(#RequestParam(name = "id") Long id, HttpServletRequest request,
HttpServletResponse response) throws IOException {
try {
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
Iterator<String> imageNames = multipartRequest.getFileNames();
System.out.println(imageNames);
MultipartFile imageMutipart = multipartRequest.getFile(imageNames.next());
byte[] imageBytes = imageMutipart.getBytes();
String imageNameNew = id + ".png";
BufferedOutputStream bout = new BufferedOutputStream(
new FileOutputStream(new File("src/main/resources/static/images/" + imageNameNew)));
bout.write(imageBytes);
bout.flush();
bout.close();
return new ResponseEntity("Upload success", HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity("Upload failed", HttpStatus.BAD_REQUEST);
}
}
--angular service
upload(bookId: number) {
this.makeFileRequest("http://localhost:8181/book/add/image?id="+bookId, [], this.filesToUpload).then((result) => {
console.log(result);
}, (error) => {
console.log(error);
});
}
modifyImage(bookId: number){
if(this.filesToUpload.length>0){
this.makeFileRequest("http://localhost:8181/book/add/image?id="+bookId, [], this.filesToUpload).then((result) => {
console.log(result);
}, (error) => {
console.log(error);
});
}
}
I was able to solve the issue using the below resource handler
#Configuration
public class AdditionalResourceWebConfiguration implements WebMvcConfigurer {
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/media/**").addResourceLocations("file:///" + System.getProperty("user.dir") + "/src/main/media/");
}
}

Spring-boot jersey : resources not autodiscover

I try to use Spring-boot with jetty and jersey.
No problem with the jetty part. I can start server and spring resources are running (trace, metrics,info,beans,....) but my resources didn't run.
My configuration files are :
Launcher.java
#Configuration
#PropertySource("classpath:application.properties")
#EnableAutoConfiguration
#ComponentScan(basePackages = {"com.fdilogbox.report.serveur"})
public class Launcher extends SpringBootServletInitializer {
public static void main(String[] args) throws Exception {
SpringApplication.run(Launcher.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Launcher.class);
}
#Bean
public ServletRegistrationBean jerseyServlet() {
ServletRegistrationBean registration = new ServletRegistrationBean(new ServletContainer(), "/api/*");
registration.addInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS, ResourcesConfiguration.class.getName());
return registration;
}
#Bean
public EmbeddedServletContainerFactory containerFactory() {
final JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory = new JettyEmbeddedServletContainerFactory() {
#Override
protected JettyEmbeddedServletContainer getJettyEmbeddedServletContainer(Server server) {
return new JettyEmbeddedServletContainer(server);
}
};
jettyEmbeddedServletContainerFactory.addServerCustomizers(new JettyConfiguration());
return jettyEmbeddedServletContainerFactory;
}
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
return new PropertySourcesPlaceholderConfigurer();
}
}
JettyConfiguration.java
public class JettyConfiguration implements JettyServerCustomizer {
#Override
public void customize(Server server) {
WebAppContext webAppContext = (WebAppContext) server.getHandler();
try {
// Load configuration from resource file (standard Jetty xml configuration) and configure the context.
createConfiguration("/jetty.xml").configure(webAppContext);
createConfiguration("/jetty-rewrite.xml").configure(server);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private XmlConfiguration createConfiguration(String xml) throws IOException, SAXException {
return new XmlConfiguration(Launcher.class.getResourceAsStream(xml));
}
}
ResourcesConfiguration.java
public class ResourcesConfiguration extends ResourceConfig {
public ResourcesConfiguration() {
super();
PackageNamesScanner resourceFinder = new PackageNamesScanner(new String[]{"com.fdilogbox.report.serveur.business.resources"}, true);
registerFinder(resourceFinder);
register(JacksonFeature.class);
}
}
and my resources file :
#Path("builder")
#Component
public class ReportBuilderResource {
#Autowired
private ReportBuilderService reportBuilderService;
#GET
#Path("list")
#Produces(MediaType.APPLICATION_JSON)
public String[] findAll() {
return reportBuilderService.findAllReport();
}
}
If I try to acces "localhost:9090/api/builder/list" I get an 404 error.
But if I try "localhost:9090/bean" I get all bean on JSon format.
I think I have an error in my conf but I don't know where.
I found my mistake : management port is 9090 but the normal resources port is 8090.

How to disable ErrorPageFilter in Spring Boot?

I'm creating a SOAP service that should be running on Tomcat.
I'm using Spring Boot for my application, similar to:
#Configuration
#EnableAutoConfiguration(exclude = ErrorMvcAutoConfiguration.class)
public class AppConfig {
}
My webservice (example):
#Component
#WebService
public class MyWebservice {
#WebMethod
#WebResult
public String test() {
throw new MyException();
}
}
#WebFault
public class MyException extends Exception {
}
Problem:
Whenever I throw an exception within the webservice class, the following message is logged on the server:
ErrorPageFilter: Cannot forward to error page for request
[/services/MyWebservice] as the response has already been committed.
As a result, the response may have the wrong status code. If your
application is running on WebSphere Application Server you may be able
to resolve this problem by setting
com.ibm.ws.webcontainer.invokeFlushAfterService to false
Question:
How can I prevent this?
To disable the ErrorPageFilter in Spring Boot (tested with 1.3.0.RELEASE), add the following beans to your Spring configuration:
#Bean
public ErrorPageFilter errorPageFilter() {
return new ErrorPageFilter();
}
#Bean
public FilterRegistrationBean disableSpringBootErrorFilter(ErrorPageFilter filter) {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(filter);
filterRegistrationBean.setEnabled(false);
return filterRegistrationBean;
}
The simpliest way to disable ErrorPageFilter is:
#SpringBootApplication
public class App extends SpringBootServletInitializer {
public App() {
super();
setRegisterErrorPageFilter(false); // <- this one
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(App.class);
}
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
#SpringBootApplication
public class MyApplication extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
//set register error pagefilter false
setRegisterErrorPageFilter(false);
builder.sources(MyApplication.class);
return builder;
}
}
The best way is to tell the WebSphere container to stop ErrorPageFiltering. To achieve this we have to define a property in the server.xml file.
<webContainer throwExceptionWhenUnableToCompleteOrDispatch="false" invokeFlushAfterService="false"></webContainer>
Alternatively, you also can disable it in the spring application.properties file
logging.level.org.springframework.boot.context.web.ErrorPageFilter=off
I prefer the first way.Hope this helps.
I found in the sources that the ErrorPageFilter.java has the following code:
private void doFilter(HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws IOException, ServletException {
ErrorWrapperResponse wrapped = new ErrorWrapperResponse(response);
try {
chain.doFilter(request, wrapped);
int status = wrapped.getStatus();
if (status >= 400) {
handleErrorStatus(request, response, status, wrapped.getMessage());
response.flushBuffer();
}
else if (!request.isAsyncStarted() && !response.isCommitted()) {
response.flushBuffer();
}
}
catch (Throwable ex) {
handleException(request, response, wrapped, ex);
response.flushBuffer();
}
}
As you can see when you throw an exception and return a response code >= 400 it will do some code. there should be some additional check if the response was already committed or not.
The way to remove the ErrorPageFilter is like this
protected WebApplicationContext run(SpringApplication application) {
application.getSources().remove(ErrorPageFilter.class);
return super.run(application);
}
Chris
public class Application extends SpringBootServletInitializer
{
private static final Logger logger = LogManager.getLogger(Application.class);
public Application()
{
super();
setRegisterErrorPageFilter(false);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}

Testing Spring MultipartHttpServletRequest

Trying to test a spring controller that we have for multiple file upload. Here is the controller:
#RequestMapping("/vocabularys")
#Controller
public class VocabularyController {
...
The action I want to test:
#RequestMapping(value = "/import", method = {RequestMethod.PUT, RequestMethod.POST})
#ResponseBody
#CacheEvict(value="vocabulary", allEntries=true)
public Object importVocabulary(MultipartHttpServletRequest request, HttpServletResponse response) {
...
The resolver I have in the webmvc-config.xml:
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver"/>
The code works just fine and all. I'm running into problems when I am trying to unit/integration test this.
Here is my attempt at the test:
public class VocabularyControllerTest extends BaseControllerTest {
static final private String AdminUsername = "administrator";
#Test
public void shouldBeAbleToUploadAFile() throws Exception {
createTestWorkspace();
login(AdminUsername, "*");
MockMultipartFile file = new MockMultipartFile("test_vocab.xml", new FileInputStream("src/test/files/acme_vocabulary.xml"));
MockMultipartHttpServletRequestBuilder mockMultipartHttpServletRequestBuilder = (MockMultipartHttpServletRequestBuilder) fileUpload("/vocabularys/import").accept(MediaType.ALL).session(httpSession);
mockMultipartHttpServletRequestBuilder.file(file);
mockMultipartHttpServletRequestBuilder.content("whatever");
ResultActions resultActions = mockMvc.perform(mockMultipartHttpServletRequestBuilder);
resultActions.andExpect(status().isFound());
}
}
Ignore the createWorkspace() and login() and stuff - those are for passing through some security filters.
The relevant part of the BaseControllerTest:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextHierarchy({
#ContextConfiguration(locations = {
"file:src/test/resources/META-INF/spring/applicationContext.xml",
"file:src/test/resources/META-INF/spring/applicationContext-security.xml",
"file:src/main/resources/META-INF/spring/applicationContext-database.xml",
"file:src/main/resources/META-INF/spring/applicationContext-activiti.xml",
"file:src/main/resources/META-INF/spring/applicationContext-cache.xml",
"file:src/main/resources/META-INF/spring/applicationContext-jms.xml",
"file:src/main/resources/META-INF/spring/applicationContext-mail.xml",
"file:src/main/resources/META-INF/spring/applicationContext-mongo.xml"}),
#ContextConfiguration(locations = {
"file:src/main/webapp/WEB-INF/spring/webmvc-config.xml",
"file:src/test/webapp/WEB-INF/spring/applicationContext-filters.xml"})
})
#Transactional
public class BaseControllerTest extends BaseTest {
#Autowired
WebApplicationContext wac;
#Autowired
MockHttpSession httpSession;
#Autowired
MockServletContext servletContext;
#Autowired
OpenEntityManagerInViewFilter openEntityManagerInViewFilter;
#Autowired
HiddenHttpMethodFilter hiddenHttpMethodFilter;
#Autowired
CharacterEncodingFilter characterEncodingFilter;
#Autowired
SessionFilter sessionFilter;
#Autowired
WorkflowAsSessionFilter workflowAsSessionFilter;
#Autowired
FilterChainProxy springSecurityFilterChain;
#Autowired
RequestFilter requestFilter;
MockMvc mockMvc;
protected static final String TestFileDir = "src/test/files/";
#Before
public void setUp() throws Exception {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)
.addFilter(openEntityManagerInViewFilter, "/*")
.addFilter(hiddenHttpMethodFilter, "/*")
.addFilter(characterEncodingFilter, "/*")
.addFilter(sessionFilter, "/*")
.addFilter(workflowAsSessionFilter, "/*")
.addFilter(springSecurityFilterChain, "/*")
.addFilter(requestFilter, "/*")
.build();
servletContext.setContextPath("/");
Session session = Session.findBySessionId(httpSession.getId());
if (session == null) {
session = new Session();
session.setJsessionid(httpSession.getId());
session.persist();
}
}
...
The issue is that when I try debugging this, the perform action on the mockMvc object never hits my controller method. I thought it was an issue getting past our security filters (which is why I have all the login and stuff) but I tested other actions in the vocabulary controller and I am able to hit them just fine.
Thoughts? Ideas? Suggestions?
Alright, found the issue.
Spring's MockMultipartHttpServletRequestBuilder returns a MockHttpMultipartServletRequest object eventually.
What the browser does however is post a multipart-encoded request which then gets picked up and parsed by the CommonsMultipartResolver bean defined in the XML.
In the test however, since we are already posting a MockHttpMultipartServletRequest, we don't want the resolver parsing this, so all we got to do is have a profile where the resolver doesn't kick in.
What we have chosen to do however is end up constructing a MockHttpServletRequest that has multipart encoding and put it through the Spring filters so that we can also integration test the resolver kicking in.
Unfortunately I don't see any support/helper in the Spring testing lib which allows you to take a MockHttpServletRequest and addPart() to it, or something to that effect => handcoded browser emulation function :(
The simple way how to test multipart upload is use StandardServletMultipartResolver.
and for test use this code:
final MockPart profilePicture = new MockPart("profilePicture", "stview.jpg", "image/gif", "dsdsdsd".getBytes());
final MockPart userData = new MockPart("userData", "userData", "application/json", "{\"name\":\"test aida\"}".getBytes());
this.mockMvc.perform(
fileUpload("/endUsers/" + usr.getId().toString()).with(new RequestPostProcessor() {
#Override
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
request.addPart(profilePicture);
request.addPart(userData);
return request;
}
})
MockPart class
public class MockPart extends MockMultipartFile implements Part {
private Map<String, String> headers;
public MockPart(String name, byte[] content) {
super(name, content);
init();
}
public MockPart(String name, InputStream contentStream) throws IOException {
super(name, contentStream);
init();
}
public MockPart(String name, String originalFilename, String contentType, byte[] content) {
super(name, originalFilename, contentType, content);
init();
}
public MockPart(String name, String originalFilename, String contentType, InputStream contentStream) throws IOException {
super(name, originalFilename, contentType, contentStream);
init();
}
public void init() {
this.headers = new HashMap<String, String>();
if (getOriginalFilename() != null) {
this.headers.put("Content-Disposition".toLowerCase(), "form-data; name=\"" + getName() + "\"; filename=\"" + getOriginalFilename() + "\"");
} else {
this.headers.put("Content-Disposition".toLowerCase(), "form-data; name=\"" + getName() + "\"");
}
if (getContentType() != null) {
this.headers.put("Content-Type".toLowerCase(), getContentType());
}
}
#Override
public void write(String fileName) throws IOException {
}
#Override
public void delete() throws IOException {
}
#Override
public String getHeader(String name) {
return this.headers.get(name.toLowerCase());
}
#Override
public Collection<String> getHeaders(String name) {
List<String> res = new ArrayList<String>();
if (getHeader(name) != null) {
res.add(getHeader(name));
}
return res;
}
#Override
public Collection<String> getHeaderNames() {
return this.headers.keySet();
}
}

Categories

Resources