de.uzk.hki.da.core.RepairService.java Source code

Java tutorial

Introduction

Here is the source code for de.uzk.hki.da.core.RepairService.java

Source

/*
  DA-NRW Software Suite | ContentBroker
  Copyright (C) 2015 LVR  InfoKom
    
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.
    
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
    
  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package de.uzk.hki.da.core;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.mail.MessagingException;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.uzk.hki.da.grid.IrodsCommandLineConnector;
import de.uzk.hki.da.model.Copy;
import de.uzk.hki.da.model.Node;
import de.uzk.hki.da.model.Object;
import de.uzk.hki.da.model.Package;
import de.uzk.hki.da.model.PreservationSystem;
import de.uzk.hki.da.model.WorkArea;
import de.uzk.hki.da.service.HibernateUtil;
import de.uzk.hki.da.service.Mail;
import de.uzk.hki.da.utils.StringUtilities;

/**
 * Basic Functions for object/package checking
 * @author Jens Peters
 *
 */
public class RepairService {

    private static final Logger logger = LoggerFactory.getLogger(RepairService.class);
    private String zoneName;
    private String replDestinations;
    private Node node;
    private PreservationSystem pSystem;

    public RepairService(PreservationSystem pSystem, Node node, String zoneName, String replDestinations) {
        this.pSystem = pSystem;
        this.node = node;
        this.zoneName = zoneName;
        this.replDestinations = replDestinations;
    }

    public synchronized Object fetchObjectForAudit() {

        Session session = null;
        Object objectToAudit = null;
        try {
            String nodeName = this.node.getName();

            session = HibernateUtil.openSession();

            Calendar now = Calendar.getInstance();
            now.add(Calendar.HOUR_OF_DAY, -24);

            try {
                Query query = session
                        .createQuery("from Object o where o.initial_node = ?1 and o.last_checked < ?2 and "
                                + "o.object_state = ?3 " + "order by o.last_checked asc");
                query.setParameter("1", nodeName);
                query.setTimestamp("2", now.getTime());
                query.setParameter("3", Object.ObjectStatus.ArchivedAndValidAndNotInWorkflow);

                @SuppressWarnings("unchecked")
                List<Object> obbiList = query.list();

                if (obbiList.size() > 0) {
                    objectToAudit = (Object) obbiList.get(0);

                    for (Package p : objectToAudit.getPackages()) {
                        p.getCopies().size();
                    }

                    objectToAudit.setObject_state(Object.ObjectStatus.UnderAudit);
                    Transaction trans = session.beginTransaction();
                    session.save(objectToAudit);
                    trans.commit();
                }
            } finally {
                session.close();
            }

            return objectToAudit;

        } catch (IndexOutOfBoundsException e) {
            if (session != null)
                session.close();
            return null;
        }
    }

    /**
     * Updates the object state, sets the current time, and updates
     * the database object accordingly
     * @param object
     * @param auditResult
     */
    public synchronized void unlockObject(Object object) {

        object.setLast_checked(new Date());
        Session session = HibernateUtil.openSession();
        session.beginTransaction();
        session.update(object);
        object.setObject_state(Object.ObjectStatus.ArchivedAndValidAndNotInWorkflow);
        session.getTransaction().commit();
        session.close();
    }

    /**
     * Send email.
     *
     * @param obj the obj
     */
    public void sendEmail(Object obj, String msgText) {
        // send Mail to Admin with Package in Error
        logger.debug("Trying to send email");
        String subject = "[" + "da-nrw".toUpperCase() + "] Problem Report fr " + obj.getIdentifier();
        String to = node.getAdmin().getEmailAddress();
        if (to != null && !to.equals("")) {
            try {
                Mail.queueMail(node.getName(), pSystem.getAdmin().getEmailAddress(), to, subject, msgText,
                        node.getAdmin().isMailsPooled());
            } catch (MessagingException e) {
                logger.error("Sending email problem report for " + obj.getIdentifier() + "failed");
            }
        } else {
            logger.error("Node Admin has no valid Email address!");
        }
    }

