com.adaptris.core.services.metadata.CheckUniqueMetadataValueService.java Source code

Java tutorial

Introduction

Here is the source code for com.adaptris.core.services.metadata.CheckUniqueMetadataValueService.java

Source

/*
 * Copyright 2015 Adaptris Ltd.
 * 
 * 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.adaptris.core.services.metadata;

import static org.apache.commons.lang.StringUtils.isEmpty;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import org.hibernate.validator.constraints.NotBlank;

import com.adaptris.annotation.AdapterComponent;
import com.adaptris.annotation.ComponentProfile;
import com.adaptris.annotation.DisplayOrder;
import com.adaptris.core.AdaptrisMessage;
import com.adaptris.core.BranchingServiceImp;
import com.adaptris.core.CoreException;
import com.adaptris.core.ServiceException;
import com.thoughtworks.xstream.annotations.XStreamAlias;

/**
 * <p>
 * Branching <code>Service</code> implementation which checks the value stored against a configured metadata key against a list of
 * previously received values.
 * <p>
 * The service obeys the following rules when checking the metadata key
 * <ul>
 * <li>If the looked-up value is null or empty, a {@link ServiceException} is thrown.</li>
 * <li>If the value is set and has previously been received, the configured <code>nextServiceIdIfDuplicate</code> is set on the
 * message.</li>
 * <li>If the looked-up value is not contained in the store of previous values <code>nextServiceIdIfUnique</code> is set and the
 * value is added to this store for future checking.</li>
 * </p>
 * <p>
 * The store of previous values has a configurable maximum size. After a new value is added, if the store exceeds the maximum size
 * the oldest value is removed. The store is then persisted to the configured store file.
 * </p>
 * 
 * @config check-unique-metadata-value-service
 * 
 * 
 */
@XStreamAlias("check-unique-metadata-value-service")
@AdapterComponent
@ComponentProfile(summary = "Perform a branch by checking a metadata key and comparing it against a list of previously received values", tag = "service,branching")
@DisplayOrder(order = { "metadataKeyToCheck", "nextServiceIdIfUnique", "nextServiceIdIfDuplicate", "storeFileUrl",
        "numberOfPreviousValuesToStore" })
public class CheckUniqueMetadataValueService extends BranchingServiceImp {

    /**
     * <p>
     * Default next Service ID to set if the message metadata value does not
     * appear in the store of previously received values.
     * </p>
     */
    public static final String DEFAULT_SERVICE_ID_UNIQUE = "001";

    /**
     * <p>
     * Default next Service ID to set if the message metadata value <em>does</em>
     * appear in the store of previously received values.
     * </p>
     */
    public static final String DEFAULT_SERVICE_ID_DUPLICATE = "002";

    @NotBlank
    private String metadataKeyToCheck;
    @NotBlank
    private String storeFileUrl;
    @NotBlank
    private String nextServiceIdIfDuplicate;
    @NotBlank
    private String nextServiceIdIfUnique;
    private int numberOfPreviousValuesToStore;

    // not marshalled
    private transient List<Object> previousValuesStore;
    private transient File store;

    /**
     * <p>
     * Creates a new instance. Default history size is 1000.
     * </p>
     */
    public CheckUniqueMetadataValueService() {
        this.setNumberOfPreviousValuesToStore(1000);

        // this.setNextServiceIdIfUnique(DEFAULT_SERVICE_ID_UNIQUE);
        // this.setNextServiceIdIfDuplicate(DEFAULT_SERVICE_ID_DUPLICATE);
    }

    @Override
    protected void initService() throws CoreException {
        if (this.getMetadataKeyToCheck() == null) {
            throw new CoreException("metadataKeyToCheck must be set");
        }

        this.createStoreFile();
        this.loadPreviouslyReceivedValues();

        if (previousValuesStore == null) {
            previousValuesStore = new ArrayList<Object>();
        }
    }

    @Override
    protected void closeService() {

    }

    private void createStoreFile() throws CoreException {
        if (this.getStoreFileUrl() == null) {
            throw new CoreException("store file URL is null");
        }

        URL url = null;

        try {
            url = new URL(this.getStoreFileUrl());
        } catch (MalformedURLException e) {
            throw new CoreException(e);
        }

        this.store = new File(url.getFile());
    }

    /**
     * @see com.adaptris.core.Service
     *      #doService(com.adaptris.core.AdaptrisMessage)
     */
    public void doService(AdaptrisMessage msg) throws ServiceException {
        String value = msg.getMetadataValue(this.getMetadataKeyToCheck());

        if (value == null || "".equals(value)) {
            throw new ServiceException("required metadata [" + this.getMetadataKeyToCheck() + "] missing");
        }

        if (previousValuesStore.contains(value)) {
            this.handleDuplicate(msg, value);
        } else {
            try {
                this.handleNewValue(msg, value);
            } catch (Exception e) {
                throw new ServiceException(e);
            }
        }
    }

    private void handleDuplicate(AdaptrisMessage msg, String value) throws ServiceException {

        String errorMessage = this.createErrorMessage(value);
        log.warn(errorMessage);

        msg.setNextServiceId(this.getNextServiceIdIfDuplicate());
    }

