org.wso2.carbon.mediation.connector.pmode.PModeRepository.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.mediation.connector.pmode.PModeRepository.java

Source

/*
*  Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
*  WSO2 Inc. licenses this file to you 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 org.wso2.carbon.mediation.connector.pmode;

import org.apache.axis2.AxisFault;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.vfs2.FileListener;
import org.apache.commons.vfs2.FileSystemManager;
import org.apache.commons.vfs2.VFS;
import org.apache.commons.vfs2.FileChangeEvent;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSystemException;
import org.apache.commons.vfs2.impl.DefaultFileMonitor;
import org.wso2.carbon.mediation.connector.AS4Constants;
import org.wso2.carbon.mediation.connector.pmode.impl.PMode;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.File;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * PMode repository implementation which will store current PModes. This will work as file listener as well. So adding
 * and editing PModes will get automatically synced with this in memory repository.
 */
public class PModeRepository implements FileListener {

    private static final Log log = LogFactory.getLog(PModeRepository.class);

    private static PModeRepository pmodeRepository;

    //key - agreement.name value - pMode
    private Map<String, PMode> pModeMap;
    //key - fileName, value - agreementRef
    private Map<String, String> fileNameRefMap;
    //key - fileName, value - agreementRef
    private Map<String, String> possibleNameChangeMap;
    private Unmarshaller pModeUnmarshaller;
    private int basePathLength;

    /**
     * Constructor for PMode repository implementation.
     *
     * @param pmodeRepositoryPath Path to the directory containing PMode files
     */
    private PModeRepository(String pmodeRepositoryPath) throws AxisFault {

        if (log.isDebugEnabled()) {
            log.debug("Initializing PMode repository for the location : " + pmodeRepositoryPath);
        }

        this.pModeMap = new HashMap<String, PMode>();
        this.fileNameRefMap = new HashMap<String, String>();
        this.possibleNameChangeMap = new HashMap<String, String>();
        File pmodeFolder;

        try {
            this.pModeUnmarshaller = JAXBContext.newInstance(PMode.class).createUnmarshaller();
        } catch (JAXBException e) {
            log.error("Unable to create JAXB unmarshaller for : " + PMode.class, e);
            throw new AxisFault("Unable to create JAXB unmarshaller for : " + PMode.class, e);
        }

        if (pmodeRepositoryPath != null) {
            pmodeFolder = new File(pmodeRepositoryPath);
            if (!pmodeFolder.exists() || !pmodeFolder.isDirectory()) {
                log.warn("Provided PMode directory is invalid, falling back to default PMode Directory : "
                        + AS4Constants.AS4_PMODE_LOCATION);
                pmodeFolder = new File(AS4Constants.AS4_PMODE_LOCATION);
            }
        } else {
            if (log.isDebugEnabled()) {
                log.debug("PMode directory not provided, falling back to default PMode Directory : "
                        + AS4Constants.AS4_PMODE_LOCATION);
            }
            pmodeFolder = new File(AS4Constants.AS4_PMODE_LOCATION);
        }

        traversePmodeDirectory(pmodeFolder);
        try {
            FileSystemManager fileSystemManager = VFS.getManager();
            FileObject listenDirectory = fileSystemManager.resolveFile(pmodeFolder.getAbsolutePath());
            this.basePathLength = listenDirectory.getName().getPathDecoded().length() + 1;
            DefaultFileMonitor fileMonitor = new DefaultFileMonitor(this);
            fileMonitor.addFile(listenDirectory);
            fileMonitor.start();
        } catch (FileSystemException e) {
            log.warn("Error registering PMode watcher, hence needs to restart the server when PModes "
                    + "change or added, " + e.getMessage(), e);
        }
    }

    /**
     * This will return the PModeRepository instance
     * @throws AxisFault
     */
    public static synchronized PModeRepository getInstance() throws AxisFault {
        if (pmodeRepository == null) {
            pmodeRepository = new PModeRepository(null);
        }
        return pmodeRepository;
    }

    /**
     * Helper method which traverse PMode directory and add PModes for the first time
     *
     * @param folder PMode directory File object
     */
    private void traversePmodeDirectory(final File folder) {

        if (folder.listFiles() == null || folder.listFiles().length == 0) {
            log.warn("No PMode files found in the directory : " + folder.getName());
            return;
        }
        for (final File file : folder.listFiles()) {
            if (file.isDirectory()) {
                log.warn("PMode directory has sub directory, skipping sub directory : " + file.getName());
            } else {
                if (!file.getName().endsWith("~")) {
                    processPModeFile(file);
                } else if (log.isDebugEnabled()) {
                    log.debug("Skipping backup file : " + file.getName());
                }
            }
        }
    }