    protected void incrementRepair(Package pack) {
        if (pack.getRepair() == null) {
            pack.setRepair(1);
        } else {
            pack.setRepair(pack.getRepair() + 1);
        }
    }

    protected void incrementRepair(Copy cop) {
        if (cop.getRepair() == null) {
            cop.setRepair(1);
        } else {
            cop.setRepair(cop.getRepair() + 1);
        }
    }

    protected void repairLocalPackage(Object object, Package pack, String daoBase, Copy intactCopy) {
        IrodsCommandLineConnector iclc = new IrodsCommandLineConnector();
        String source = "/" + intactCopy.getNode().getIdentifier() + "/federated" + "/" + this.zoneName + "/"
                + daoBase + intactCopy.getPackName() + ".tar";
        ;
        String corruptedOld = "/" + this.zoneName + "/" + daoBase + pack.getName() + ".tar";
        this.incrementRepair(pack);
        String target = "/" + this.zoneName + "/" + daoBase + pack.getName() + ".tar";
        String resc = this.replDestinations;

        logger.debug("Local Package corrupt: " + corruptedOld);
        logger.debug("Will be replaced with: " + source);
        logger.debug("          New Name is: " + target);

        iclc.rsync(source, target, resc);
        String checksum = iclc.computeChecksumForce(target);
        pack.setChecksum(checksum);

        String msg = "Defektes Paket: " + corruptedOld + " ersetzt durch " + source + ", neuer Name ist " + target;
        this.sendEmail(object, msg);
    }

    protected void repairCopyFromLocal(Object object, Copy cop, Package pack, String daoBase) {
        IrodsCommandLineConnector iclc = new IrodsCommandLineConnector();

        String source = "/" + this.zoneName + "/" + daoBase + pack.getName() + ".tar";
        String corruptedOld = "/" + cop.getNode().getIdentifier() + "/federated/" + this.zoneName + "/" + daoBase
                + cop.getPackName() + ".tar";

        this.incrementRepair(cop);
        String copyPath = this.zoneName + "/" + daoBase + cop.getPackName() + ".tar";
        String target = "/" + cop.getNode().getIdentifier() + "/federated/" + copyPath;
        String resc = this.replDestinations;

        logger.debug("Foreign Package corrupt: " + corruptedOld);
        logger.debug("  Will be replaced with: " + source);
        logger.debug("            New Name is: " + target);

        iclc.rsync(source, target, resc);
        cop.setChecksum(pack.getChecksum());
        cop.setPath(copyPath);

        String msg = "Defektes Paket: " + corruptedOld + " ersetzt durch " + source + ", neuer Name ist " + target;
        this.sendEmail(object, msg);
    }

