Java tutorial
/* DA-NRW Software Suite | ContentBroker Copyright (C) 2013 Historisch-Kulturwissenschaftliche Informationsverarbeitung Universitt zu Kln 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.Collections; import java.util.Date; import java.util.List; import org.apache.commons.io.FilenameUtils; import org.hibernate.Session; import org.hibernate.UnresolvableObjectException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import de.uzk.hki.da.core.UserException.UserExceptionId; import de.uzk.hki.da.model.Node; import de.uzk.hki.da.model.Object; import de.uzk.hki.da.model.ObjectNamedQueryDAO; import de.uzk.hki.da.model.Package; import de.uzk.hki.da.model.PreservationSystem; import de.uzk.hki.da.model.User; import de.uzk.hki.da.service.HibernateUtil; import de.uzk.hki.da.utils.StringUtilities; /** * Provides facility to register objects at a certain node. * * This object is intended to be wired up as a Spring bean and as a singleton. It should get created once and then * the localNode and preservationSystem ids have to be set. Then it should get initialized via the init method. * Only then registerObject should be called. * * @author Daniel M. de Oliveira * @author Thomas Kleinke * */ public class RegisterObjectService { private static final Logger logger = LoggerFactory.getLogger(RegisterObjectService.class); private String localNodeName; private String urnNameSpace; private int localNodeId; private int preservationSystemId; private static boolean singletonInstanceCreated = false; private static boolean initialized = false; /** * */ public RegisterObjectService() { if (singletonInstanceCreated) throw new IllegalStateException("Will not instantiate a second instance."); singletonInstanceCreated = true; } /** * Init method for getting wired up by Spring. */ public void init() { Session session = HibernateUtil.openSession(); session.getTransaction().begin(); Node node = null; PreservationSystem pSystem = null; try { pSystem = (PreservationSystem) session.get(PreservationSystem.class, preservationSystemId); node = (Node) session.get(Node.class, localNodeId); } catch (UnresolvableObjectException e) { throw new IllegalStateException(e); } if (node.getUrn_index() < 0) throw new IllegalStateException("Node's urn_index must not be lower than 0"); urnNameSpace = pSystem.getUrnNameSpace(); localNodeName = node.getName(); session.close(); initialized = true; } /** * Compares the csn/origName pair of a SIP which identifies an object uniquely against the database. * If there is a match, the SIP is considered a delta. If not is considered a primary import. * <br><br> * If the SIP is a delta, a new package gets created and attached to the object (which gets fetched from db). * If it is a primary import, a new object gets created. In this case a new technical identifier gets created, which * is base on the nodes urn index and the preservation systems urnNameSpace. The nodes urn index gets incremented when * generating the identifier. The database record gets updated. For security reasons the the object database gets checked of * the identifier does not already exist. * * @param containerName the file name of the SIP container * @param contractor the contractor who owns the container * * @throws UserException when trying to register a delta record for an object which is not archived (<50) yet * @throws IllegalStateException if the system tries to generate an identifier for which an object already exists. * * @return the object. */ public Object registerObject(String containerName, User contractor) { if (!initialized) throw new IllegalStateException("call init first"); if (contractor == null) throw new IllegalArgumentException("contractor is null"); if (contractor.getShort_name() == null || contractor.getShort_name().isEmpty()) throw new IllegalArgumentException("contractor short name not set"); String origName = convertMaskedSlashes(FilenameUtils.removeExtension(containerName)); Object obj; if ((obj = (new ObjectNamedQueryDAO().getUniqueObject(origName, contractor.getShort_name()))) != null) { // is delta then logger.info("Package is a delta record for Object with identifier: " + obj.getIdentifier()); updateExistingObject(obj, containerName); } else { final String technicalIdentifier = convertURNtoTechnicalIdentifier(generateURNForNode(localNodeId)); // check identifier if (getUniqueObject(technicalIdentifier) != null) throw new IllegalStateException("CRITICAL SYSTEM ERROR: DUPLICATE IDENTIFIER"); logger.info("Creating new Object with identifier " + technicalIdentifier); obj = createNewObject(containerName, origName, contractor); obj.setIdentifier(technicalIdentifier); } return obj; } private void updateExistingObject(Object obj, String containerName) { Package newPkg = new Package(); int max = obj.getLatestPackage().getDelta(); newPkg.setDelta(max + 1); newPkg.setContainerName(containerName); if (obj.getObject_state() < 100) throw new UserException(UserExceptionId.DELTA_RECIEVED_BEFORE_ARCHIVED, "Delta Record fr ein nicht fertig archiviertes Objekt"); obj.getPackages().add(newPkg); } private Object createNewObject(String containerName, String origName, User contractor) { Object obj = new Object(); obj.setObject_state(Object.ObjectStatus.InitState); Package newPkg = new Package(); newPkg.setDelta(1); newPkg.setContainerName(containerName); obj.getPackages().add(newPkg); obj.setContractor(contractor); obj.setCreatedAt(new Date()); obj.setModifiedAt(new Date()); obj.setLast_checked(new Date()); obj.setInitial_node(localNodeName); obj.setOrig_name(origName); return obj; } /** * Replaces %2F inside a string to /. * * @param input the input * @return the string */ private String convertMaskedSlashes(String input) { return input.replaceAll("%2F", "/"); } /** * @param urn * @return */ private String convertURNtoTechnicalIdentifier(String urn) { return urn.replace(urnNameSpace + "-", ""); } private Object getUniqueObject(String identifier) { Session session = HibernateUtil.openSession(); session.getTransaction().begin(); @SuppressWarnings("rawtypes") List l = null; l = session.createQuery("from Object where identifier=?1").setParameter("1", identifier).list(); session.close(); try { l.get(0); } catch (IndexOutOfBoundsException e) { return null; } return (Object) l.get(0); } /** * Generates a URN of the form [nameSpace]-[node_id]-[number]. * * @return the generated URN. * @author Daniel M. de Oliveira */ private String generateURNForNode(int nodeId) { String base = urnNameSpace + "-" + nodeId + "-" + StringUtilities.todayAsSimpleIsoDate(new Date()) + incrementURNindex(nodeId); return base + (new URNCheckDigitGenerator()).checkDigit(base); } /** * Increments the urn_index of node and writes it back to the * database immediately on every call. * * The generated number is ensured to be unique per node_id * (the system never generates the same * number twice for any given [nodeId] across the database). * * @return the new value of the urn index for the node with the id node id. */ private synchronized // only one thread per node is allowed to increment the nodes urn index. int incrementURNindex(int nodeId) { Session session = HibernateUtil.openSession(); session.getTransaction().begin(); Node node = // making sure to work with a local copy of node object to prevent it from being modified by other threads. (Node) session.get(Node.class, nodeId); int incrementedURNIndex = node.getUrn_index() + 1; logger.debug("Updating local node urn index " + node.getUrn_index() + " to " + incrementedURNIndex); session.update(node); // further changes are tracked. node.setUrn_index(incrementedURNIndex); session.getTransaction().commit(); session.close(); if (incrementedURNIndex != node.getUrn_index()) { throw new RuntimeException( "SERIOUS TROUBLE. It seems the database has not been updated properly (value:" + node.getUrn_index() + ")"); } return incrementedURNIndex; } public String getLocalNodeId() { return new Integer(localNodeId).toString(); } public void setLocalNodeId(String localNodeId) { this.localNodeId = Integer.parseInt(localNodeId); } public String getPreservationSystemId() { return new Integer(preservationSystemId).toString(); } public void setPreservationSystemId(String preservationSystemId) { this.preservationSystemId = Integer.parseInt(preservationSystemId); } }