com.htmlhifive.sync.resource.AbstractCrudSyncResource.java Source code

Java tutorial

Introduction

Here is the source code for com.htmlhifive.sync.resource.AbstractCrudSyncResource.java

Source

/*
 * Copyright (C) 2012-2013 NS Solutions Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package com.htmlhifive.sync.resource;

import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;

import com.htmlhifive.resourcefw.config.MessageMetadata;
import com.htmlhifive.resourcefw.exception.AbstractResourceException;
import com.htmlhifive.resourcefw.exception.BadRequestException;
import com.htmlhifive.resourcefw.exception.ConflictException;
import com.htmlhifive.resourcefw.exception.GenericResourceException;
import com.htmlhifive.resourcefw.exception.LockedException;
import com.htmlhifive.resourcefw.exception.NotFoundException;
import com.htmlhifive.resourcefw.exception.NotModifiedException;
import com.htmlhifive.resourcefw.exception.ServiceUnavailableException;
import com.htmlhifive.resourcefw.message.RequestMessage;
import com.htmlhifive.resourcefw.message.RequestMessageUtil;
import com.htmlhifive.resourcefw.resource.AbstractCrudResource;
import com.htmlhifive.sync.config.SyncConfigurationParameter;
import com.htmlhifive.sync.exception.SyncConflictException;
import com.htmlhifive.sync.exception.SyncDuplicateIdConflictException;
import com.htmlhifive.sync.exception.SyncUpdateConflictException;
import com.htmlhifive.sync.resource.common.ResourceItemCommonData;
import com.htmlhifive.sync.resource.common.ResourceItemCommonDataId;
import com.htmlhifive.sync.resource.common.SyncAction;
import com.htmlhifive.sync.resource.update.UpdateStrategy;
import com.htmlhifive.sync.service.SyncRequestCommonData;

/**
 * JPA Entity?CRUD?sync?.<br/>
 * ?()??sync??????.<br/>
 * ????????????????????????.
 *
 * @author kishigam
 * @param <T>(JPA Entity)
 */
public abstract class AbstractCrudSyncResource<T> extends AbstractCrudResource<T> implements SyncResource {

    /**
     * ?(Map)???
     */
    private static final String DOWNLOAD_RESULT_ITEM_KEY = "item";

    /**
     * ?(Map)???
     */
    private static final String DOWNLOAD_RESULT_COMMON_DATA_KEY = "resourceItemCommonData";

    /**
     * sync??????synchronizer.
     */
    private Synchronizer synchronizer;

    /**
     * ???.
     */
    private UpdateStrategy updateStrategy;

    /**
     * {@link Synchronizer Synchronizer}??????.<br/>
     * ???sync?????????????.<br/>
     * ??????{@link SyncConflictException}???.
     *
     * @param requestMessage 
     * @return ?
     */
    public Object upload(RequestMessage requestMessage) throws BadRequestException, ServiceUnavailableException,
            LockedException, NotFoundException, SyncConflictException {

        // ID??
        ResourceItemCommonDataId resourceItemIdObj = (ResourceItemCommonDataId) requestMessage
                .get(synchronizer.getSyncConfigurationParameter().RESOURCE_ITEM_COMMON_DATA_ID);
        if (resourceItemIdObj.getResourceItemId() == null || resourceItemIdObj.getResourceItemId().isEmpty()) {
            // ????ResourceExceptionHandler????
            throw new GenericResourceException(
                    new BadRequestException("Resource item id is needed.", requestMessage));
        }

        // ??
        ResourceItemCommonData clientItemCommon = createResourceItemCommonData(requestMessage);

        ResourceItemCommonData currentItemCommon = getCurrentItemCommon(requestMessage);

        switch (clientItemCommon.getSyncAction()) {
        case CREATE:
            currentItemCommon = doCreate(requestMessage, clientItemCommon, currentItemCommon);
            break;

        case UPDATE:
        case DELETE:
            currentItemCommon = doUpdateOrDelete(requestMessage, clientItemCommon, currentItemCommon);
            break;

        default:
            throw new GenericResourceException("Unknown sync action.");
        }

        // sync?
        synchronizer.modify(currentItemCommon);

        return currentItemCommon;
    }