    /**
     * Helper method to process PMode files
     *
     * @param pmodeFile PMode file File object
     */
    private void processPModeFile(File pmodeFile) {

        if (log.isDebugEnabled()) {
            log.debug("Processing PMode file : " + pmodeFile.getName());
        }
        try {
            PMode pmode = (PMode) this.pModeUnmarshaller.unmarshal(pmodeFile);
            if (pmode.getAgreement() == null || pmode.getAgreement().getName() == null
                    || pmode.getAgreement().getName().isEmpty()) {
                log.warn("Agreement not found in the PMode file, hence ignoring : " + pmodeFile.getName());
                return;
            }
            validatePMode(pmode);
            addUpdateRemovePMode(Operation.ADD, pmodeFile.getAbsolutePath(), pmode);
        } catch (JAXBException e) {
            log.warn("Unable to unmarshall PMode file : " + pmodeFile.getName() + ", " + e.getMessage(), e);
        } catch (AxisFault axisFault) {
            log.warn("Error while validating PMode file : " + pmodeFile.getName() + ", " + axisFault.getMessage(),
                    axisFault);
        }
    }

    /**
     * Helper method to add, update or remove PMode files from this repository
     *
     * @param operation Operation to be performed
     * @param filePath  Path to PMode file
     * @param pMode     PMode object
     */
    private synchronized void addUpdateRemovePMode(Operation operation, String filePath, PMode pMode) {
        switch (operation) {
        case ADD:
            addPMode(filePath, pMode);
            break;
        case UPDATE:
            updatePMode(filePath, pMode);
            break;
        case REMOVE:
            removePMode(filePath);
            break;
        }
    }

    /**
     * Helper method to add PMode to the repository
     *
     * @param filePath path to PMode file
     * @param pMode    PMode object
     */
    private void addPMode(String filePath, PMode pMode) {

        if (log.isDebugEnabled()) {
            log.debug("Adding PMode file : " + filePath);
        }
        if (!pModeMap.containsKey(pMode.getAgreement().getName()) && !fileNameRefMap.containsKey(filePath)) {
            // Not in both maps -> implies new PMode
            pModeMap.put(pMode.getAgreement().getName(), pMode);
            fileNameRefMap.put(filePath, pMode.getAgreement().getName());
            if (log.isDebugEnabled()) {
                log.debug(
                        "PMode added with agreement : " + pMode.getAgreement().getName() + ", file : " + filePath);
            }
        } else if (!fileNameRefMap.containsKey(filePath)) {
            // Existing in PMode map but not in name map -> implies possible file name change
            String existingFilePath = getExistingPath(pMode.getAgreement().getName());
            if (existingFilePath != null && !existingFilePath.isEmpty()) {
                File existingFile = new File(existingFilePath);
                if (existingFile.exists()) {
                    log.warn("Duplicate PMode agreements found in two files, agreement : "
                            + pMode.getAgreement().getName() + ", ignoring : " + filePath);
                    return;
                }
            }
            fileNameRefMap.remove(existingFilePath);
            fileNameRefMap.put(filePath, pMode.getAgreement().getName());
            possibleNameChangeMap.put(existingFilePath, pMode.getAgreement().getName());
            if (log.isDebugEnabled()) {
                log.debug("File path updated for the renamed PMode, agreement : " + pMode.getAgreement().getName()
                        + ", previous file : " + existingFilePath + ", new file : " + filePath);
            }
        } else {
            /**
             * Comes to this for two conditions
             * 1 - not in PMode map and in fileMap
             * 2 - in PMode map and in fileMap
             *
             * Those two scenarios cannot happen with this implementation
             */
            log.warn(
                    "Duplicate PMode agreements found in two files, agreement : " + pMode.getAgreement().getName());
        }
    }

    /**
     * Helper method to update PMode in the repository
     *
     * @param filePath path to PMode file
     * @param pMode    PMode object
     */
    private void updatePMode(String filePath, PMode pMode) {

        if (log.isDebugEnabled()) {
            log.debug("Updating PMode File : " + filePath);
        }
        if (pModeMap.containsKey(pMode.getAgreement().getName())) {
            pModeMap.put(pMode.getAgreement().getName(), pMode);
            if (log.isDebugEnabled()) {
                log.debug("Updating existing PMode : " + pMode.getAgreement().getName());
            }
        } else {
            pModeMap.remove(fileNameRefMap.get(filePath));
            fileNameRefMap.put(filePath, pMode.getAgreement().getName());
            pModeMap.put(pMode.getAgreement().getName(), pMode);
            if (log.isDebugEnabled()) {
                log.debug("PMode agreement changed, updating PMode : " + pMode.getAgreement().getName());
            }
        }
    }

    /**
     * Helper method to remove PModes
     *
     * @param filePath path to PMode file
     */
    private void removePMode(String filePath) {

        if (log.isDebugEnabled()) {
            log.debug("Removing PMode file : " + filePath);
        }
        if (!possibleNameChangeMap.containsKey(filePath)) {
            if (fileNameRefMap.containsKey(filePath)) {
                pModeMap.remove(fileNameRefMap.get(filePath));
                fileNameRefMap.remove(filePath);
                if (log.isDebugEnabled()) {
                    log.debug("Removing PMode : " + filePath);
                }
            } else {
                //directory deletion will come to this or files within inner directories will come to this, hence ignore
            }
        } else {
            possibleNameChangeMap.remove(filePath);
            if (log.isDebugEnabled()) {
                log.debug("File name changed : " + filePath);
            }
        }
    }

