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

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.registry.synchronization.operation.CheckOutCommand.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.OMElement;
import org.apache.axiom.om.OMText;
import org.apache.axiom.om.util.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.registry.core.Collection;
import org.wso2.carbon.registry.core.Registry;
import org.wso2.carbon.registry.core.Resource;
import org.wso2.carbon.registry.core.exceptions.RegistryException;
import org.wso2.carbon.registry.core.jdbc.DumpConstants;
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.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.*;
import java.net.UnknownHostException;
import java.util.Iterator;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * This command is used to perform a check-out operation which will download the resources and
 * collections from the provided registry instance into the local filesystem.
 */
@SuppressWarnings({ "ResultOfMethodCallIgnored", "unused" })
public class CheckOutCommand {

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

    private String outputFile = null;
    private String checkOutPath = null;
    private String registryUrl = null;
    private String workingDir = null;
    private String username = null;
    private boolean cleanRegistry = false;
    private boolean dumpLite = true;

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

    private int addedCount = -1;
    private int overwrittenCount = 0;
    private int nonOverwrittenCount = 0;

    /**
     * Creates an instance of a check-out command which can be executed against a provided registry
     * instance.
     *
     * @param outputFile    if the content is to be downloaded into a single meta file, this
     *                      parameter can be used to specify the path to the meta file.
     * @param workingDir    if the content is to be downloaded into 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 cleanRegistry whether the embedded registry instance must be cleaned after the
     *                      execution of the operation.
     *
     * @throws SynchronizationException if the operation failed.
     */
    public CheckOutCommand(String outputFile, String workingDir, String userUrl, String username,
            boolean cleanRegistry) throws SynchronizationException {

        this.outputFile = outputFile;
        this.workingDir = workingDir;
        this.username = username;
        this.cleanRegistry = cleanRegistry;

        if (userUrl == null) {
            throw new SynchronizationException(MessageCode.CO_PATH_MISSING);
        }

        // derive the registry url and the path
        registryUrl = Utils.getRegistryUrl(userUrl);
        checkOutPath = Utils.getPath(userUrl);

        if (checkOutPath == null || checkOutPath.equals("")) {
            checkOutPath = "/";
            // we are converting the root path to the current directory of the file system
        }
    }

    /**
     * Method to obtain the count of files added.
     *
     * @return the count of files added.
     */
    public int getAddedCount() {
        return addedCount;
    }

    /**
     * Method to obtain the count of files overwritten.
     *
     * @return the count of files overwritten.
     */
    public int getOverwrittenCount() {
        return overwrittenCount;
    }

    /**
     * Method to obtain the count of files that were not overwritten.
     *
     * @return the count of files that were not overwritten.
     */
    public int getNonOverwrittenCount() {
        return nonOverwrittenCount;
    }

    /**
     * Method to specify weather to use dump or dump lite
     *
     * @param dumpLite
     */
    public void setDumpLite(boolean dumpLite) {
        this.dumpLite = dumpLite;
    }

    /**
     * This method will execute the check-out command utilizing the various parameters passed when
     * creating the instance of the command. This method accepts the users preference to whether a
     * file or directory should be overwritten on the filesystem.
     *
     * @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 overwriting an existing file or directory during operation.
     *                 If this parameter is null, the default behaviour of overwriting the existing
     *                 file will be used.
     * @return whether checkout succeeded
     *
     * @throws SynchronizationException if the operation failed.
     */
    public boolean execute(Registry registry, UserInputCallback callback) throws SynchronizationException {
        if (outputFile != null) {
            dumpToFile(registry);
        } else {
            dumpToFileSystem(registry, callback);
        }
        return true;
    }

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

