eu.databata.SupplementPropagation.java Source code

Java tutorial

Introduction

Here is the source code for eu.databata.SupplementPropagation.java

Source

/**
 *   Copyright 2014 Nortal AS
 *
 *   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 eu.databata;

import eu.databata.engine.version.VersionProvider;
import eu.databata.engine.version.VersionUtil;

import eu.databata.engine.util.PropagationUtils;

import eu.databata.engine.model.PropagationObject;
import eu.databata.engine.model.PropagationObject.ObjectType;

import eu.databata.engine.dao.PropagationDAO;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

/**
 * An object of this class provides the propagation of one type of supplement data, i.e. packages, package headers,
 * views, or triggers.
 * 
 * @author Aleksei Lissitsin  {@literal<aleksei.lissitsin@webmedia.ee>}
 * @author Maksim Boiko
 */
public class SupplementPropagation {
    private static final Logger LOG = Logger.getLogger(SupplementPropagation.class);

    private File directory;
    private Map<String, PropagationObject> propagatedObjectsHashes = new HashMap<String, PropagationObject>(); // hash ->
    // propagation
    // object
    private SQLPropagationTool sqlExecutor;
    private PropagationDAO propagationDAO;
    private ObjectType objectType;
    private String moduleName;
    private String fileSearchRegexp;
    private VersionProvider versionProvider;
    private boolean simulationMode;
    private File[] propagatedFiles;
    private PropagatorFileHandler propagatorFileLocator;

    public SupplementPropagation(File directory, ObjectType objectType, String moduleName,
            SQLPropagationTool sqlExecutor, PropagationDAO propagationDAO, String fileSearchRegexp) {
        this.directory = directory;
        this.objectType = objectType;
        this.moduleName = moduleName;
        this.sqlExecutor = sqlExecutor;
        this.propagationDAO = propagationDAO;
        this.fileSearchRegexp = fileSearchRegexp;
    }

    public void setSimulationMode(boolean simulationMode) {
        this.simulationMode = simulationMode;
    }

    public void setVersionProvider(VersionProvider versionProvider) {
        this.versionProvider = versionProvider;
    }

    public void setPropagatorFileHandler(PropagatorFileHandler propagatorFileLocator) {
        this.propagatorFileLocator = propagatorFileLocator;
    }