    /**
     * Helper method to get previous file path.
     *
     * @param agreement PMode Agreement
     * @return previous file path
     */
    private String getExistingPath(String agreement) {

        for (Map.Entry entry : fileNameRefMap.entrySet()) {
            if (entry.getValue().equals(agreement)) {
                return entry.getKey().toString();
            }
        }
        return null;
    }

    /**
     * API method to get PMode using agreementRef
     *
     * @param agreement PMode Agreement
     * @return pMode PMode object
     */
    public PMode findPModeFromAgreement(String agreement) {
        return pModeMap.get(agreement);
    }

    @Override
    public void fileCreated(FileChangeEvent fileChangeEvent) throws Exception {

        try {
            if (isProcessingRequired(fileChangeEvent)) {
                InputStream inputStream = fileChangeEvent.getFile().getContent().getInputStream();
                PMode pMode = (PMode) this.pModeUnmarshaller.unmarshal(inputStream);
                validatePMode(pMode);
                addUpdateRemovePMode(Operation.ADD, fileChangeEvent.getFile().getName().getPathDecoded(), pMode);
            }
        } catch (FileSystemException e) {
            log.warn("File system exception occurred while dynamically updating PModes, " + e.getMessage(), e);
        } catch (JAXBException e) {
            log.warn("JAXB exception occurred while dynamically updating PModes, " + e.getMessage(), e);
        } catch (AxisFault e) {
            log.warn("SynapseException occurred while dynamically updating PModes, " + e.getMessage(), e);
        } catch (Exception e) {
            log.warn("exception occurred while dynamically updating PModes, " + e.getMessage(), e);
        }
    }

    @Override
    public void fileDeleted(FileChangeEvent fileChangeEvent) throws Exception {

        try {
            addUpdateRemovePMode(Operation.REMOVE, fileChangeEvent.getFile().getName().getPathDecoded(), null);
        } catch (Exception e) {
            log.warn("Exception occurred while dynamically updating PModes, " + e.getMessage(), e);
        }
    }

    @Override
    public void fileChanged(FileChangeEvent fileChangeEvent) throws Exception {

        try {
            if (isProcessingRequired(fileChangeEvent)) {
                InputStream inputStream = fileChangeEvent.getFile().getContent().getInputStream();
                PMode pMode = (PMode) this.pModeUnmarshaller.unmarshal(inputStream);
                validatePMode(pMode);
                addUpdateRemovePMode(Operation.UPDATE, fileChangeEvent.getFile().getName().getPathDecoded(), pMode);
            }
        } catch (FileSystemException e) {
            log.warn("File system exception occurred while dynamically updating PModes, " + e.getMessage(), e);
        } catch (JAXBException e) {
            log.warn("JAXB exception occurred while dynamically updating PModes, " + e.getMessage(), e);
        } catch (AxisFault e) {
            log.warn("AxisFault occurred while dynamically updating PModes, " + e.getMessage(), e);
        } catch (Exception e) {
            log.warn("Exception occurred while dynamically updating PModes, " + e.getMessage(), e);
        }
    }

    /**
     * Helper method to detect whether  the file corresponding to FileChangeEvent needs to be processed
     *
     * @param fileChangeEvent FileChangeEvent object
     * @return true if needs to process, false otherwise
     * @throws FileSystemException
     */
    private boolean isProcessingRequired(FileChangeEvent fileChangeEvent) throws FileSystemException {

        String path = fileChangeEvent.getFile().getName().getPathDecoded();
        File file = new File(path);
        if (file.isDirectory()) {
            return false;
        }
        String fileName = path.substring(basePathLength);
        if (fileName.contains(File.separator)) {
            return false;
        }
        if (fileChangeEvent.getFile().getName().getPath().endsWith("~")) {
            return false;
        }
        return true;
    }

    /**
     * Validation method to validate PMode files
     *
     * @param pmode PMode instance
     */
    private void validatePMode(PMode pmode) throws AxisFault {

        if (pmode == null) {
            throw new AxisFault("PMode not found");
        }
        if (pmode.getAgreement() == null || pmode.getAgreement().getName() == null
                || pmode.getAgreement().getName().isEmpty()) {
            throw new AxisFault("Invalid PMode Agreement");
        }
        if (pmode.getInitiator() == null) {
            throw new AxisFault("Initiator not found in PMode");
        }
        if (pmode.getInitiator().getParty() == null || pmode.getInitiator().getParty().isEmpty()) {
            throw new AxisFault("Invalid Initiator Party in PMode");
        }
        if (pmode.getInitiator().getRole() == null || pmode.getInitiator().getRole().isEmpty()) {
            throw new AxisFault("Invalid Initiator Role in PMode");
        }
        if (pmode.getProtocol() == null) {
            throw new AxisFault("Protocol not found in PMode");
        }
        if (pmode.getProtocol().getAddress() == null || pmode.getProtocol().getAddress().isEmpty()) {
            throw new AxisFault("Invalid Protocol Address found in PMode");
        }
    }

    /**
     * Operation type for PMode
     */
    enum Operation {
        ADD, REMOVE, UPDATE
    }
}