org.wso2.carbon.registry.synchronization.operation.CheckInCommand.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.registry.synchronization.operation.CheckInCommand.java

Source

/*
 * Copyright (c) 2008, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * 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 org.wso2.carbon.registry.synchronization.operation;

import org.apache.axiom.om.*;
import org.apache.axiom.om.util.Base64;
import org.apache.commons.io.FileUtils;
import org.wso2.carbon.registry.core.Registry;
import org.wso2.carbon.registry.core.exceptions.RegistryException;
import org.wso2.carbon.registry.core.jdbc.DumpConstants;
import org.wso2.carbon.registry.core.utils.RegistryUtils;
import org.wso2.carbon.registry.synchronization.SynchronizationConstants;
import org.wso2.carbon.registry.synchronization.SynchronizationException;
import org.wso2.carbon.registry.synchronization.UserInputCallback;
import org.wso2.carbon.registry.synchronization.Utils;
import org.wso2.carbon.registry.synchronization.message.Message;
import org.wso2.carbon.registry.synchronization.message.MessageCode;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.io.*;
import java.net.UnknownHostException;
import java.util.zip.ZipInputStream;

/**
 * This command is used to perform a check-in operation which will upload the files and directories
 * from the local filesystem into the provided registry instance.
 */
@SuppressWarnings({ "ResultOfMethodCallIgnored", "unused" })
public class CheckInCommand {

    private static boolean ignoreConflicts = Boolean
            .parseBoolean(System.getProperty(SynchronizationConstants.REGISTRY_IGNORE_CONFLICTS, "false"));

    private String inputFile = null;
    private String workingLocation = null;
    private String registryUrl = null;
    private String checkInPath = null;
    private String username = null;
    private boolean silentUpdate = true;
    private boolean cleanRegistry = false;
    private boolean updated = false;

    ////////////////////////////////////////////////////////
    // Fields maintaining status of command execution
    ////////////////////////////////////////////////////////

    private int sentCount = -1;

    /**
     * Creates an instance of a check-in command which can be executed against a provided registry
     * instance.
     *
     * @param inputFile          if the content is to be uploaded from a single meta file, this
     *                           parameter can be used to specify the path to the meta file.
     * @param workingLocation         if the content is to be uploaded from a directory on the
     *                           filesystem, this parameter can be used to specify the path to the
     *                           corresponding location.
     * @param userUrl            aggregate URL containing a concatenation of the registry URL and
     *                           the resource path that is capable of referencing a remote resource.
     *                           This url will contain only the resource path if the resource was
     *                           local to the given registry instance.
     * @param username           the name of the user (which should be a valid username on the
     *                           target server on which the provided registry instance is running)
     *                           that performs this operation.
     * @param silentUpdate       to ignore the conflicts and perform silent update on the server
     *                           side.
     * @param cleanRegistry      whether the embedded registry instance must be cleaned after the
     *                           execution of the operation.
     * @param testContentChanged when this parameter is set to true, check-in will only happen if
     *                           the content has changed.
     *
     * @throws SynchronizationException if the operation failed.
     */
    public CheckInCommand(String inputFile, String workingLocation, String userUrl, String username,
            boolean silentUpdate, boolean cleanRegistry, boolean testContentChanged)
            throws SynchronizationException {
        // now if the user url is different to the registry url we are going to consider that as
        // well.

        this.inputFile = inputFile;
        if (workingLocation.endsWith(File.separator)) {
            workingLocation = workingLocation.substring(0, workingLocation.length() - 1);
        }
        this.workingLocation = workingLocation;
        String userUrl1 = userUrl;
        this.username = username;
        this.silentUpdate = silentUpdate;
        this.cleanRegistry = cleanRegistry;
        boolean testContentChanged1 = testContentChanged;

        // get the update details form the meta element of the current checkout
        OMElement metaOMElement = Utils.getMetaOMElement(workingLocation);
        if (metaOMElement != null) {
            checkInPath = metaOMElement.getAttributeValue(new QName(DumpConstants.RESOURCE_PATH));
        }

        if (userUrl != null) {
            registryUrl = Utils.getRegistryUrl(userUrl);
            String suggestedCheckInPath = Utils.getPath(userUrl);
            if (suggestedCheckInPath == null || suggestedCheckInPath.equals("")) {
                suggestedCheckInPath = "/";
                // we are converting the root path to the current directory of the file system
            }
            if (!suggestedCheckInPath.equals(checkInPath)) {
                testContentChanged1 = false;
                checkInPath = suggestedCheckInPath;
            }
        } else {
            if (metaOMElement == null) {
                throw new SynchronizationException(MessageCode.CHECKOUT_BEFORE_CHECK_IN);
            }
            registryUrl = metaOMElement.getAttributeValue(new QName("registryUrl"));
        }
    }