    /**
     * ?????????????.
     *
     * @param requestMessage 
     * @return 
     */
    private ResourceItemCommonData createResourceItemCommonData(RequestMessage requestMessage)
            throws BadRequestException {

        SyncConfigurationParameter configParam = synchronizer.getSyncConfigurationParameter();

        ResourceItemCommonData common = new ResourceItemCommonData(
                (ResourceItemCommonDataId) requestMessage.get(configParam.RESOURCE_ITEM_COMMON_DATA_ID));

        common.setTargetItemId(getId(requestMessage));

        // syncAction??
        String syncActionStr = (String) requestMessage.get(configParam.SYNC_ACTION);
        if (!isSyncAction(syncActionStr)) {
            throw new BadRequestException("No such sync action. : sync action = " + syncActionStr, requestMessage);
        }

        common.setSyncAction(SyncAction.valueOf(syncActionStr));

        Object lastModifiedObj = requestMessage.get(configParam.LAST_MODIFIED);
        if (lastModifiedObj != null) {
            common.setLastModified(Long.parseLong((String) lastModifiedObj));
        }

        return common;
    }

    /**
     * ??SyncAction????true???.
     *
     * @param syncActionStr syncAction?
     * @return SyncAction???true
     */
    private boolean isSyncAction(String syncActionStr) {

        if (syncActionStr == null)
            return false;

        for (SyncAction syncAction : EnumSet.allOf(SyncAction.class)) {
            if (syncAction.toString().equals(syncActionStr))
                return true;
        }

        return false;
    }

    /**
     * ???????????.<br/>
     * ?????????????????????????.<br/>
     * ????????.
     *
     * @param requestMessage 
     * @return 
     */
    @SuppressWarnings("unchecked")
    private ResourceItemCommonData getCurrentItemCommon(RequestMessage requestMessage)
            throws SyncConflictException, NotFoundException, BadRequestException, LockedException {

        SyncConfigurationParameter configParam = synchronizer.getSyncConfigurationParameter();

        ResourceItemCommonData currentItemCommon;

        // CREATE????
        SyncAction syncAction = SyncAction.valueOf((String) requestMessage.get(configParam.SYNC_ACTION));
        if (syncAction == SyncAction.CREATE) {
            ResourceItemCommonDataId commonDataId = (ResourceItemCommonDataId) requestMessage
                    .get(configParam.RESOURCE_ITEM_COMMON_DATA_ID);
            currentItemCommon = synchronizer.getNew(commonDataId);

            // ?ID???
            if (currentItemCommon.getSyncAction() == SyncAction.DUPLICATE) {
                throw new SyncDuplicateIdConflictException(currentItemCommon.getSyncAction().toString(),
                        findById(requestMessage), configParam, requestMessage);
            }

            return currentItemCommon;
        }

        // CREATE??

        // ???getForUpdate??????
        Object gotCommonDataListObj = requestMessage.get(configParam.RESOURCE_ITEM_COMMON_DATA);
        if (gotCommonDataListObj != null) {

            List<ResourceItemCommonData> commonList = (List<ResourceItemCommonData>) gotCommonDataListObj;
            // ???NotFound
            if (commonList.isEmpty()) {
                throw new NotFoundException("Sync target resource item is not found.", requestMessage);
            }
            return commonList.get(0);

        }

        // ????????????
        ResourceItemCommonDataId commonDataId = (ResourceItemCommonDataId) requestMessage
                .get(configParam.RESOURCE_ITEM_COMMON_DATA_ID);

        currentItemCommon = synchronizer.getForUpdate(commonDataId);

        // ???NotFound
        if (currentItemCommon == null) {
            throw new NotFoundException("Sync target resource item is not found.", requestMessage);
        }

        return currentItemCommon;
    }

