Merge branch 'Feature/Add_Rest_Api_with_Java_client' of https://github.com/bsinno/hawkbit.git into Feature/Add_Rest_Api_with_Java_client

Conflicts:
	hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/web/ResourceControllerAutoConfiguration.java


Signed-off-by: Jonathan Philip Knoblauch <JonathanPhilip.Knoblauch@bosch-si.com>
This commit is contained in:
Jonathan Philip Knoblauch
2016-04-22 18:19:20 +02:00
9 changed files with 209 additions and 18 deletions

View File

@@ -71,6 +71,11 @@
<artifactId>feign-jackson</artifactId> <artifactId>feign-jackson</artifactId>
<version>8.14.2</version> <version>8.14.2</version>
</dependency> </dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.5</version>
</dependency>
<dependency> <dependency>
<artifactId>hibernate-validator</artifactId> <artifactId>hibernate-validator</artifactId>
<groupId>org.hibernate</groupId> <groupId>org.hibernate</groupId>

View File

@@ -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.MgmtTargetClientResource;
import org.eclipse.hawkbit.mgmt.client.resource.MgmtTargetTagClientResource; import org.eclipse.hawkbit.mgmt.client.resource.MgmtTargetTagClientResource;
import org.springframework.cloud.netflix.feign.support.ResponseEntityDecoder; 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;
import feign.Feign.Builder; import feign.Feign.Builder;
@@ -47,10 +51,14 @@ public class MgmtDefaultFeignClient {
private final String baseUrl; private final String baseUrl;
public MgmtDefaultFeignClient(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()) feignBuilder = Feign.builder().contract(new IgnoreMultipleConsumersProducersSpringMvcContract())
.requestInterceptor(new ApplicationJsonRequestHeaderInterceptor()).logLevel(Level.FULL) .requestInterceptor(new ApplicationJsonRequestHeaderInterceptor()).logLevel(Level.FULL)
.logger(new Logger.ErrorLogger()).encoder(new JacksonEncoder()) .logger(new Logger.ErrorLogger()).encoder(new JacksonEncoder())
.decoder(new ResponseEntityDecoder(new JacksonDecoder())); .decoder(new ResponseEntityDecoder(new JacksonDecoder(mapper)));
this.baseUrl = baseUrl; this.baseUrl = baseUrl;
} }
@@ -93,7 +101,7 @@ public class MgmtDefaultFeignClient {
public MgmtSoftwareModuleClientResource getMgmtSoftwareModuleClientResource() { public MgmtSoftwareModuleClientResource getMgmtSoftwareModuleClientResource() {
if (mgmtSoftwareModuleClientResource == null) { if (mgmtSoftwareModuleClientResource == null) {
mgmtSoftwareModuleClientResource = feignBuilder.target(MgmtSoftwareModuleClientResource.class, mgmtSoftwareModuleClientResource = feignBuilder.target(MgmtSoftwareModuleClientResource.class,
MgmtSoftwareModuleClientResource.PATH); this.baseUrl + MgmtSoftwareModuleClientResource.PATH);
} }
return mgmtSoftwareModuleClientResource; return mgmtSoftwareModuleClientResource;
} }

View File

@@ -8,12 +8,20 @@
*/ */
package org.eclipse.hawkbit.autoconfigure.web; package org.eclipse.hawkbit.autoconfigure.web;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.hawkbit.ddi.EnableDdiApi; import org.eclipse.hawkbit.ddi.EnableDdiApi;
import org.eclipse.hawkbit.mgmt.annotation.EnableMgmtApi; 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.eclipse.hawkbit.system.annotation.EnableSystemApi;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; 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. * Auto-Configuration for enabling the REST-Resources.
@@ -24,4 +32,29 @@ import org.springframework.context.annotation.Import;
@Import({ EnableDdiApi.class, EnableMgmtApi.class, EnableSystemApi.class }) @Import({ EnableDdiApi.class, EnableMgmtApi.class, EnableSystemApi.class })
public class ResourceControllerAutoConfiguration { 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();
}
} }

View File

@@ -8,9 +8,6 @@
*/ */
package org.eclipse.hawkbit.mgmt.rest.api; 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.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
@@ -40,7 +37,6 @@ public interface MgmtDownloadArtifactRestApi {
@RequestMapping(method = RequestMethod.GET, value = "/{softwareModuleId}/artifacts/{artifactId}/download") @RequestMapping(method = RequestMethod.GET, value = "/{softwareModuleId}/artifacts/{artifactId}/download")
@ResponseBody @ResponseBody
ResponseEntity<Void> downloadArtifact(@PathVariable("softwareModuleId") final Long softwareModuleId, ResponseEntity<Void> downloadArtifact(@PathVariable("softwareModuleId") final Long softwareModuleId,
@PathVariable("artifactId") final Long artifactId, final HttpServletResponse servletResponse, @PathVariable("artifactId") final Long artifactId);
final HttpServletRequest request);
} }

View File

