diff --git a/examples/hawkbit-mgmt-api-client/pom.xml b/examples/hawkbit-mgmt-api-client/pom.xml index 75e520ea6..70b394ac4 100644 --- a/examples/hawkbit-mgmt-api-client/pom.xml +++ b/examples/hawkbit-mgmt-api-client/pom.xml @@ -71,6 +71,11 @@ feign-jackson 8.14.2 + + com.fasterxml.jackson.core + jackson-databind + 2.5.5 + hibernate-validator org.hibernate diff --git a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/MgmtDefaultFeignClient.java b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/MgmtDefaultFeignClient.java index 8bfc2ec52..6425bad4d 100644 --- a/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/MgmtDefaultFeignClient.java +++ b/examples/hawkbit-mgmt-api-client/src/main/java/org/eclipse/hawkbit/mgmt/client/MgmtDefaultFeignClient.java @@ -19,6 +19,10 @@ import org.eclipse.hawkbit.mgmt.client.resource.MgmtSoftwareModuleTypeClientReso import org.eclipse.hawkbit.mgmt.client.resource.MgmtTargetClientResource; import org.eclipse.hawkbit.mgmt.client.resource.MgmtTargetTagClientResource; import org.springframework.cloud.netflix.feign.support.ResponseEntityDecoder; +import org.springframework.hateoas.hal.Jackson2HalModule; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; import feign.Feign; import feign.Feign.Builder; @@ -47,10 +51,14 @@ public class MgmtDefaultFeignClient { private final String baseUrl; public MgmtDefaultFeignClient(final String baseUrl) { + final ObjectMapper mapper = new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .registerModule(new Jackson2HalModule()); + feignBuilder = Feign.builder().contract(new IgnoreMultipleConsumersProducersSpringMvcContract()) .requestInterceptor(new ApplicationJsonRequestHeaderInterceptor()).logLevel(Level.FULL) .logger(new Logger.ErrorLogger()).encoder(new JacksonEncoder()) - .decoder(new ResponseEntityDecoder(new JacksonDecoder())); + .decoder(new ResponseEntityDecoder(new JacksonDecoder(mapper))); this.baseUrl = baseUrl; } @@ -93,7 +101,7 @@ public class MgmtDefaultFeignClient { public MgmtSoftwareModuleClientResource getMgmtSoftwareModuleClientResource() { if (mgmtSoftwareModuleClientResource == null) { mgmtSoftwareModuleClientResource = feignBuilder.target(MgmtSoftwareModuleClientResource.class, - MgmtSoftwareModuleClientResource.PATH); + this.baseUrl + MgmtSoftwareModuleClientResource.PATH); } return mgmtSoftwareModuleClientResource; } diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/web/ResourceControllerAutoConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/web/ResourceControllerAutoConfiguration.java index afb90bd13..ae92d3200 100644 --- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/web/ResourceControllerAutoConfiguration.java +++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/web/ResourceControllerAutoConfiguration.java @@ -8,12 +8,20 @@ */ package org.eclipse.hawkbit.autoconfigure.web; +import javax.servlet.http.HttpServletResponse; + import org.eclipse.hawkbit.ddi.EnableDdiApi; import org.eclipse.hawkbit.mgmt.annotation.EnableMgmtApi; +import org.eclipse.hawkbit.rest.util.FilterHttpResponse; +import org.eclipse.hawkbit.rest.util.HttpResponseFactoryBean; +import org.eclipse.hawkbit.rest.util.RequestResponseContextHolder; import org.eclipse.hawkbit.system.annotation.EnableSystemApi; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Scope; +import org.springframework.web.context.WebApplicationContext; /** * Auto-Configuration for enabling the REST-Resources. @@ -24,4 +32,29 @@ import org.springframework.context.annotation.Import; @Import({ EnableDdiApi.class, EnableMgmtApi.class, EnableSystemApi.class }) public class ResourceControllerAutoConfiguration { + /** + * Create filter for {@link HttpServletResponse}. + */ + @Bean + public FilterHttpResponse filterHttpResponse() { + return new FilterHttpResponse(); + } + + /** + * Create factory bean for {@link HttpServletResponse}. + */ + @Bean + public HttpResponseFactoryBean httpResponseFactoryBean() { + return new HttpResponseFactoryBean(); + } + + /** + * Create factory bean for {@link HttpServletResponse}. + */ + @Bean + @Scope(value = WebApplicationContext.SCOPE_REQUEST) + public RequestResponseContextHolder requestResponseContextHolder() { + return new RequestResponseContextHolder(); + } + } diff --git a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtDownloadArtifactRestApi.java b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtDownloadArtifactRestApi.java index e07d77f2f..4b50dc8f5 100644 --- a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtDownloadArtifactRestApi.java +++ b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtDownloadArtifactRestApi.java @@ -8,9 +8,6 @@ */ package org.eclipse.hawkbit.mgmt.rest.api; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @@ -40,7 +37,6 @@ public interface MgmtDownloadArtifactRestApi { @RequestMapping(method = RequestMethod.GET, value = "/{softwareModuleId}/artifacts/{artifactId}/download") @ResponseBody ResponseEntity downloadArtifact(@PathVariable("softwareModuleId") final Long softwareModuleId, - @PathVariable("artifactId") final Long artifactId, final HttpServletResponse servletResponse, - final HttpServletRequest request); + @PathVariable("artifactId") final Long artifactId); } diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDownloadArtifactResource.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDownloadArtifactResource.java index ddd7180cf..de01a917a 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDownloadArtifactResource.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtDownloadArtifactResource.java @@ -9,7 +9,6 @@ package org.eclipse.hawkbit.mgmt.rest.resource; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.eclipse.hawkbit.artifact.repository.model.DbArtifact; import org.eclipse.hawkbit.mgmt.rest.api.MgmtDownloadArtifactRestApi; @@ -18,18 +17,22 @@ import org.eclipse.hawkbit.repository.SoftwareManagement; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.model.LocalArtifact; import org.eclipse.hawkbit.repository.model.SoftwareModule; +import org.eclipse.hawkbit.rest.util.RequestResponseContextHolder; import org.eclipse.hawkbit.rest.util.RestResourceConversionHelper; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.context.WebApplicationContext; /** * */ @RestController +@Scope(value = WebApplicationContext.SCOPE_REQUEST) public class MgmtDownloadArtifactResource implements MgmtDownloadArtifactRestApi { @Autowired @@ -38,6 +41,9 @@ public class MgmtDownloadArtifactResource implements MgmtDownloadArtifactRestApi @Autowired private ArtifactManagement artifactManagement; + @Autowired + private RequestResponseContextHolder requestResponseContextHolder; + /** * Handles the GET request for downloading an artifact. * @@ -55,8 +61,7 @@ public class MgmtDownloadArtifactResource implements MgmtDownloadArtifactRestApi @Override @ResponseBody public ResponseEntity downloadArtifact(@PathVariable("softwareModuleId") final Long softwareModuleId, - @PathVariable("artifactId") final Long artifactId, final HttpServletResponse servletResponse, - final HttpServletRequest request) { + @PathVariable("artifactId") final Long artifactId) { final SoftwareModule module = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, artifactId); if (null == module || !module.getLocalArtifact(artifactId).isPresent()) { @@ -65,13 +70,14 @@ public class MgmtDownloadArtifactResource implements MgmtDownloadArtifactRestApi final LocalArtifact artifact = module.getLocalArtifact(artifactId).get(); final DbArtifact file = artifactManagement.loadLocalArtifactBinary(artifact); - + final HttpServletRequest request = requestResponseContextHolder.getHttpServletRequest(); final String ifMatch = request.getHeader("If-Match"); if (ifMatch != null && !RestResourceConversionHelper.matchesHttpHeader(ifMatch, artifact.getSha1Hash())) { return new ResponseEntity<>(HttpStatus.PRECONDITION_FAILED); } - return RestResourceConversionHelper.writeFileResponse(artifact, servletResponse, request, file); + return RestResourceConversionHelper.writeFileResponse(artifact, + requestResponseContextHolder.getHttpServletResponse(), request, file); } diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleMapper.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleMapper.java index 38b4ace75..5ca100ca9 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleMapper.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtSoftwareModuleMapper.java @@ -145,8 +145,8 @@ public final class MgmtSoftwareModuleMapper { response.add(linkTo(methodOn(MgmtSoftwareModuleRestApi.class).getSoftwareModule(response.getModuleId())) .withRel("self")); - response.add(linkTo( - methodOn(MgmtSoftwareModuleTypeRestApi.class).getSoftwareModuleType(baseSofwareModule.getType().getId())) + response.add(linkTo(methodOn(MgmtSoftwareModuleTypeRestApi.class) + .getSoftwareModuleType(baseSofwareModule.getType().getId())) .withRel(MgmtRestConstants.SOFTWAREMODULE_V1_TYPE)); response.add(linkTo(methodOn(MgmtSoftwareModuleResource.class).getMetadata(response.getModuleId(), @@ -176,13 +176,12 @@ public final class MgmtSoftwareModuleMapper { MgmtRestModelMapper.mapBaseToBase(artifactRest, artifact); - artifactRest.add(linkTo(methodOn(MgmtSoftwareModuleRestApi.class).getArtifact(artifact.getSoftwareModule().getId(), - artifact.getId())).withRel("self")); + artifactRest.add(linkTo(methodOn(MgmtSoftwareModuleRestApi.class) + .getArtifact(artifact.getSoftwareModule().getId(), artifact.getId())).withRel("self")); if (artifact instanceof LocalArtifact) { artifactRest.add(linkTo(methodOn(MgmtDownloadArtifactResource.class) - .downloadArtifact(artifact.getSoftwareModule().getId(), artifact.getId(), null, null)) - .withRel("download")); + .downloadArtifact(artifact.getSoftwareModule().getId(), artifact.getId())).withRel("download")); } return artifactRest; diff --git a/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/util/FilterHttpResponse.java b/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/util/FilterHttpResponse.java new file mode 100644 index 000000000..7620e7404 --- /dev/null +++ b/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/util/FilterHttpResponse.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.rest.util; + +import java.io.IOException; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletResponse; + +/** + * Filter is needed to autowire the {@link HttpServletResponse}. + * + */ +public class FilterHttpResponse implements Filter { + + private ThreadLocal threadLocalResponse = new ThreadLocal<>(); + + @Override + public void init(final FilterConfig filterConfig) throws ServletException { + // not needed + } + + @Override + public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) + throws IOException, ServletException { + try { + threadLocalResponse.set((HttpServletResponse) response); + chain.doFilter(request, response); + } finally { + threadLocalResponse.remove(); + } + } + + public HttpServletResponse getHttpServletReponse() { + return threadLocalResponse.get(); + } + + @Override + public void destroy() { + threadLocalResponse = null; + } + +} diff --git a/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/util/HttpResponseFactoryBean.java b/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/util/HttpResponseFactoryBean.java new file mode 100644 index 000000000..6df7987e6 --- /dev/null +++ b/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/util/HttpResponseFactoryBean.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.rest.util; + +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.NamedBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +/** + * + * Factory bean to autowire the {@link HttpServletResponse}. + * + */ +public class HttpResponseFactoryBean implements FactoryBean, ApplicationContextAware, NamedBean { + + public static final String FACTORY_BEAN_NAME = "httpResponseFactoryBean"; + + private ApplicationContext applicationContext; + + @Override + public HttpServletResponse getObject() throws Exception { + return applicationContext.getBean(FilterHttpResponse.class).getHttpServletReponse(); + } + + @Override + public Class getObjectType() { + return HttpServletResponse.class; + } + + @Override + public boolean isSingleton() { + return false; + } + + @Override + public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + + @Override + public String getBeanName() { + return FACTORY_BEAN_NAME; + } + +} diff --git a/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/util/RequestResponseContextHolder.java b/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/util/RequestResponseContextHolder.java new file mode 100644 index 000000000..4c2dc5c5a --- /dev/null +++ b/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/util/RequestResponseContextHolder.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2011-2016 Bosch Software Innovations GmbH, Germany. All rights reserved. + */ +package org.eclipse.hawkbit.rest.util; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Copyright (c) 2015 Bosch Software Innovations GmbH and others. + * + * All rights reserved. This program and the accompanying materials are made + * available under the terms of the Eclipse Public License v1.0 which + * accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +public class RequestResponseContextHolder { + + @Autowired + private HttpServletRequest httpServletRequest; + + @Resource(name = HttpResponseFactoryBean.FACTORY_BEAN_NAME) + private HttpServletResponse httpServletResponse; + + public HttpServletRequest getHttpServletRequest() { + return httpServletRequest; + } + + public HttpServletResponse getHttpServletResponse() { + return httpServletResponse; + } +}