    /**
     * ??????????????.<br/>
     * ?????????{@link SyncConflictException}???.
     *
     * @param requestMessage 
     * @param clientItemCommon ???
     * @param currentItemCommon ??????
     * @return 
     */
    private ResourceItemCommonData doCreate(RequestMessage requestMessage, ResourceItemCommonData clientItemCommon,
            ResourceItemCommonData currentItemCommon) throws BadRequestException, ServiceUnavailableException,
            LockedException, NotFoundException, SyncConflictException {

        SyncConfigurationParameter configParam = synchronizer.getSyncConfigurationParameter();

        SyncRequestCommonData requestCommon = (SyncRequestCommonData) requestMessage
                .get(configParam.REQUEST_COMMON_DATA);

        String targetItemId = null;
        try {
            targetItemId = insert(requestMessage);
        } catch (ConflictException e) {
            // sync?ID???
            throw new SyncDuplicateIdConflictException(e, findById(requestMessage), configParam, requestMessage);
        }

        currentItemCommon.setTargetItemId(targetItemId);
        currentItemCommon.modify(clientItemCommon.getSyncAction(), requestCommon.getSyncTime());

        return currentItemCommon;
    }

    /**
     * ?????????????.<br/>
     * ?????????{@link UpdateStrategy}?????.<br/>
     * ????????????????.
     *
     * @param requestMessage 
     * @param clientItemCommon ???
     * @param currentItemCommon ??????
     * @return 
     */
    private ResourceItemCommonData doUpdateOrDelete(RequestMessage requestMessage,
            ResourceItemCommonData clientItemCommon, ResourceItemCommonData currentItemCommon)
            throws BadRequestException, NotFoundException, LockedException, SyncConflictException {

        SyncConfigurationParameter configParam = synchronizer.getSyncConfigurationParameter();

        SyncRequestCommonData requestCommon = (SyncRequestCommonData) requestMessage
                .get(configParam.REQUEST_COMMON_DATA);

        // ???
        SyncAction resolvedSyncAction = clientItemCommon.getSyncAction();
        if (synchronizer.isConflicted(clientItemCommon, currentItemCommon, requestCommon)) {

            T clientItem = RequestMessageUtil.extractObject(requestMessage, getItemType(), getIdFieldName(),
                    getId(requestMessage));

            Object currentItem = findById(requestMessage);

            // UpdateStrategy??????????getUpdateStrategy???
            resolvedSyncAction = getUpdateStrategy().resolveConflict(clientItemCommon, clientItem,
                    currentItemCommon, currentItem);

            if (resolvedSyncAction == SyncAction.CONFLICT) {
                throw new SyncUpdateConflictException(resolvedSyncAction.toString(), currentItem, configParam,
                        requestMessage);
            }
        }

        // ???????
        switch (resolvedSyncAction) {
        case NONE:
            break;
        case UPDATE:
            update(requestMessage);
            break;
        case DELETE:
            remove(requestMessage);
            break;
        default:
            throw new GenericResourceException("Unknown resolved SyncAction. : " + resolvedSyncAction);
        }

        currentItemCommon.modify(resolvedSyncAction, requestCommon.getSyncTime());

        return currentItemCommon;
    }

    /**
     * {@link Synchronizer Synchronizer}??????.<br/>
     * ??????????????????????????.
     *
     * @param requestMessage 
     * @return ??
     * @throws AbstractResourceException
     */
    public Object download(RequestMessage requestMessage) throws AbstractResourceException {

        SyncConfigurationParameter configParam = synchronizer.getSyncConfigurationParameter();

        // lastModified??????????
        long modifiedSince = 0L;
        Object lastModifiedObj = requestMessage.get(configParam.LAST_MODIFIED);
        try {
            if (lastModifiedObj != null) {
                modifiedSince = Long.parseLong((String) lastModifiedObj);
            }
        } catch (NumberFormatException e) {
            throw new BadRequestException("Failed to parse Last modified time. : " + (String) lastModifiedObj,
                    requestMessage);
        }

        String resourceName = ((ResourceItemCommonDataId) requestMessage
                .get(configParam.RESOURCE_ITEM_COMMON_DATA_ID)).getResourceName();

        MessageMetadata messageMetadata = requestMessage.getMessageMetadata();

        // query???downloadByQuery,???downloadById
        Object query = requestMessage.get(messageMetadata.QUERY);
        if (query == null) {
            return downloadById(resourceName, modifiedSince, requestMessage);
        }

        return downloadByQuery(resourceName, modifiedSince, requestMessage);
    }

