Java tutorial
/******************************************************************************* * Educational Online Test Delivery System * Copyright (c) 2013 American Institutes for Research * * Distributed under the AIR Open Source License, Version 1.0 * See accompanying file AIR-License-1_0.txt or at * http://www.smarterapp.org/documents/American_Institutes_for_Research_Open_Source_Software_License.pdf ******************************************************************************/ package org.opentestsystem.authoring.testspecbank.client; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.Maps; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import org.opentestsystem.authoring.testspecbank.client.domain.TestSpecBankClientObj; import org.opentestsystem.authoring.testspecbank.client.domain.TestSpecificationSearchResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.oauth2.client.OAuth2RestTemplate; import org.springframework.stereotype.Component; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestOperations; import org.springframework.web.util.UriComponentsBuilder; import javax.annotation.Resource; import java.net.URI; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.Set; import tds.common.ValidationError; import tds.common.web.resources.NoContentResponseResource; /** * Integrated implementation of the Test Specification Bank client. Talks to a running instance of the Test Specification Bank REST api. */ @Component public class TestSpecBankClient implements TestSpecBankClientInterface { private static final Logger LOGGER = LoggerFactory.getLogger(TestSpecBankClient.class); /** * RestTemplate used to talk to the REST API */ @Resource @Qualifier("tbsRestTemplate") protected RestOperations tsbRestTemplate; /** * RestTemplate used to talk to a REST API using OAuth authentication. * <p> * This rest template is used because it does not rely on the * {@link org.opentestsystem.shared.security.oauth.client.grant.samlbearer.SamlAssertionAccessTokenProvider}. * Using the tsbRestTemplate declared above results in the following error when called from ART: * <pre> * java.lang.String cannot be cast to org.springframework.security.saml.SAMLCredential * </pre> * </p> */ @Resource @Qualifier("oauthRestTemplate") protected OAuth2RestTemplate tsbOauthRestTemplate; /** * Base URI to use to talk to REST API */ @Value("${tsb.tsbUrl:}") private String baseUri; /** * {@link com.fasterxml.jackson.databind.ObjectMapper} configured with the {@link com.fasterxml.jackson.datatype.guava.GuavaModule} */ @Autowired @Qualifier("integrationObjectMapper") private ObjectMapper objectMapper; // tenant is separate as a reminder that tenant is very much required, the contents of the parameter map are optional and not enforced @Override public TestSpecificationSearchResponse getTestSpecificationByTenantSet(final Set<String> tenantIds, Map<String, String[]> parameterMap) { TestSpecificationSearchResponse response; if (parameterMap == null) { parameterMap = Maps.newHashMap(); } final String[] tenants = tenantIds.toArray(new String[0]); if (ArrayUtils.isNotEmpty(tenants)) { parameterMap.put("tenantSet", tenants); } try { return this.tsbRestTemplate.getForObject(this.baseUri + buildTestSpecURL(parameterMap), TestSpecificationSearchResponse.class, convertStringArray(parameterMap)); } catch (final RestClientException e) { response = new TestSpecificationSearchResponse(); LOGGER.error("Error searching for test specifications", e); } return response; } @Override public Optional<ValidationError> deleteTestSpecification(final String testSpecificationKey) { Optional<ValidationError> maybeValidationError = Optional.empty(); final URI uri = UriComponentsBuilder .fromUriString(String.format("%stestSpecification/%s", baseUri, testSpecificationKey)).build() .toUri(); try { tsbOauthRestTemplate.delete(uri); } catch (final HttpClientErrorException hce) { LOGGER.error("Error deleting {0} test specification: ", hce); if (hce.getStatusCode() == HttpStatus.UNPROCESSABLE_ENTITY) { try { // The NoContentResponseResource contains an array of ValidationErrors. If we got to this point, // the TestSpecificationController#deleteTestSpecification endpoint will have returned a // NoContentResponseResource with a single ValidationError describing what went wrong. final NoContentResponseResource response = objectMapper.readValue(hce.getResponseBodyAsString(), NoContentResponseResource.class); maybeValidationError = Optional.of(response.getErrors()[0]); } catch (final Exception mapEx) { LOGGER.error(String.format("Error mapping response %s to ValidationError: ", hce.getResponseBodyAsString()), mapEx); final String errorMessage = mapEx.getMessage() == null ? mapEx.getClass().getName() : mapEx.getMessage(); maybeValidationError = Optional.of(new ValidationError("mapping exception", errorMessage)); } } else { maybeValidationError = Optional.of(new ValidationError("client exception", hce.getMessage())); } } catch (final Exception e) { LOGGER.error("Error deleting {0} test specification: ", e); maybeValidationError = Optional.of(new ValidationError("server exception", e.getMessage())); } return maybeValidationError; } private Map<String, String> convertStringArray(final Map<String, String[]> inParameterMap) { final Map<String, String> ret = new HashMap<String, String>(); if (inParameterMap != null && inParameterMap.size() > 0) { for (final Map.Entry<String, String[]> entry : inParameterMap.entrySet()) { if (entry != null && entry.getValue() != null && entry.getValue().length > 0) { String retValue = ""; if (entry.getValue().length == 1) { retValue = entry.getValue()[0]; } else { retValue = StringUtils.join(entry.getValue(), ","); } ret.put(entry.getKey(), retValue); } } } return ret; } private String buildTestSpecURL(final Map<String, String[]> inParameterMap) { String ret = "testSpecification"; if (inParameterMap != null && inParameterMap.size() > 0) { ret += "?"; for (final String key : inParameterMap.keySet()) { ret += key + "={" + key + "}&"; } } return ret; } @Override public TestSpecBankClientObj publishTestSpecification(final TestSpecBankClientObj testSpecification) { final ResponseEntity<TestSpecBankClientObj> response = this.tsbOauthRestTemplate .postForEntity(this.baseUri + "testSpecification", testSpecification, TestSpecBankClientObj.class); return response.getBody(); } }