    /**
     * Method to obtain the count of files sent.
     *
     * @return the count of files sent.
     */
    public int getSentCount() {
        return sentCount;
    }

    /**
     * This method will execute the check-in command utilizing the various parameters passed when
     * creating the instance of the command. This method accepts the users preference if a deletion
     * of a file or directory is required in the process.
     *
     * @param registry the registry instance to be used.
     * @param callback the instance of a callback that can be used to determine the user's
     *                 preference before deleting an existing file or directory during the update
     *                 after the check-in has been done. If this parameter is null, the default
     *                 behaviour of deleting the existing file will be used.
     * @return whether the resources updated in the registry
     *
     * @throws SynchronizationException if the operation failed.
     */
    public boolean execute(Registry registry, UserInputCallback callback) throws SynchronizationException {
        if (inputFile != null) {
            // restore a single file.
            restoreFromFile(registry);
        } else {
            restoreFromFileSystem(registry, callback);
        }
        return updated;
    }

    /**
     * This method will execute the check-in command utilizing the various parameters passed when
     * creating the instance of the command.
     *
     * @param registry the registry instance to be used.
     *
     * @return whether the resources updated in the registry
     *
     * @throws SynchronizationException if the operation failed.
     */
    public boolean execute(Registry registry) throws SynchronizationException {
        return execute(registry, null);
    }

    // Restores the given resources and collections from a dump file.
    private void restoreFromFile(Registry registry) throws SynchronizationException {
        String workingLocation = this.workingLocation;

        if (workingLocation != null) {
            inputFile = workingLocation + File.separator + inputFile;
        }

        // do the restoring
        try {
            ZipInputStream zis = new ZipInputStream(new FileInputStream(inputFile));
            zis.getNextEntry();
            Reader reader = new InputStreamReader(zis);
            registry.restore(checkInPath, reader);
        } catch (FileNotFoundException e) {
            throw new SynchronizationException(MessageCode.FILE_DOES_NOT_EXIST, e,
                    new String[] { "Output file" + inputFile });
        } catch (Exception e) {
            if (e.getCause() instanceof UnknownHostException) {
                throw new SynchronizationException(MessageCode.ERROR_IN_CONNECTING_REGISTRY, e,
                        new String[] { " registry url:" + registryUrl });
            }
            throw new SynchronizationException(MessageCode.ERROR_IN_RESTORING, e, new String[] {
                    "path: " + checkInPath, "registry url: " + registryUrl, "username: " + username });
        }

        if (cleanRegistry && registryUrl == null) {
            Utils.cleanEmbeddedRegistry();
        }
    }

    // Restores the given resources and collections from files and folders on the filesystem.
    private void restoreFromFileSystem(Registry registry, UserInputCallback callback)
            throws SynchronizationException {
        sentCount = 0;

        // we are doing the check-in through a temp file. (so assumed enough spaces are there)
        File tempFile = null;
        boolean deleteTempFileFailed = false;
        XMLStreamWriter xmlWriter = null;
        Writer writer = null;
        try {
            try {
                tempFile = File.createTempFile(SynchronizationConstants.DUMP_META_FILE_NAME,
                        SynchronizationConstants.META_FILE_EXTENSION);

                try {
                    writer = new FileWriter(tempFile);
                    // wrap the writer with an xml stream writer
                    xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(writer);
                    // prepare the dump xml
                    xmlWriter.writeStartDocument();
                    createMetaElement(xmlWriter, workingLocation, checkInPath, callback);
                    xmlWriter.writeEndDocument();
                } finally {
                    try {
                        if (xmlWriter != null) {
                            xmlWriter.close();
                        }
                    } finally {
                        if (writer != null) {
                            writer.close();
                        }
                    }
                }
            } catch (IOException e) {
                throw new SynchronizationException(MessageCode.ERROR_IN_CREATING_TEMP_FILE_FOR_DUMP, e);
            } catch (XMLStreamException e) {
                throw new SynchronizationException(MessageCode.ERROR_IN_CREATING_XML_STREAM_WRITER, e);
            }

            // do the restoring if the file system is actually updated
            if (updated) {
                try {
                    Reader reader = null;
                    try {
                        reader = new FileReader(tempFile);
                        registry.restore(checkInPath, reader);
                    } finally {
                        if (reader != null) {
                            reader.close();
                        }
                    }
                } catch (IOException e) {
                    throw new SynchronizationException(MessageCode.ERROR_IN_READING_TEMP_FILE_OF_DUMP, e);
                } catch (RegistryException e) {
                    throw new SynchronizationException(MessageCode.ERROR_IN_RESTORING, e, new String[] {
                            "path: " + checkInPath, "registry url: " + registryUrl, "username: " + username });
                }
            }
        } finally {
            if (tempFile != null) {
                // Our intention here is to delete the temporary file. We are not bothered whether
                // this operation fails.
                deleteTempFileFailed = !FileUtils.deleteQuietly(tempFile);
            }
        }
        if (deleteTempFileFailed) {
            throw new SynchronizationException(MessageCode.ERROR_IN_CLEANING_UP,
                    new String[] { "file path: " + tempFile.getAbsolutePath() });
        }

        if (cleanRegistry && registryUrl == null) {
            Utils.cleanEmbeddedRegistry();
        }
    }