    protected boolean estimatePackageChecksumms(Object object, Package pack, String daoBase) {
        IrodsCommandLineConnector iclc = new IrodsCommandLineConnector();

        boolean completelyValid = true;

        String dao = "/" + this.zoneName + "/" + daoBase + pack.getName() + ".tar";
        logger.debug("Checking: " + dao);

        String localChecksum = "";
        try {
            localChecksum = iclc.computeChecksumForce(dao);
        } catch (Exception exc) {
        }

        TreeMap<String, ArrayList<Copy>> replChecks = new TreeMap<String, ArrayList<Copy>>();
        for (Copy copy : pack.getCopies()) {
            String checksum = copy.getChecksum();
            ArrayList<Copy> copyList = replChecks.get(checksum);
            if (copyList == null) {
                copyList = new ArrayList<Copy>();
                replChecks.put(checksum, copyList);
            }
            copyList.add(copy);
        }

        logger.debug("Old (DB) Checksum is: " + pack.getChecksum());
        logger.debug("Computed Checksum is: " + localChecksum);
        if (StringUtilities.isNotSet(pack.getChecksum()) || (!pack.getChecksum().equals(localChecksum))) {
            completelyValid = false;
        }

        for (Map.Entry<String, ArrayList<Copy>> replCheck : replChecks.entrySet()) {
            String checksum = replCheck.getKey();
            ArrayList<Copy> copyList = replCheck.getValue();
            for (int nnn = 0; nnn < copyList.size(); nnn++) {
                String nodeIdent = copyList.get(nnn).getNode().getIdentifier();
                logger.debug("  Remote Checksum is: " + checksum + " on " + nodeIdent);
            }
            if (StringUtilities.isNotSet(pack.getChecksum()) || (!pack.getChecksum().equals(checksum))) {
                completelyValid = false;
            }
        }

        if (completelyValid) {
            return true;
        }

        String votedChecksum = null;
        int maxCheckMatch = 0;
        for (Map.Entry<String, ArrayList<Copy>> replCheck : replChecks.entrySet()) {
            String checksum = replCheck.getKey();
            if (StringUtilities.isSet(checksum)) {
                ArrayList<Copy> copyList = replCheck.getValue();
                if (maxCheckMatch < copyList.size()) {
                    votedChecksum = checksum;
                    maxCheckMatch = copyList.size();
                } else if (maxCheckMatch == copyList.size()) {
                    if (checksum.equals(localChecksum) || checksum.equals(pack.getChecksum())) {
                        votedChecksum = checksum;
                    }
                }
            }
        }

        if (maxCheckMatch < 2) {
            if (localChecksum.equals(pack.getChecksum())) {
                votedChecksum = localChecksum;
            } else if (!localChecksum.equals(votedChecksum) && !pack.getChecksum().equals(votedChecksum)) {
                String msg = "Package " + pack.getName() + " kann nicht maschinell repariert werden, "
                        + " da nicht ermittelt werden kann, welche Kopie gltig ist.";
                this.sendEmail(object, msg);

                logger.error("Package " + pack.getName() + " kann nicht maschinell repariert werden");
                return false;
            }
        }

        if (!localChecksum.equals(votedChecksum)) {
            Copy intactCopy = replChecks.get(votedChecksum).get(0);
            this.repairLocalPackage(object, pack, daoBase, intactCopy);
        } else if (!pack.getChecksum().equals(votedChecksum)) {
            pack.setChecksum(votedChecksum);
        }

        for (Map.Entry<String, ArrayList<Copy>> replCheck : replChecks.entrySet()) {
            String checksum = replCheck.getKey();
            if (!votedChecksum.equals(checksum)) {
                ArrayList<Copy> copyList = replCheck.getValue();
                for (int nnn = 0; nnn < copyList.size(); nnn++) {
                    Copy cop = copyList.get(nnn);
                    if (votedChecksum.equals(pack.getChecksum())) {
                        this.repairCopyFromLocal(object, cop, pack, daoBase);
                    }
                }
            }
        }

        Session session = HibernateUtil.openSession();
        session.update(pack);

        Transaction transi = session.beginTransaction();

        session.save(pack);
        transi.commit();
        session.close();

        return false;
    }

    public boolean estimateChecksumms(Object obj) {
        Calendar olderThan = Calendar.getInstance();
        olderThan.add(Calendar.DAY_OF_YEAR, -365);

        logger.debug("Check Object " + obj.getIdentifier());
        boolean completelyValid = true;
        if (obj.getContractor() == null) {
            String err = "Could not determine valid Contractor for object " + obj.getIdentifier();
            logger.error(err);
            return false;
        }
        String daoBase = WorkArea.AIP + "/" + obj.getContractor().getShort_name() + "/" + obj.getIdentifier() + "/"
                + obj.getIdentifier() + ".pack_";

        for (Package pack : obj.getPackages()) {
            completelyValid &= this.estimatePackageChecksumms(obj, pack, daoBase);
        }

        return completelyValid;
    }
}