@@ -9,7 +9,6 @@
package org.eclipse.hawkbit.mgmt.rest.resource; package org.eclipse.hawkbit.mgmt.rest.resource;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact; import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtDownloadArtifactRestApi; 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.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.model.LocalArtifact; import org.eclipse.hawkbit.repository.model.LocalArtifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.rest.util.RequestResponseContextHolder;
import org.eclipse.hawkbit.rest.util.RestResourceConversionHelper; import org.eclipse.hawkbit.rest.util.RestResourceConversionHelper;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;
/** /**
* *
*/ */
@RestController @RestController
@Scope(value = WebApplicationContext.SCOPE_REQUEST)
public class MgmtDownloadArtifactResource implements MgmtDownloadArtifactRestApi { public class MgmtDownloadArtifactResource implements MgmtDownloadArtifactRestApi {
@Autowired @Autowired
@@ -38,6 +41,9 @@ public class MgmtDownloadArtifactResource implements MgmtDownloadArtifactRestApi
@Autowired @Autowired
private ArtifactManagement artifactManagement; private ArtifactManagement artifactManagement;
@Autowired
private RequestResponseContextHolder requestResponseContextHolder;
/** /**
* Handles the GET request for downloading an artifact. * Handles the GET request for downloading an artifact.
* *
@@ -55,8 +61,7 @@ public class MgmtDownloadArtifactResource implements MgmtDownloadArtifactRestApi
@Override @Override
@ResponseBody @ResponseBody
public ResponseEntity<Void> downloadArtifact(@PathVariable("softwareModuleId") final Long softwareModuleId, public ResponseEntity<Void> downloadArtifact(@PathVariable("softwareModuleId") final Long softwareModuleId,
@PathVariable("artifactId") final Long artifactId, final HttpServletResponse servletResponse, @PathVariable("artifactId") final Long artifactId) {
final HttpServletRequest request) {
final SoftwareModule module = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, artifactId); final SoftwareModule module = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, artifactId);
if (null == module || !module.getLocalArtifact(artifactId).isPresent()) { if (null == module || !module.getLocalArtifact(artifactId).isPresent()) {
@@ -65,13 +70,14 @@ public class MgmtDownloadArtifactResource implements MgmtDownloadArtifactRestApi
final LocalArtifact artifact = module.getLocalArtifact(artifactId).get(); final LocalArtifact artifact = module.getLocalArtifact(artifactId).get();
final DbArtifact file = artifactManagement.loadLocalArtifactBinary(artifact); final DbArtifact file = artifactManagement.loadLocalArtifactBinary(artifact);
final HttpServletRequest request = requestResponseContextHolder.getHttpServletRequest();
final String ifMatch = request.getHeader("If-Match"); final String ifMatch = request.getHeader("If-Match");
if (ifMatch != null && !RestResourceConversionHelper.matchesHttpHeader(ifMatch, artifact.getSha1Hash())) { if (ifMatch != null && !RestResourceConversionHelper.matchesHttpHeader(ifMatch, artifact.getSha1Hash())) {
return new ResponseEntity<>(HttpStatus.PRECONDITION_FAILED); return new ResponseEntity<>(HttpStatus.PRECONDITION_FAILED);
} }
return RestResourceConversionHelper.writeFileResponse(artifact, servletResponse, request, file); return RestResourceConversionHelper.writeFileResponse(artifact,
requestResponseContextHolder.getHttpServletResponse(), request, file);
} }

View File

@@ -145,8 +145,8 @@ public final class MgmtSoftwareModuleMapper {
response.add(linkTo(methodOn(MgmtSoftwareModuleRestApi.class).getSoftwareModule(response.getModuleId())) response.add(linkTo(methodOn(MgmtSoftwareModuleRestApi.class).getSoftwareModule(response.getModuleId()))
.withRel("self")); .withRel("self"));
response.add(linkTo( response.add(linkTo(methodOn(MgmtSoftwareModuleTypeRestApi.class)
methodOn(MgmtSoftwareModuleTypeRestApi.class).getSoftwareModuleType(baseSofwareModule.getType().getId())) .getSoftwareModuleType(baseSofwareModule.getType().getId()))
.withRel(MgmtRestConstants.SOFTWAREMODULE_V1_TYPE)); .withRel(MgmtRestConstants.SOFTWAREMODULE_V1_TYPE));
response.add(linkTo(methodOn(MgmtSoftwareModuleResource.class).getMetadata(response.getModuleId(), response.add(linkTo(methodOn(MgmtSoftwareModuleResource.class).getMetadata(response.getModuleId(),
@@ -176,13 +176,12 @@ public final class MgmtSoftwareModuleMapper {
MgmtRestModelMapper.mapBaseToBase(artifactRest, artifact); MgmtRestModelMapper.mapBaseToBase(artifactRest, artifact);
artifactRest.add(linkTo(methodOn(MgmtSoftwareModuleRestApi.class).getArtifact(artifact.getSoftwareModule().getId(), artifactRest.add(linkTo(methodOn(MgmtSoftwareModuleRestApi.class)
artifact.getId())).withRel("self")); .getArtifact(artifact.getSoftwareModule().getId(), artifact.getId())).withRel("self"));
if (artifact instanceof LocalArtifact) { if (artifact instanceof LocalArtifact) {
artifactRest.add(linkTo(methodOn(MgmtDownloadArtifactResource.class) artifactRest.add(linkTo(methodOn(MgmtDownloadArtifactResource.class)
.downloadArtifact(artifact.getSoftwareModule().getId(), artifact.getId(), null, null)) .downloadArtifact(artifact.getSoftwareModule().getId(), artifact.getId())).withRel("download"));
.withRel("download"));
} }
return artifactRest; return artifactRest;

View File

@@ -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<HttpServletResponse> 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;
}
}

View File

@@ -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<HttpServletResponse>, 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;
}
}

View File

@@ -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;
}
}