    // Downloads the given resources and collections into a dump file.
    private void dumpToFile(Registry registry) throws SynchronizationException {

        String outputXml = outputFile + SynchronizationConstants.META_FILE_EXTENSION;
        if (workingDir != null) {
            outputFile = workingDir + File.separator + outputFile;
        }

        try {
            if (!registry.resourceExists(checkOutPath)) {
                throw new SynchronizationException(MessageCode.ERROR_IN_DUMPING_NO_RESOURCE_OR_NO_PERMISSION,
                        new String[] { "path: " + checkOutPath, "username: " + username });
            }
        } 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_DUMPING_AUTHORIZATION_FAILED, e,
                    new String[] { "path: " + checkOutPath, "username: " + username });
        }

        try {
            // we don't care what is dumping..
            // doing the dump
            ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(outputFile));
            ZipEntry ze = new ZipEntry(outputXml);
            ze.setMethod(ZipEntry.DEFLATED);
            zos.putNextEntry(ze);

            Writer zipWriter = new OutputStreamWriter(zos);

            log.debug("Starting to do registry 'dumpToFile' for : " + checkOutPath + " with : " + outputXml);
            registry.dumpLite(checkOutPath, zipWriter);
            log.debug("Registry 'dumpToFile' completed for : " + checkOutPath + " in : " + outputXml);

            zos.close();
        } catch (Exception e) {
            throw new SynchronizationException(MessageCode.ERROR_IN_DUMPING, e,
                    new String[] { "path: " + checkOutPath, "username: " + username });
        }

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

    // Downloads the given resources and collections into files and folders on the filesystem.
    private void dumpToFileSystem(Registry registry, UserInputCallback callback) throws SynchronizationException {
        addedCount = 0;

        // first try getting the path and confirmed it is a collection
        Resource r;
        try {
            r = registry.get(checkOutPath);
        } catch (Exception e) {
            throw new SynchronizationException(MessageCode.ERROR_IN_DUMPING_NO_RESOURCE_OR_NO_PERMISSION,
                    new String[] { "path: " + checkOutPath, "username: " + username });
        }
        if (!(r instanceof Collection)) {
            throw new SynchronizationException(MessageCode.DUMPING_NON_COLLECTION,
                    new String[] { "path: " + checkOutPath, "username: " + username });
        }

        // we are doing the checkout through a temp file. (so assumed enough spaces are there)
        File tempFile = null;
        boolean deleteTempFileFailed = false;
        FileWriter writer = null;
        try {
            try {
                tempFile = File.createTempFile(SynchronizationConstants.DUMP_META_FILE_NAME,
                        SynchronizationConstants.META_FILE_EXTENSION);
                try {
                    writer = new FileWriter(tempFile);
                    // doing the dump
                    log.debug("Starting to do registry 'dumpToFileSystem' for : " + checkOutPath + " with : "
                            + tempFile.getName());
                    if (dumpLite) {
                        registry.dumpLite(checkOutPath, writer);
                    } else {
                        registry.dump(checkOutPath, writer);
                    }
                    log.debug("Registry 'dumpToFileSystem' completed for : " + checkOutPath + " in : "
                            + tempFile.getName());

                } finally {
                    if (writer != null) {
                        writer.close();
                    }
                }
            } catch (RegistryException e) {
                throw new SynchronizationException(MessageCode.ERROR_IN_DUMPING_NO_RESOURCE_OR_NO_PERMISSION,
                        new String[] { "path: " + checkOutPath, "username: " + username });
            } catch (IOException e) {
                throw new SynchronizationException(MessageCode.ERROR_IN_CREATING_TEMP_FILE_FOR_DUMP, e);
            }
            // now read the xml stream from the file
            XMLStreamReader xmlReader = null;
            Reader reader = null;
            try {
                try {
                    reader = new FileReader(tempFile);
                    xmlReader = XMLInputFactory.newInstance().createXMLStreamReader(reader);
                    log.debug("Starting registry 'checkOutRecursively' for repository : " + checkOutPath
                            + " with : " + tempFile.getName());
                    checkOutRecursively(xmlReader, workingDir, checkOutPath, callback);
                    log.debug("Checkout recursively completed for repository : " + checkOutPath + " with : "
                            + tempFile.getName());
                } finally {
                    try {
                        if (xmlReader != null) {
                            xmlReader.close();
                        }
                    } finally {
                        if (reader != null) {
                            reader.close();
                        }
                    }
                }
            } catch (IOException e) {
                throw new SynchronizationException(MessageCode.ERROR_IN_READING_TEMP_FILE_OF_DUMP, e);
            } catch (XMLStreamException e) {
                throw new SynchronizationException(MessageCode.ERROR_IN_READING_STREAM_OF_TEMP_FILE_OF_DUMP, e);
            }
        } 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();
        }
    }

    // Performs a recursive check-out operation.
    private void checkOutRecursively(XMLStreamReader xmlReader, String filePath, String path,
            UserInputCallback callback) throws SynchronizationException, XMLStreamException {
        // we will first generate the axiom node from the reader,
        OMElement root = Utils.readMetaElement(xmlReader);
        // adding path and the registryUrl
        root.addAttribute(DumpConstants.RESOURCE_PATH, path, null);
        if (registryUrl != null) {
            root.addAttribute("registryUrl", registryUrl, null);
        }

        String isCollectionString = root.getAttributeValue(new QName(DumpConstants.RESOURCE_IS_COLLECTION));
        boolean isCollection = isCollectionString.equals("true");

        String name = root.getAttributeValue(new QName(DumpConstants.RESOURCE_NAME));

        byte[] contentBytes = new byte[0];
        File file = new File(filePath);
        boolean overwrite = true;
        boolean fileAlreadyExist = false;

        String parentDirName = file.getAbsoluteFile().getParent();

        String metaDirectoryName;
        String metaFilePath;
        if (isCollection) {
            metaDirectoryName = filePath + File.separator + SynchronizationConstants.META_DIRECTORY;
            metaFilePath = filePath + File.separator + SynchronizationConstants.META_DIRECTORY + File.separator
                    + SynchronizationConstants.META_FILE_PREFIX + SynchronizationConstants.META_FILE_EXTENSION;
        } else {
            metaDirectoryName = parentDirName + File.separator + SynchronizationConstants.META_DIRECTORY;
            metaFilePath = parentDirName + File.separator + SynchronizationConstants.META_DIRECTORY + File.separator
                    + SynchronizationConstants.META_FILE_PREFIX + Utils.encodeResourceName(name)
                    + SynchronizationConstants.META_FILE_EXTENSION;
        }

        if (file.exists()) {
            fileAlreadyExist = true;
        }

        if (!isCollection && fileAlreadyExist) {
            if (!Utils.resourceUpdated(metaFilePath, root) || (callback != null && !callback.getConfirmation(
                    new Message(MessageCode.FILE_OVERWRITE_CONFIRMATION, new String[] { filePath }),
                    SynchronizationConstants.OVERWRITE_CONFIRMATION_CONTEXT))) {
                overwrite = false;
            }
        }
        try {
            // Create file if it does not exist
            if (isCollection) {
                if (!fileAlreadyExist && Utils.resourceUpdated(metaFilePath, root)) {
                    boolean ignore = file.mkdir(); // ignores the return value purposely
                } else {
                    overwrite = false;
                }
            } else if (overwrite) {
                boolean ignore = file.createNewFile(); // ignores the return value purposely
            }
        } catch (IOException e) {
            throw new SynchronizationException(MessageCode.FILE_CREATION_FAILED, e,
                    new String[] { "file: " + filePath });
        }

        // we are extracting the content from the meta element.
        Iterator children = root.getChildren();
        while (children.hasNext()) {
            OMElement child = (OMElement) children.next();
            String localName = child.getLocalName();

            // LastModified
            if (localName.equals(DumpConstants.LAST_MODIFIED)) {
                OMText text = (OMText) child.getFirstOMChild();
                if (text != null) {
                    long date = Long.parseLong(text.getText());
                    // We are not bothered whether this failed to set the last-modified time. If we
                    // cannot modify the file, we would fail when attempting to write to it anyway.
                    boolean ignore = file.setLastModified(date);
                }
            }
            // get content
            else if (localName.equals(DumpConstants.CONTENT)) {
                OMText text = (OMText) child.getFirstOMChild();
                // we keep content as base64 encoded
                if (text != null) {
                    contentBytes = Base64.decode(text.getText());
                }
                String md5 = Utils.getMD5(contentBytes);
                root.addAttribute("md5", md5, null);
                child.detach();
            }
        }

        if (!isCollection && overwrite) {
            try {
                FileOutputStream fileStream = null;
                try {
                    fileStream = new FileOutputStream(file);
                    fileStream.write(contentBytes);
                    fileStream.flush();
                } finally {
                    if (fileStream != null) {
                        fileStream.close();
                    }
                }
            } catch (IOException e) {
                throw new SynchronizationException(MessageCode.PROBLEM_IN_CREATING_CONTENT, e,
                        new String[] { "file: " + filePath });
            }
        }

        // creating the meta directory
        File metaDirectory = new File(metaDirectoryName);
        if (!metaDirectory.exists() && !metaDirectory.mkdir()) {
            throw new SynchronizationException(MessageCode.ERROR_CREATING_META_FILE,
                    new String[] { "file: " + metaDirectoryName });
        }

        // creating the meta file
        Utils.createMetaFile(metaFilePath, root);

        // printing out the information of the file
        if (!fileAlreadyExist) {
            if (callback != null) {
                callback.displayMessage(new Message(MessageCode.ADDED, new String[] { filePath }));
            }
            addedCount++;
        } else {
            if (overwrite) {
                if (callback != null) {
                    callback.displayMessage(new Message(MessageCode.OVERWRITTEN, new String[] { filePath }));
                }
                overwrittenCount++;
            } else {
                if (callback != null) {
                    callback.displayMessage(new Message(MessageCode.NON_OVERWRITTEN, new String[] { filePath }));
                }
                nonOverwrittenCount++;
            }
        }

        if (!xmlReader.hasNext()
                || !(xmlReader.isStartElement() && xmlReader.getLocalName().equals(DumpConstants.CHILDREN))) {
            // finished the recursion
            // consuming the stream until the resource end element found
            while (xmlReader.hasNext()
                    && !(xmlReader.isEndElement() && xmlReader.getLocalName().equals(DumpConstants.RESOURCE))) {
                xmlReader.next();
            }
            return;
        }

        do {
            xmlReader.next();
            if (xmlReader.isEndElement() && xmlReader.getLocalName().equals(DumpConstants.CHILDREN)) {
                // this means empty children, just quit from here
                // before that we have to set the cursor to the end of the current resource
                if (xmlReader.hasNext()) {
                    do {
                        xmlReader.next();
                    } while (xmlReader.hasNext() && !(xmlReader.isEndElement()
                            && xmlReader.getLocalName().equals(DumpConstants.RESOURCE)));
                }
                return;
            }
        } while (!xmlReader.isStartElement() && xmlReader.hasNext());

        while (xmlReader.hasNext() && xmlReader.isStartElement()
                && xmlReader.getLocalName().equals(DumpConstants.RESOURCE)) {
            // prepare the children absolute path
            String childName = xmlReader.getAttributeValue(null, DumpConstants.RESOURCE_NAME);
            String fileResourceName = childName;
            String childFilePath = filePath + File.separator + fileResourceName;
            String childPath = (path.equals("/") ? "" : path) + "/" + childName;

            checkOutRecursively(xmlReader, childFilePath, childPath, callback);

            while ((!xmlReader.isStartElement() && xmlReader.hasNext())
                    && !(xmlReader.isEndElement() && xmlReader.getLocalName().equals(DumpConstants.CHILDREN))) {
                xmlReader.next();
            }
            if (xmlReader.isEndElement() && xmlReader.getLocalName().equals(DumpConstants.CHILDREN)) {
                // we are in the end of the children tag.
                break;
            }
        }
        // consuming the stream until the resource end element found
        while (xmlReader.hasNext()
                && !(xmlReader.isEndElement() && xmlReader.getLocalName().equals(DumpConstants.RESOURCE))) {
            xmlReader.next();
        }
    }
}