    /**
     * ID??????.<br/>
     * ???????????????????{@link NotModifiedException}????.
     *
     * @param resourceName ??
     * @param modifiedSince ?
     * @param requestMessage 
     * @return ??
     * @throws AbstractResourceException
     */
    @SuppressWarnings("unchecked")
    private Object downloadById(String resourceName, long modifiedSince, RequestMessage requestMessage)
            throws AbstractResourceException {

        // ????NotFound
        if (!(boolean) exists(requestMessage).get("exists")) {
            throw new NotFoundException("Sync target resource item is not found.", requestMessage);
        }

        SyncConfigurationParameter configParam = synchronizer.getSyncConfigurationParameter();

        List<ResourceItemCommonData> commonList;

        Object gotCommonDataListObj = requestMessage.get(configParam.RESOURCE_ITEM_COMMON_DATA);
        if (gotCommonDataListObj != null) {

            // ??getForUpdate??????
            commonList = (List<ResourceItemCommonData>) gotCommonDataListObj;

        } else {

            // ????????
            List<String> targetItemIdList = new ArrayList<>();
            targetItemIdList.add(getId(requestMessage));

            commonList = synchronizer.getModified(resourceName, targetItemIdList, modifiedSince);
        }

        // modified????NotModified
        if (commonList.isEmpty()) {
            throw new NotModifiedException(requestMessage);
        }

        // ??????Object(Map)

        Map<String, Object> result = new HashMap<>();
        ResourceItemCommonData common = commonList.get(0);
        result.put(DOWNLOAD_RESULT_COMMON_DATA_KEY, common);

        // ????ID????
        if (common.getSyncAction() == SyncAction.DELETE) {
            result.put(DOWNLOAD_RESULT_ITEM_KEY, createDeletedItem(common));
        } else {
            // ????
            // findById?ID?list????????????
            // ????????
            result.put(DOWNLOAD_RESULT_ITEM_KEY, findById(requestMessage));
        }

        return result;
    }

    /**
     * ID??????.<br/>
     * ?????????????????????.<br/>
     *
     * @param resourceName ??
     * @param modifiedSince ?
     * @param requestMessage 
     * @return ???
     */
    @SuppressWarnings("unchecked")
    private Object downloadByQuery(String resourceName, long modifiedSince, RequestMessage requestMessage)
            throws BadRequestException, LockedException, NotModifiedException, NotFoundException {

        SyncConfigurationParameter configParam = synchronizer.getSyncConfigurationParameter();

        List<ResourceItemCommonData> modifiedCommonList;

        Object gotCommonDataListObj = requestMessage.get(configParam.RESOURCE_ITEM_COMMON_DATA);
        if (gotCommonDataListObj != null) {

            // ??getForUpdate??????
            modifiedCommonList = (List<ResourceItemCommonData>) gotCommonDataListObj;
        } else {

            // modified???????ID??
            List<String> targetItemIdList = new ArrayList<>();
            Object itemObj = findByQuery(requestMessage);

            // ????????????
            if (!(itemObj instanceof List)) {
                return itemObj;
            }
            List<?> itemObjList = (List<?>) itemObj;
            if (itemObjList.isEmpty() || !getItemType().isAssignableFrom(itemObjList.get(0).getClass())) {
                return itemObj;
            }

            for (Object item : itemObjList) {
                targetItemIdList.add(getIdFieldValue(getItemType().cast(item)));
            }

            modifiedCommonList = synchronizer.getModified(resourceName, targetItemIdList, modifiedSince);
        }

        // ??????Object(Map)?List
        ArrayList<Map<String, Object>> resultList = new ArrayList<>();
        for (ResourceItemCommonData common : modifiedCommonList) {

            Map<String, Object> result = new HashMap<>();
            result.put(DOWNLOAD_RESULT_COMMON_DATA_KEY, common);

            // ????ID????
            if (common.getSyncAction() == SyncAction.DELETE) {
                result.put(DOWNLOAD_RESULT_ITEM_KEY, createDeletedItem(common));
            } else { // ID?RequestMessage?????????
                T item = getRepository().findOne(common.getTargetItemId());
                result.put(DOWNLOAD_RESULT_ITEM_KEY, item);
            }

            resultList.add(result);
        }

        return resultList;
    }