    // Creates the dump element from the given file or directory.
    private void createMetaElement(XMLStreamWriter xmlWriter, String filePath, String path,
            UserInputCallback callback) throws SynchronizationException, XMLStreamException {
        File file = new File(filePath);
        if (file.isDirectory()) {
            createDirectoryMetaElement(xmlWriter, filePath, path, callback);
        } else {
            String metaFilePath = Utils.getMetaFilePath(file.getPath());
            createResourceMetaElement(xmlWriter, file, metaFilePath, path, callback);
        }
    }

    // Creates the dump element from the given directory. If the path is not given it is retrieved
    // from the meta file.
    private void createDirectoryMetaElement(XMLStreamWriter xmlWriter, String filePath, String path,
            UserInputCallback callback) throws SynchronizationException, XMLStreamException {
        // first get the meta file of the directory.
        String metaDirectoryPath = filePath + File.separator + SynchronizationConstants.META_DIRECTORY;
        String metaFilePath = metaDirectoryPath + File.separator + SynchronizationConstants.META_FILE_PREFIX
                + SynchronizationConstants.META_FILE_EXTENSION;

        // confirm the existence of the meta file.
        OMElement metaElement = Utils.getOMElementFromMetaFile(metaFilePath);
        if (metaElement == null) {
            return;
        }

        // alerting non-backward compatibility...
        String checkoutPathAttribute = metaElement.getAttributeValue(new QName("checkoutPath"));
        if (checkoutPathAttribute != null) {
            throw new SynchronizationException(MessageCode.CHECKOUT_OLD_VERSION);
        }

        // we are re-adjusting the name of the resource to make sure the file name and the resource
        // name is equal
        String resourceName = RegistryUtils.getResourceName(path);
        metaElement.addAttribute(DumpConstants.RESOURCE_NAME, resourceName, null);

        OMFactory factory = OMAbstractFactory.getOMFactory();

        OMAttribute status;
        if ((status = metaElement.getAttribute(new QName(DumpConstants.RESOURCE_STATUS))) != null) {
            if (DumpConstants.RESOURCE_ADDED.equals(status.getAttributeValue())) {
                metaElement = Utils.updateDefaultAddMetaFile(metaElement, path, username, true);
            }
        }

        if (status != null && !DumpConstants.RESOURCE_DELETED.equals(status.getAttributeValue())) {
            metaElement.removeAttribute(
                    factory.createOMAttribute(DumpConstants.RESOURCE_STATUS, null, status.getAttributeValue()));
            Utils.updateMetaFile(metaFilePath, metaElement);
            metaElement.addAttribute(DumpConstants.RESOURCE_STATUS, status.getAttributeValue(), null);
        }

        // now write the meta data of the meta element to the writer (except children)
        Utils.writeMetaElement(xmlWriter, metaElement);
        if (status != null) {
            if (callback != null) {
                callback.displayMessage(new Message(MessageCode.SENT, new String[] { filePath }));
            }
            sentCount++;
            updated = true;
        }

        // now add the child element to the meta element
        xmlWriter.writeStartElement(DumpConstants.CHILDREN);

        File metaDirFile = new File(metaDirectoryPath);
        String[] metaFiles = metaDirFile.list(new FilenameFilter() {
            public boolean accept(File file, String s) {
                if (!s.equals("~.xml")) {
                    return true;
                }
                return false;
            }
        });
        if (metaFiles != null) {
            for (String childMetaFileName : metaFiles) {
                String childFileName = Utils
                        .decodeFilename(childMetaFileName.substring(1, childMetaFileName.length() - 4));
                String childFilePath = metaDirFile.getParent() + File.separator + childFileName;
                createResourceMetaElement(xmlWriter, new File(childFilePath),
                        metaDirectoryPath + File.separator + childMetaFileName, path, callback);
            }
        }

        File directory = new File(filePath);
        String[] childrenNames = directory.list(new FilenameFilter() {
            public boolean accept(File file, String s) {
                if (file.isDirectory()) {
                    if (s.equals(SynchronizationConstants.META_DIRECTORY)) {
                        return false;
                    }
                    return true;
                }
                return false;
            }
        });
        if (childrenNames != null) {
            for (String childFileName : childrenNames) {
                // Get childFileName of file or directory
                String childResourceName = Utils.decodeFilename(childFileName);

                if (childResourceName.endsWith(SynchronizationConstants.MINE_FILE_POSTFIX)
                        || childResourceName.endsWith(SynchronizationConstants.SERVER_FILE_POSTFIX)) {
                    // there is an conflicts
                    throw new SynchronizationException(MessageCode.RESOLVE_CONFLICTS);
                }

                String childPath = path + "/" + childResourceName;
                String childFilePath = filePath + File.separator + childFileName;
                createDirectoryMetaElement(xmlWriter, childFilePath, childPath, callback);
            }
        }

        if (status != null && DumpConstants.RESOURCE_DELETED.equals(status.getAttributeValue())) {
            FileUtils.deleteQuietly(directory);
        }

        xmlWriter.writeEndElement(); // to end children tag.
        xmlWriter.writeEndElement(); // to end resource tag.
        xmlWriter.flush();
    }