    public void propagate() {
        if (!canPropagate()) {
            LOG.info("Propagation of " + objectType.name()
                    + " objects is not activated. Define corresponding property: 'viewDir', 'packageDir', 'triggerDir'");

            return;
        }
        if (propagatedFiles == null) {
            LOG.info("Nothing to propagate");
            return;
        }
        Set<String> localObjectsHashes = new HashSet<String>();
        Set<String> modifiedObjectsNames = new HashSet<String>();
        List<PropagationObject> propagationObjectsToUpdate = new ArrayList<PropagationObject>();

        long start = System.currentTimeMillis();

        for (File file : propagatedFiles) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("reading file <" + file.getName() + ">, " + this.objectType.name());
            }
            String md5 = null;
            try {
                md5 = getHash(new FileInputStream(file));
            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            }
            localObjectsHashes.add(md5);
            if (!propagatedObjectsHashes.containsKey(md5)) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("New hash is calculated for <" + file.getName() + "> : " + md5);
                }
                String modifiedObjectName = PropagationUtils.removeExtension(file);
                PropagationObject propagationObject = new PropagationObject(moduleName, modifiedObjectName, file,
                        this.objectType, md5);
                if (versionProvider != null) {
                    propagationObject.setVersion(versionProvider.getVersion());
                }
                modifiedObjectsNames.add(modifiedObjectName);
                propagationObjectsToUpdate.add(propagationObject);
            } else {
                propagatedObjectsHashes.remove(md5);
            }
        }
        LOG.info("Hash calculation for <" + objectType.name() + "> took " + (System.currentTimeMillis() - start)
                + " ms.");

        for (PropagationObject propagationObject : propagationObjectsToUpdate) {
            LOG.info("PROPAGATION OBJECT TO UPDATE " + propagationObject.getObjectName() + "; "
                    + propagationObject.getObjectType().name());
        }
        propagateObjects(propagationObjectsToUpdate);
        dropObjects(modifiedObjectsNames);
    }

    /**
     * Searches for *.sql files in given directory, also searches for *.sql files in subdirectory with the name of
     * databaseCode variable set ('ORA', 'MSS', ...). Returns union of described searches results.
     */
    public void collectPropagatedFiles() {
        if (!canPropagate()) {
            return;
        }
        LOG.debug("Supplement directory path: " + directory.getPath());

        this.propagatedFiles = propagatorFileLocator.findSupplementFiles(this.directory, fileSearchRegexp,
                sqlExecutor.getDatabaseCode());
    }

    protected void propagateObjects(List<PropagationObject> propagationObjects) {
        for (PropagationObject propagationObject : propagationObjects) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Propagating <" + propagationObject.getObjectName() + ">");
            }
            if (!canUpdateWithSearch(propagationObject.getObjectName())) {
                LOG.warn("\n!!! Object with name " + propagationObject.getObjectName()
                        + " will NOT be updated (you try to install smaller version)\n");
                continue;
            }
            sqlExecutor.setCurrentDbChange(propagationObject.getPropagatedFile().getName());
            sqlExecutor.executeFile(null, propagationObject.getPropagatedFile());
            if (!simulationMode) {
                updateMD5Entry(propagationObject);
            }
        }
    }

    private void updateMD5Entry(PropagationObject propagationObject) {
        if (propagationDAO.hasPropagationObjectEntry(propagationObject.getObjectName(),
                propagationObject.getModuleName())) {
            propagationDAO.updatePropagationObjectEntry(propagationObject);
        } else {
            propagationDAO.insertPropagationObjectEntry(propagationObject);
        }
    }

    protected void dropObjects(Set<String> modifiedObjectsNames) {
        for (Map.Entry<String, PropagationObject> entry : propagatedObjectsHashes.entrySet()) {
            if (modifiedObjectsNames.contains(entry.getValue().getObjectName())) {
                // do not drop modified objects.
                continue;
            }
            if (!canUpdate(entry.getValue().getVersion())) {
                LOG.warn("\n!!! Object with name " + entry.getValue().getObjectName()
                        + " will NOT be updated (you try to install smaller version)\n");
                continue;
            }
            if (LOG.isDebugEnabled()) {
                LOG.debug("Dropping <" + entry.getValue().getObjectName() + ">");
            }
            sqlExecutor.dropObject(objectType.getSqlName() + " " + entry.getValue().getObjectName());
            propagationDAO.removePropagationObjectEntry(entry.getValue().getObjectName(), moduleName);
        }
    }

    private boolean canUpdateWithSearch(String objectName) {
        for (PropagationObject propagationObject : propagatedObjectsHashes.values()) {
            if (propagationObject.getObjectName().equals(objectName)) {
                return canUpdate(propagationObject.getVersion());
            }
        }

        return true;
    }

    private boolean canUpdate(String version) {
        if (versionProvider == null || StringUtils.isEmpty(versionProvider.getVersion())) {
            return true;
        }
        if (StringUtils.isEmpty(version)) {
            return true;
        }

        return VersionUtil.isEqualOrGreater(versionProvider.getVersion(), version);
    }

    private String getHash(InputStream in) {
        MessageDigest md5;
        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalArgumentException("No MD5 algorithm found!", e);
        }
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int read;
        try {
            while ((read = in.read(buffer)) != -1) {
                os.write(buffer, 0, read);
            }
            os.flush();
            md5.update(os.toByteArray());
        } catch (IOException e) {
            throw new IllegalStateException("Error reading file.", e);
        } finally {
            try {
                in.close();
                os.close();
            } catch (IOException e) {
                throw new IllegalStateException("Error closing stream ", e);
            }
        }
        return convertToHex(md5.digest());
    }

    private String convertToHex(byte[] data) {
        StringBuffer buf = new StringBuffer();
        for (int i = 0; i < data.length; i++) {
            int halfbyte = (data[i] >>> 4) & 0x0F;
            int two_halfs = 0;
            do {
                if ((0 <= halfbyte) && (halfbyte <= 9))
                    buf.append((char) ('0' + halfbyte));
                else
                    buf.append((char) ('a' + (halfbyte - 10)));
                halfbyte = data[i] & 0x0F;
            } while (two_halfs++ < 1);
        }
        return buf.toString();
    }

    private boolean canPropagate() {
        return directory != null;
    }

    public void addHash(String objectMd5Hash, PropagationObject propagationObject) {
        propagatedObjectsHashes.put(objectMd5Hash, propagationObject);
    }
}