org.openlmis.fulfillment.service.ObjReferenceExpander.java Source code

Java tutorial

Introduction

Here is the source code for org.openlmis.fulfillment.service.ObjReferenceExpander.java

Source

/*
 * This program is part of the OpenLMIS logistics management information system platform software.
 * Copyright  2017 VillageReach
 *
 * This program is free software: you can redistribute it and/or modify it under the terms
 * of the GNU Affero General Public License as published by the Free Software Foundation, either
 * version 3 of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details. You should have received a copy of
 * the GNU Affero General Public License along with this program. If not, see
 * http://www.gnu.org/licenses. For additional information contact info@OpenLMIS.org.
 */

package org.openlmis.fulfillment.service;

import static org.openlmis.fulfillment.i18n.MessageKeys.ERROR_DTO_EXPANSION_ASSIGNMENT;
import static org.openlmis.fulfillment.i18n.MessageKeys.ERROR_DTO_EXPANSION_CAST;
import static org.openlmis.fulfillment.i18n.MessageKeys.ERROR_DTO_EXPANSION_HREF;
import static org.openlmis.fulfillment.i18n.MessageKeys.ERROR_ENCODING;
import static org.openlmis.fulfillment.service.request.RequestHelper.createEntity;
import static org.openlmis.fulfillment.service.request.RequestHelper.createUri;

import com.google.common.collect.Maps;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.annotation.PostConstruct;
import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.openlmis.fulfillment.service.request.RequestHeaders;
import org.openlmis.fulfillment.web.ValidationException;
import org.openlmis.fulfillment.web.shipment.ShipmentDto;
import org.openlmis.fulfillment.web.util.ObjectReferenceDto;
import org.openlmis.fulfillment.web.util.OrderDto;
import org.openlmis.util.converter.UuidConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestOperations;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriUtils;

@Component
public class ObjReferenceExpander {
    private static final Logger LOGGER = LoggerFactory.getLogger(ObjReferenceExpander.class);
    private static final Map<String, Class> REF_RESOURCES = Maps.newHashMap();

    static {
        REF_RESOURCES.put("shipment", ShipmentDto.class);
        REF_RESOURCES.put("order", OrderDto.class);
    }

    @Autowired
    private AuthService authService;

    private BeanUtilsBean beanUtils;

    private RestOperations restTemplate = new RestTemplate();

    /**
     * Create an instance of the {@link BeanUtilsBean} and register custom converters with it.
     */
    @PostConstruct
    public void registerConverters() {
        beanUtils = BeanUtilsBean.getInstance();
        beanUtils.getConvertUtils().register(new UuidConverter(), UUID.class);
    }

    /**
     * Expands the DTO object. The requirement is that the field names in the {@code expands}
     * list exactly correspond to the field names in the passed DTO object. Moreover, those fields
     * need to extend the {@link ObjectReferenceDto}. If that's the case, this method will query the
     * URL from {@link ObjectReferenceDto#getHref()} and add the retrieved fields to the
     * representation.
     *
     * @param dto the DTO to expand
     * @param expands a set of field names from the passed DTO to expand
     */
    public void expandDto(Object dto, Set<String> expands) {
        if (expands == null) {
            return;
        }

        for (String expand : expands) {
            try {
                String[] elements = expand.split("\\.", 2);
                String field = elements[0];

                ObjectReferenceDto refDto = getObjectReferenceDto(dto, field);
                StringBuilder href = new StringBuilder(getHref(expand, refDto));

                if (elements.length >= 2) {
                    href.append("?expand=")
                            .append(UriUtils.encodeQueryParam(elements[1], StandardCharsets.UTF_8.name()));
                }

                Class<?> responseType = REF_RESOURCES.computeIfAbsent(field, key -> Map.class);
                Object refObj = retrieve(href.toString(), responseType);
                populate(dto, field, refObj);
            } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException ex) {
                throw new ValidationException(ex, ERROR_DTO_EXPANSION_ASSIGNMENT, expand);
            } catch (UnsupportedEncodingException exp) {
                throw new ValidationException(exp, ERROR_ENCODING);
            }
        }
    }

    private ObjectReferenceDto getObjectReferenceDto(Object dto, String expand)
            throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        Object retrievedField = PropertyUtils.getProperty(dto, expand);

        if (!(retrievedField instanceof ObjectReferenceDto)) {
            throw new ValidationException(ERROR_DTO_EXPANSION_CAST, expand);
        }
        return (ObjectReferenceDto) retrievedField;
    }

    private String getHref(String expand, ObjectReferenceDto refDto) {
        String href = refDto.getHref();
        if (StringUtils.isBlank(href)) {
            throw new ValidationException(ERROR_DTO_EXPANSION_HREF, expand);
        }
        return href;
    }

    private <T> T retrieve(String href, Class<T> type) {
        HttpEntity<Object> entity = createEntity(RequestHeaders.init().setAuth(authService.obtainAccessToken()));
        try {
            return restTemplate.exchange(createUri(href), HttpMethod.GET, entity, type).getBody();
        } catch (HttpStatusCodeException ex) {
            // We don't want to stop processing if the referenced instance does not exist.
            if (HttpStatus.NOT_FOUND == ex.getStatusCode()) {
                LOGGER.warn("The instance referenced under {} does not exist.", href);
                return null;
            }

            throw new DataRetrievalException(href, ex);
        }
    }

    private void populate(Object dto, String field, Object refObj)
            throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        if (refObj instanceof Map) {
            Map map = (Map) refObj;

            if (MapUtils.isNotEmpty(map)) {
                ObjectReferenceDto refDto = getObjectReferenceDto(dto, field);
                beanUtils.populate(refDto, map);
            }
        } else if (null != refObj) {
            populate(dto, field, beanUtils.getPropertyUtils().describe(refObj));
        }
    }

}