    private void createResourceMetaElement(XMLStreamWriter xmlWriter, File resourceFile, String metaFilePath,
            String path, UserInputCallback callback) throws XMLStreamException, SynchronizationException {
        String fileName = resourceFile.getName();
        String filePath = resourceFile.getPath();
        // confirm the existence of the meta file.
        OMElement metaElement = Utils.getOMElementFromMetaFile(metaFilePath);
        if (metaElement == null) {
            return;
        }
        // we are re-adjusting the name of the resource to make sure the file name and the
        // resource name is equal
        metaElement.addAttribute(DumpConstants.RESOURCE_NAME, fileName, null);
        if (!silentUpdate && !ignoreConflicts) {
            // we only set the ignoreConflicts attribute if it is failing. This will enforce a
            // check for conflicts at the server side.
            metaElement.addAttribute(DumpConstants.IGNORE_CONFLICTS, "false", null);
        }

        OMFactory factory = OMAbstractFactory.getOMFactory();
        OMAttribute status;
        if ((status = metaElement.getAttribute(new QName(DumpConstants.RESOURCE_STATUS))) != null) {
            if (DumpConstants.RESOURCE_ADDED.equals(status.getAttributeValue())) {
                metaElement = Utils.updateDefaultAddMetaFile(metaElement, path + File.separator + fileName,
                        username, false);
            } else if (DumpConstants.RESOURCE_DELETED.equals(status.getAttributeValue())) {
                FileUtils.deleteQuietly(new File(metaFilePath));
                FileUtils.deleteQuietly(resourceFile);
            }
        } else if (Utils.fileContentChanged(resourceFile)) {
            status = metaElement.addAttribute(DumpConstants.RESOURCE_STATUS, DumpConstants.RESOURCE_UPDATED, null);
        }

        if (status != null && !DumpConstants.RESOURCE_DELETED.equals(status.getAttributeValue())) {
            metaElement.removeAttribute(
                    factory.createOMAttribute(DumpConstants.RESOURCE_STATUS, null, status.getAttributeValue()));
            metaElement.addAttribute("md5", Utils.getMD5(resourceFile), null);
            Utils.updateMetaFile(metaFilePath, metaElement);
            metaElement.addAttribute(DumpConstants.RESOURCE_STATUS, status.getAttributeValue(), null);
        }

        // now write the meta data of the meta element to the writer (except children)
        Utils.writeMetaElement(xmlWriter, metaElement);
        if (status != null) {
            if (callback != null) {
                callback.displayMessage(new Message(MessageCode.SENT, new String[] { filePath }));
            }
            sentCount++;
            updated = true;
        }

        // adding the content if resource is not deleted
        if (status == null
                || (status != null && !DumpConstants.RESOURCE_DELETED.equals(status.getAttributeValue()))) {
            byte[] content = Utils.getBytesFromFile(resourceFile);
            String encodedContent = Base64.encode(content);

            OMElement contentEle = factory.createOMElement(new QName(DumpConstants.CONTENT));
            OMText contentText = factory.createOMText(encodedContent);
            contentEle.addChild(contentText);
            contentEle.serialize(xmlWriter);
        }
        xmlWriter.writeEndElement(); // to end resource tag.
        xmlWriter.flush();
    }
}