    private String createErrorMessage(String value) {
        StringBuffer result = new StringBuffer();
        result.append("value [");
        result.append(value);
        result.append("] stored against key [");
        result.append(this.getMetadataKeyToCheck());
        result.append("] exists in list of previously stored values");

        return result.toString();
    }

    private void handleNewValue(AdaptrisMessage msg, String value) throws Exception {

        msg.setNextServiceId(this.getNextServiceIdIfUnique());

        previousValuesStore.add(value);

        while (previousValuesStore.size() > this.getNumberOfPreviousValuesToStore()) {

            previousValuesStore.remove(0);
        }

        this.storePreviouslyReceivedValues();
    }

    private void storePreviouslyReceivedValues() {
        if (store != null) {
            try {
                ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream(store));

                o.writeObject(previousValuesStore);
                o.flush();
                o.close();
            } catch (Exception e) {
                log.error("exception storing previously received values", e);
                log.error(previousValuesStore.toString());
            }
        }
    }

    private void loadPreviouslyReceivedValues() throws CoreException {
        if (store != null) {
            try {
                if (store.exists()) {
                    ObjectInputStream o = new ObjectInputStream(new FileInputStream(store));

                    previousValuesStore = (ArrayList<Object>) o.readObject();
                    o.close();
                }
            } catch (Exception e) {
                throw new CoreException(e);
            }
        }
    }

    int storeSize() {
        return previousValuesStore.size();
    }

    // properties...

    /**
     * <p>
     * Returns the metadata key whose value should be checked.
     * </p>
     *
     * @return metadataKey the metadata key whose value should be checked
     */
    public String getMetadataKeyToCheck() {
        return this.metadataKeyToCheck;
    }

    /**
     * <p>
     * Sets the metadata key whose value should be checked. May not be null.
     * </p>
     *
     * @param s the metadata key whose value should be checked
     */
    public void setMetadataKeyToCheck(String s) {
        if (isEmpty(s)) {
            throw new IllegalArgumentException("null or empty param");
        }
        this.metadataKeyToCheck = s;
    }

    /**
     * <p>
     * Returns the number of previous values to keep.
     * </p>
     *
     * @return the number of previous values to keep
     */
    public int getNumberOfPreviousValuesToStore() {
        return this.numberOfPreviousValuesToStore;
    }

    /**
     * <p>
     * Sets the number of previous values to keep. Must be greater than 0.
     * </p>
     *
     * @param i the number of previous values to keep
     */
    public void setNumberOfPreviousValuesToStore(int i) {
        if (i < 1) {
            throw new IllegalArgumentException("history size is 0 or negative");
        }
        this.numberOfPreviousValuesToStore = i;
    }

    /**
     * <p>
     * Returns the persistent store for previously received values in the form of
     * a file URL. E.g. <code>file:////Users/adaptris/store.dat/</code>.
     * </p>
     *
     * @return the persistent store for previously received values in the form of
     *         a file URL
     */
    public String getStoreFileUrl() {
        return this.storeFileUrl;
    }

    /**
     * <p>
     * Sets the persistent store for previously received values in the form of a
     * file URL. E.g. <code>file:////Users/adaptris/store.dat</code>. May not be
     * null or empty.
     * </p>
     *
     * @param s the persistent store for previously received values in the form of
     *          a file URL
     */
    public void setStoreFileUrl(String s) {
        if (isEmpty(s)) {
            throw new IllegalArgumentException("null or empty param");
        }
        this.storeFileUrl = s;
    }

    /**
     * <p>
     * Returns the ID of the next <code>Service</code> to apply if the metadata
     * exists if the store of previous values.
     * </p>
     *
     * @return the ID of the next <code>Service</code> to apply if the metadata
     *         exists if the store of previous values
     */
    public String getNextServiceIdIfDuplicate() {
        return this.nextServiceIdIfDuplicate;
    }

    /**
     * <p>
     * Sets the ID of the next <code>Service</code> to apply if the metadata
     * exists if the store of previous values. May not be null or empty.
     * </p>
     *
     * @param s the ID of the next <code>Service</code> to apply if the metadata
     *          exists if the store of previous values
     */
    public void setNextServiceIdIfDuplicate(String s) {
        if (isEmpty(s)) {
            throw new IllegalArgumentException("null or empty param");
        }
        this.nextServiceIdIfDuplicate = s;
    }

    /**
     * <p>
     * Returns the ID of the next <code>Service</code> to apply if the metadata
     * does not exist if the store of previous values.
     * </p>
     *
     * @return the ID of the next <code>Service</code> to apply if the metadata
     *         does not exist if the store of previous values
     */
    public String getNextServiceIdIfUnique() {
        return this.nextServiceIdIfUnique;
    }

    /**
     * <p>
     * Sets the ID of the next <code>Service</code> to apply if the metadata does
     * not exist if the store of previous values. May not be null or empty.
     * </p>
     *
     * @param s the ID of the next <code>Service</code> to apply if the metadata
     *          does not exist if the store of previous values
     */
    public void setNextServiceIdIfUnique(String s) {
        if (isEmpty(s)) {
            throw new IllegalArgumentException("null or empty param");
        }
        this.nextServiceIdIfUnique = s;
    }

    @Override
    public void prepare() throws CoreException {
    }

}