    /**
     * ???ID???????.
     *
     * @param common ?
     * @return 
     */
    private T createDeletedItem(ResourceItemCommonData common) {
        Class<T> itemType = getItemType();

        BeanWrapper deletedWrapper = PropertyAccessorFactory
                .forBeanPropertyAccess(BeanUtils.instantiateClass(itemType));
        deletedWrapper.setPropertyValue(getIdFieldName(), common.getTargetItemId());

        return itemType.cast(deletedWrapper.getWrappedInstance());
    }

    /**
     * ???????.
     *
     * @param requestMessage 
     * @return ?
     * @throws AbstractResourceException
     */
    @Override
    public List<ResourceItemCommonData> getForUpdate(RequestMessage requestMessage)
            throws AbstractResourceException {

        SyncConfigurationParameter configParam = synchronizer.getSyncConfigurationParameter();

        // lastModified??????????
        long modifiedSince = 0L;
        Object lastModifiedObj = requestMessage.get(configParam.LAST_MODIFIED);
        try {
            if (lastModifiedObj != null) {
                modifiedSince = Long.parseLong((String) lastModifiedObj);
            }
        } catch (NumberFormatException e) {
            throw new BadRequestException("Failed to parse Last modified time. : " + (String) lastModifiedObj,
                    requestMessage);
        }

        String resourceName = ((ResourceItemCommonDataId) requestMessage
                .get(configParam.RESOURCE_ITEM_COMMON_DATA_ID)).getResourceName();

        MessageMetadata messageMetadata = requestMessage.getMessageMetadata();

        Object query = requestMessage.get(messageMetadata.QUERY);
        if (query == null) {
            return getByIdForUpdate(resourceName, modifiedSince, requestMessage);
        }

        return getByQueryForUpdate(resourceName, modifiedSince, requestMessage);
    }

    /**
     * ID????????.<br/>
     * ???????????????????null????
     *
     * @param resourceName ??
     * @param modifiedSince ?
     * @param requestMessage 
     * @return 
     * @throws AbstractResourceException
     */
    private List<ResourceItemCommonData> getByIdForUpdate(String resourceName, long modifiedSince,
            RequestMessage requestMessage) throws AbstractResourceException {

        List<String> targetItemIdList = new ArrayList<>();

        // ID??
        targetItemIdList.add(getId(requestMessage));

        // 1
        return synchronizer.getModifiedForUpdate(resourceName, targetItemIdList, modifiedSince);
    }

    /**
     * ID??????.<br/>
     * ?????????????????????.<br/>
     *
     * @param resourceName ??
     * @param modifiedSince ?
     * @param requestMessage 
     * @return ?
     * @throws AbstractResourceException
     */
    private List<ResourceItemCommonData> getByQueryForUpdate(String resourceName, long modifiedSince,
            RequestMessage requestMessage) throws AbstractResourceException {

        Object itemObj = findByQuery(requestMessage);

        // ???????
        if (!(itemObj instanceof List)) {
            return Collections.emptyList();
        }
        List<?> itemObjList = (List<?>) itemObj;
        if (itemObjList.isEmpty() || !getItemType().isAssignableFrom(itemObjList.get(0).getClass())) {
            return Collections.emptyList();
        }

        List<String> targetItemIdList = new ArrayList<>();
        for (Object item : itemObjList) {
            targetItemIdList.add(getIdFieldValue(getItemType().cast(item)));
        }

        return synchronizer.getModifiedForUpdate(resourceName, targetItemIdList, modifiedSince);
    }

    /**
     * ??????.
     *
     * @return synchronizer
     */
    @Override
    public Synchronizer getSynchronizer() {
        return this.synchronizer;
    }

    /**
     * ??????????.
     */
    @Override
    public void setSynchronizer(Synchronizer synchronizer) {
        this.synchronizer = synchronizer;
    }

    @Override
    public UpdateStrategy getUpdateStrategy() {

        if (this.updateStrategy == null)
            return this.synchronizer.getDefaultUpdateStrategy();

        return updateStrategy;
    }

    @Override
    public void setUpdateStrategy(UpdateStrategy updateStrategy) {

        this.updateStrategy = updateStrategy;
    }
}