org.jumpmind.symmetric.file.FileSyncZipDataWriter.java Source code

Java tutorial

Introduction

Here is the source code for org.jumpmind.symmetric.file.FileSyncZipDataWriter.java

Source

/**
 * Licensed to JumpMind Inc under one or more contributor
 * license agreements.  See the NOTICE file distributed
 * with this work for additional information regarding
 * copyright ownership.  JumpMind Inc licenses this file
 * to you under the GNU General Public License, version 3.0 (GPLv3)
 * (the "License"); you may not use this file except in compliance
 * with the License.
 *
 * You should have received a copy of the GNU General Public License,
 * version 3.0 (GPLv3) along with this library; if not, see
 * <http://www.gnu.org/licenses/>.
 *
 * 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.jumpmind.symmetric.file;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.jumpmind.db.model.Table;
import org.jumpmind.exception.IoException;
import org.jumpmind.symmetric.io.data.Batch;
import org.jumpmind.symmetric.io.data.CsvData;
import org.jumpmind.symmetric.io.data.DataContext;
import org.jumpmind.symmetric.io.data.DataEventType;
import org.jumpmind.symmetric.io.data.IDataWriter;
import org.jumpmind.symmetric.io.stage.IStagedResource;
import org.jumpmind.symmetric.model.FileConflictStrategy;
import org.jumpmind.symmetric.model.FileSnapshot;
import org.jumpmind.symmetric.model.FileSnapshot.LastEventType;
import org.jumpmind.symmetric.model.FileTrigger;
import org.jumpmind.symmetric.model.FileTriggerRouter;
import org.jumpmind.symmetric.model.Node;
import org.jumpmind.symmetric.service.IFileSyncService;
import org.jumpmind.symmetric.service.INodeService;
import org.jumpmind.util.Statistics;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class FileSyncZipDataWriter implements IDataWriter {

    static final Logger log = LoggerFactory.getLogger(FileSyncZipDataWriter.class);

    protected long byteCount;
    protected long maxBytesToSync;
    protected IFileSyncService fileSyncService;
    protected IStagedResource stagedResource;
    protected ZipOutputStream zos;
    protected Table snapshotTable;
    protected Batch batch;
    protected Map<Batch, Statistics> statistics = new HashMap<Batch, Statistics>();
    protected List<FileSnapshot> snapshotEvents;
    protected DataContext context;
    protected INodeService nodeService;

    public FileSyncZipDataWriter(long maxBytesToSync, IFileSyncService fileSyncService, INodeService nodeService,
            IStagedResource stagedResource) {
        this.maxBytesToSync = maxBytesToSync;
        this.fileSyncService = fileSyncService;
        this.stagedResource = stagedResource;
        this.nodeService = nodeService;
    }

    public void open(DataContext context) {
        this.context = context;
    }

    public void close() {
    }

    public Map<Batch, Statistics> getStatistics() {
        return statistics;
    }

    public void start(Batch batch) {
        this.batch = batch;
        this.statistics.put(batch, new Statistics());
        this.snapshotEvents = new ArrayList<FileSnapshot>();
    }

    public boolean start(Table table) {
        this.snapshotTable = table;
        return true;
    }

    public void write(CsvData data) {
        DataEventType eventType = data.getDataEventType();
        if (eventType == DataEventType.INSERT || eventType == DataEventType.UPDATE) {
            Map<String, String> columnData = data.toColumnNameValuePairs(snapshotTable.getColumnNames(),
                    CsvData.ROW_DATA);
            Map<String, String> oldColumnData = data.toColumnNameValuePairs(snapshotTable.getColumnNames(),
                    CsvData.OLD_DATA);

            FileSnapshot snapshot = new FileSnapshot();
            snapshot.setTriggerId(columnData.get("TRIGGER_ID"));
            snapshot.setRouterId(columnData.get("ROUTER_ID"));
            snapshot.setFileModifiedTime(Long.parseLong(columnData.get("FILE_MODIFIED_TIME")));
            snapshot.setCrc32Checksum(Long.parseLong(columnData.get("CRC32_CHECKSUM")));
            String oldChecksum = oldColumnData.get("CRC32_CHECKSUM");
            if (StringUtils.isNotBlank(oldChecksum)) {
                snapshot.setOldCrc32Checksum(Long.parseLong(oldChecksum));
            }
            snapshot.setFileSize(Long.parseLong(columnData.get("FILE_SIZE")));
            snapshot.setLastUpdateBy(columnData.get("LAST_UPDATE_BY"));
            snapshot.setFileName(columnData.get("FILE_NAME"));
            snapshot.setRelativeDir(columnData.get("RELATIVE_DIR"));
            snapshot.setLastEventType(LastEventType.fromCode(columnData.get("LAST_EVENT_TYPE")));
            snapshotEvents.add(snapshot);
        } else if (eventType == DataEventType.RELOAD) {
            String targetNodeId = context.getBatch().getTargetNodeId();
            Node targetNode = nodeService.findNode(targetNodeId);
            List<FileTriggerRouter> fileTriggerRouters = fileSyncService.getFileTriggerRoutersForCurrentNode();
            for (FileTriggerRouter fileTriggerRouter : fileTriggerRouters) {
                if (fileTriggerRouter.isEnabled() && fileTriggerRouter.isInitialLoadEnabled()
                        && fileTriggerRouter.getRouter().getNodeGroupLink().getTargetNodeGroupId()
                                .equals(targetNode.getNodeGroupId())) {
                    DirectorySnapshot directorySnapshot = fileSyncService.getDirectorySnapshot(fileTriggerRouter);
                    snapshotEvents.addAll(directorySnapshot);
                }
            }
        }
    }

    public void end(Table table) {
    }

    public void end(Batch batch, boolean inError) {

        try {
            if (!inError) {
                if (zos == null) {
                    zos = new ZipOutputStream(stagedResource.getOutputStream());
                }

                Map<String, LastEventType> entries = new HashMap<String, LastEventType>();
                StringBuilder script = new StringBuilder("fileList = new HashMap();\n");
                for (FileSnapshot snapshot : snapshotEvents) {
                    FileTriggerRouter triggerRouter = fileSyncService.getFileTriggerRouter(snapshot.getTriggerId(),
                            snapshot.getRouterId());
                    if (triggerRouter != null) {
                        StringBuilder command = new StringBuilder("\n");
                        LastEventType eventType = snapshot.getLastEventType();

                        FileTrigger fileTrigger = triggerRouter.getFileTrigger();

                        String targetBaseDir = ((triggerRouter.getTargetBaseDir() == null) ? null
                                : triggerRouter.getTargetBaseDir().replace('\\', '/'));
                        if (StringUtils.isBlank(targetBaseDir)) {
                            targetBaseDir = ((fileTrigger.getBaseDir() == null) ? null
                                    : fileTrigger.getBaseDir().replace('\\', '/'));
                        }
                        targetBaseDir = StringEscapeUtils.escapeJava(targetBaseDir);

                        command.append("targetBaseDir = \"").append(targetBaseDir).append("\";\n");
                        command.append("processFile = true;\n");
                        command.append("sourceFileName = \"").append(snapshot.getFileName()).append("\";\n");
                        command.append("targetRelativeDir = \"");
                        if (!snapshot.getRelativeDir().equals(".")) {
                            command.append(StringEscapeUtils.escapeJava(snapshot.getRelativeDir()));
                            command.append("\";\n");
                        } else {
                            command.append("\";\n");
                        }
                        command.append("targetFileName = sourceFileName;\n");
                        command.append("sourceFilePath = \"");
                        command.append(StringEscapeUtils.escapeJava(snapshot.getRelativeDir())).append("\";\n");

                        StringBuilder entryName = new StringBuilder(Long.toString(batch.getBatchId()));
                        entryName.append("/");
                        if (!snapshot.getRelativeDir().equals(".")) {
                            entryName.append(snapshot.getRelativeDir()).append("/");
                        }
                        entryName.append(snapshot.getFileName());

                        File file = fileTrigger.createSourceFile(snapshot);
                        if (file.isDirectory()) {
                            entryName.append("/");
                        }

                        if (StringUtils.isNotBlank(fileTrigger.getBeforeCopyScript())) {
                            command.append(fileTrigger.getBeforeCopyScript()).append("\n");
                        }

                        command.append("if (processFile) {\n");
                        String targetFile = "targetBaseDir + \"/\" + targetRelativeDir + \"/\" + targetFileName";

                        switch (eventType) {
                        case CREATE:
                        case MODIFY:
                            if (file.exists()) {
                                command.append("  File targetBaseDirFile = new File(targetBaseDir);\n");
                                command.append("  if (!targetBaseDirFile.exists()) {\n");
                                command.append("    targetBaseDirFile.mkdirs();\n");
                                command.append("  }\n");
                                command.append("  java.io.File sourceFile = new java.io.File(batchDir + \"/\"");
                                if (!snapshot.getRelativeDir().equals(".")) {
                                    command.append(" + sourceFilePath + \"/\"");
                                }
                                command.append(" + sourceFileName");
                                command.append(");\n");

                                command.append("  java.io.File targetFile = new java.io.File(");
                                command.append(targetFile);
                                command.append(");\n");

                                // no need to copy directory if it already exists
                                command.append("  if (targetFile.exists() && targetFile.isDirectory()) {\n");
                                command.append("      processFile = false;\n");
                                command.append("  }\n");

                                // conflict resolution
                                FileConflictStrategy conflictStrategy = triggerRouter.getConflictStrategy();
                                if (conflictStrategy == FileConflictStrategy.TARGET_WINS
                                        || conflictStrategy == FileConflictStrategy.MANUAL) {
                                    command.append("  if (targetFile.exists() && !targetFile.isDirectory()) {\n");
                                    command.append(
                                            "    long targetChecksum = org.apache.commons.io.FileUtils.checksumCRC32(targetFile);\n");
                                    command.append("    if (targetChecksum != " + snapshot.getOldCrc32Checksum()
                                            + "L) {\n");
                                    if (conflictStrategy == FileConflictStrategy.MANUAL) {
                                        command.append(
                                                "      throw new org.jumpmind.symmetric.file.FileConflictException(targetFileName + \" was in conflict \");\n");
                                    } else {
                                        command.append("      processFile = false;\n");
                                    }
                                    command.append("    }\n");
                                    command.append("  }\n");
                                }

                                command.append("  if (processFile) {\n");
                                command.append("    if (sourceFile.isDirectory()) {\n");
                                command.append(
                                        "      org.apache.commons.io.FileUtils.copyDirectory(sourceFile, targetFile, true);\n");
                                command.append("    } else {\n");
                                command.append(
                                        "      org.apache.commons.io.FileUtils.copyFile(sourceFile, targetFile, true);\n");
                                command.append("    }\n");
                                command.append("  }\n");
                                command.append("  fileList.put(").append(targetFile).append(",\"");
                                command.append(eventType.getCode());
                                command.append("\");\n");
                            }
                            break;
                        case DELETE:
                            command.append("  org.apache.commons.io.FileUtils.deleteQuietly(new java.io.File(");
                            command.append(targetFile);
                            command.append("));\n");
                            command.append("  fileList.put(").append(targetFile).append(",\"");
                            command.append(eventType.getCode());
                            command.append("\");\n");
                            break;
                        default:
                            break;
                        }

                        if (StringUtils.isNotBlank(fileTrigger.getAfterCopyScript())) {
                            command.append(fileTrigger.getAfterCopyScript()).append("\n");
                        }

                        LastEventType previousEventForEntry = entries.get(entryName.toString());
                        boolean process = true;
                        if (previousEventForEntry != null) {
                            if ((previousEventForEntry == eventType)
                                    || (previousEventForEntry == LastEventType.CREATE
                                            && eventType == LastEventType.MODIFY)) {
                                process = false;
                            }
                        }

                        if (process) {
                            if (eventType != LastEventType.DELETE) {
                                if (file.exists()) {
                                    byteCount += file.length();
                                    ZipEntry entry = new ZipEntry(entryName.toString());
                                    entry.setSize(file.length());
                                    entry.setTime(file.lastModified());
                                    zos.putNextEntry(entry);
                                    if (file.isFile()) {
                                        FileInputStream fis = new FileInputStream(file);
                                        try {
                                            IOUtils.copy(fis, zos);
                                        } finally {
                                            IOUtils.closeQuietly(fis);
                                        }
                                    }
                                    zos.closeEntry();
                                    entries.put(entryName.toString(), eventType);
                                } else {
                                    log.warn(
                                            "Could not find the {} file to package for synchronization.  Skipping it.",
                                            file.getAbsolutePath());
                                }
                            }

                            command.append("}\n\n");
                            script.append(command.toString());

                        }

                    } else {
                        log.error(
                                "Could not locate the file trigger ({}) router ({}) to process a snapshot event.  The event will be ignored",
                                snapshot.getTriggerId(), snapshot.getRouterId());
                    }
                }

                script.append("return fileList;\n");
                ZipEntry entry = new ZipEntry(batch.getBatchId() + "/sync.bsh");
                zos.putNextEntry(entry);
                IOUtils.write(script.toString(), zos);
                zos.closeEntry();

                entry = new ZipEntry(batch.getBatchId() + "/batch-info.txt");
                zos.putNextEntry(entry);
                IOUtils.write(batch.getChannelId(), zos);
                zos.closeEntry();

            }
        } catch (IOException e) {
            throw new IoException(e);
        }

    }

    public void finish() {
        try {
            if (zos != null) {
                zos.finish();
                IOUtils.closeQuietly(zos);
            }
        } catch (IOException e) {
            throw new IoException(e);
        } finally {
            stagedResource.close();
            stagedResource.setState(IStagedResource.State.READY);
        }
    }

    public boolean readyToSend() {
        return byteCount > maxBytesToSync;
    }

}