podd.resources.services.EditObjectService.java Source code

Java tutorial

Introduction

Here is the source code for podd.resources.services.EditObjectService.java

Source

/*
 * Copyright (c) 2009 - 2010. School of Information Technology and Electrical
 * Engineering, The University of Queensland.  This software is being developed
 * for the "Phenomics Ontoogy Driven Data Management Project (PODD)" project.
 * PODD is a National e-Research Architecture Taskforce (NeAT) project
 * co-funded by ANDS and ARCS.
 *
 * PODD 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.
 *
 * PODD 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 PODD.  If not, see <http://www.gnu.org/licenses/>.
 */

package podd.resources.services;

import static org.restlet.data.Status.CLIENT_ERROR_BAD_REQUEST;
import static org.restlet.data.Status.CLIENT_ERROR_UNAUTHORIZED;
import static org.restlet.data.Status.SERVER_ERROR_INTERNAL;
import static org.restlet.data.Status.SUCCESS_OK;
import static podd.model.audit.AuditLog.ERROR;
import static podd.model.audit.AuditLog.INSUFFICIENT_ACCESS;
import static podd.server.authz.UserAction.UPDATE;
import static podd.util.fedora.FedoraRepositoryUtil.getCurrentTimeAsString;

import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.restlet.ext.fileupload.RestletFileUpload;
import org.restlet.ext.json.JsonRepresentation;
import org.restlet.representation.Representation;
import org.restlet.representation.Variant;
import org.restlet.resource.ResourceException;
import org.semanticweb.owl.model.OWLAnnotation;
import org.semanticweb.owl.model.OWLClass;
import org.semanticweb.owl.model.OWLClassAssertionAxiom;
import org.semanticweb.owl.model.OWLDataMaxCardinalityRestriction;
import org.semanticweb.owl.model.OWLDataProperty;
import org.semanticweb.owl.model.OWLDescription;
import org.semanticweb.owl.model.OWLEntity;
import org.semanticweb.owl.model.OWLEntityAnnotationAxiom;
import org.semanticweb.owl.model.OWLIndividual;
import org.semanticweb.owl.model.OWLIndividualAxiom;
import org.semanticweb.owl.model.OWLOntology;
import org.semanticweb.owl.model.OWLOntologyChangeException;
import org.semanticweb.owl.model.OWLOntologyCreationException;
import org.semanticweb.owl.model.OWLPropertyExpression;

import podd.exception.DataAccessException;
import podd.exception.OntologyHandlingException;
import podd.model.entity.PoddObject;
import podd.util.owl.OntologyRegistry;

/**
 * @author Faith Davies
 * @version $Id$
 */
public class EditObjectService extends ObjectService {

    private URI objectURI;
    private PoddObject origParent;
    private OntologyRegistry ontologyRegistry;

    public void setOntologyRegistry(OntologyRegistry ontologyRegistry) {
        this.ontologyRegistry = ontologyRegistry;
    }

    /**
      * Rather than overwriting the existing ontology, should we do a merge of the ontologies instead?
      */
    private boolean doMerge = false;

    @Override
    protected Representation doAuthenticatedPost(Representation entity, Variant variant) throws ResourceException {
        Representation representation = null;
        boolean conceptUnchanged = conceptNotChanged();
        boolean parentUnchanged = parentNotChanged();
        if (conceptUnchanged && parentUnchanged) {
            OWLIndividual sbj = dataFactory.getOWLIndividual(objectURI);
            if (copyOntology(objectURI, sbj) && participantPopulator.populateProjectParticipants(poddObject, null)
                    && consolidateObject() && validateObject() && saveObject()) {
                // return success
                getResponse().setStatus(SUCCESS_OK);
                representation = new JsonRepresentation(jsonHelper.convertPoddObjectToReference(poddObject));
            } else {
                // an error has occurred, rollback project participant info
                participantPopulator.rollbackProjectParticipants(poddObject);
            }
        }
        if (!errorMap.isEmpty()) {
            representation = new JsonRepresentation(jsonHelper.convertErrorMap(errorMap));
        }
        return representation;
    }

    @Override
    protected boolean copyOntology(URI objectURI, OWLIndividual sbj) {
        // Reset the data properties and annotation properties, but not any of the object properties
        // Object properties must be preserved to maintain consistency with the database.
        // Resolves Ticket #373
        if (_INFO) {
            LOGGER.info("doMerge: " + doMerge);
        }

        if (!doMerge) {
            OWLOntology ontology = poddObject.getCurrentRelations();
            try {
                ontologyHelper.removeAllEntityAnnotationPropertyAssertions(ontology, sbj.getURI());
                ontologyHelper.removeAllEntityDataPropertyAssertions(ontology, sbj.getURI());
            } catch (OWLOntologyChangeException e) {
                final String msg = "Error resetting data and annotation properties";
                errorHandler.handleException(poddObject.getPid(), "Ontology Error", msg, e);
                auditLogHelper.auditAction(ERROR, authenticatedUser, "Edit Object Service: " + msg, e.toString());
                e.printStackTrace();
                return false;
            }
        } else { // Merging          
            // Remove all overlapping singular properties with cardinality of 1

            handleMergeDataProperties(sentOntology.getDataPropertiesInSignature());

            //Retrieve the OWLIndividual definition from the sent ontology
            for (OWLIndividual sentOntologyIndividual : sentOntology.getIndividualsInSignature()) {
                if (sentOntologyIndividual.getURI().equals(objectURI)) {
                    if (_INFO) {
                        LOGGER.info("Found matching individual in sentOntology");
                    }
                    try {
                        OWLOntology[] ontologies = new OWLOntology[] { poddObject.getCurrentRelations(),
                                poddObject.getConcept().getCurrentDefinition() };
                        OWLClass owlClass = ontologyRegistry.getIndividualType(sbj, ontologies);
                        if (_INFO) {
                            LOGGER.info("owlClass=" + owlClass);
                        }
                        handleMergeDataProperties(owlClass.getDataPropertiesInSignature());
                    } catch (OntologyHandlingException e) {
                        LOGGER.error("Found ontology handling exception", e);
                    }

                }
            }

            if (_INFO) {
                for (OWLIndividualAxiom axiom : sentOntology.getAxioms(sbj)) {
                    LOGGER.info("axiom=" + axiom);

                    //handleMergeAxioms(axiom);
                }
            }

            /*
            try {
            ontologyHelper.removeAxioms(poddObject.getCurrentRelations(), sentOntology.getEntityAnnotationAxioms(sbj));
            } catch (OWLOntologyChangeException e) {
            LOGGER.error(e);
            e.printStackTrace();
            }*/

            Set<URI> annotationURIsForRemoval = new HashSet<URI>();
            for (OWLEntityAnnotationAxiom axiom : sentOntology.getEntityAnnotationAxioms(sbj)) {
                OWLAnnotation annotation = axiom.getAnnotation();
                URI annURI = annotation.getAnnotationURI();

                if (_INFO) {
                    LOGGER.info("axiom=" + axiom);
                    //handleMergeAxioms(axiom);
                    OWLEntity owlEntity = axiom.getSubject();
                    LOGGER.info("subject=" + owlEntity + " annotation=" + annotation + " annotationURI=" + annURI);
                }

                annotationURIsForRemoval.add(annURI);
            }

            // Remove all the properties that are to be replaced
            try {
                ontologyHelper.removeAxiomsByURIs(poddObject.getCurrentRelations(), sbj.getURI(),
                        annotationURIsForRemoval);
            } catch (OWLOntologyChangeException e) {
                LOGGER.error("Found owl ontology change exception", e);
                e.printStackTrace();
            }
        }

        return super.copyOntology(objectURI, sbj);
    }

    private void handleMergeDataProperties(Set<OWLDataProperty> dataProperties) {
        if (dataProperties == null)
            return;

        if (_INFO) {
            for (OWLDataProperty dataProperty : dataProperties) {
                OWLPropertyExpression propertyExpression = dataProperty;
                LOGGER.info("propertyExpression: " + propertyExpression.getClass().getName());
                if (propertyExpression instanceof OWLDataMaxCardinalityRestriction) {
                    OWLDataMaxCardinalityRestriction maxCardRestr = (OWLDataMaxCardinalityRestriction) propertyExpression;
                    int cardinality = maxCardRestr.getCardinality();
                    LOGGER.info("cardinality: " + cardinality);
                }
            }
        }

    }

    @Override
    protected boolean saveObject() {
        try {
            poddObject.setLastModified(getCurrentTimeAsString());
            poddObject.setLastModifier(authenticatedUser);
            objectDao.update(poddObject);
            if (_INFO) {
                LOGGER.info("Action: " + authenticatedUser.getUserName() + " updated object "
                        + poddObject.getObjectName() + " with pid: " + poddObject.getPid());
            }
            return true;
        } catch (DataAccessException e) {
            getResponse().setStatus(SERVER_ERROR_INTERNAL);
            final String msg = "Error occured while saving the object. ";
            errorHandler.handleException(poddObject.getPid(), "Saving Error", msg, e);
            auditLogHelper.auditAction(ERROR, authenticatedUser, "Edit Object Service: " + msg, e.toString());
            return false;
        }
    }

    @Override
    protected boolean authoriseGet() {
        if (null != authenticatedUser && errorMap.isEmpty() && null != poddObject) {
            return manager.decide(authenticatedUser, poddObject, UPDATE);
        }
        return false;
    }

    @Override
    protected Representation doUnauthenticatedGet(Variant variant) throws ResourceException {
        if (null == authenticatedUser) {
            getResponse().setStatus(CLIENT_ERROR_UNAUTHORIZED);
            final String msg = "User must be authenticated to edit objects";
            errorHandler.handleError(GENERAL_MESSAGE_ID, "errorMessage", msg);
            auditLogHelper.auditAction(INSUFFICIENT_ACCESS, authenticatedUser,
                    this.getRequest().getResourceRef().toString(), msg);
        }
        if (!errorMap.isEmpty()) {
            return new JsonRepresentation(jsonHelper.convertErrorMap(errorMap));
        } else {
            return redirectErrorStatus(CLIENT_ERROR_UNAUTHORIZED);
        }
    }

    @Override
    protected void prePostAuthorisation(Representation entity) {
        if (_DEBUG) {
            LOGGER.debug("Edit Object Service");
        }

        errorMap = new HashMap<String, Map<String, List<String>>>();
        errorHandler.setAllObjectsErrorMap(errorMap);
        poddObject = null;
        loadFormData(entity);

        if (null != objectURI && null != sentOntology) {
            try {
                poddObject = objectDao.load(objectURI.getFragment());
                if (null != poddObject) {
                    poddConcept = poddObject.getConcept();
                    origParent = poddObject.getContainer();
                } else {
                    getResponse().setStatus(CLIENT_ERROR_BAD_REQUEST);
                    errorHandler.handleError(GENERAL_MESSAGE_ID, "Invalid PID",
                            "No object can be found with the given URI: " + objectURI);
                }
            } catch (DataAccessException e) {
                getResponse().setStatus(SERVER_ERROR_INTERNAL);
                final String msg = "Error loading object with URI: " + objectURI + ". ";
                errorHandler.handleException(GENERAL_MESSAGE_ID, "Loading Error", msg, e);
                auditLogHelper.auditAction(ERROR, authenticatedUser, "Edit Object Service: " + msg, e.toString());
            }
        } else {
            if (null == objectURI) {
                getResponse().setStatus(CLIENT_ERROR_BAD_REQUEST);
                errorHandler.handleError(GENERAL_MESSAGE_ID, "Invalid Parameters",
                        "The object's URI must be sent in request with the key: URI");
            }
            if (null == sentOntology) {
                getResponse().setStatus(CLIENT_ERROR_BAD_REQUEST);
                errorHandler.handleError(GENERAL_MESSAGE_ID, "Invalid Parameters",
                        "The ontology for the edited object must be sent in the request with the key: ontology");
            }
        }
    }

    private void loadFormData(Representation entity) {
        objectURI = null;
        sentOntology = null;
        doMerge = false;
        RestletFileUpload upload = new RestletFileUpload(new DiskFileItemFactory());
        try {
            List<FileItem> list = upload.parseRepresentation(entity);
            for (FileItem item : list) {
                if (item.isFormField()) {
                    if (item.getFieldName().equals("URI")) {
                        objectURI = URI.create(item.getString().trim());
                    } else if (item.getFieldName().equals("merge")) {
                        doMerge = item.getString().equalsIgnoreCase("true") ? true : false;
                    }
                } else {
                    if (item.getFieldName().equals("ontology")) {
                        sentOntology = ontologyHelper.populateOntology(item.getInputStream());
                    }
                }

            }
        } catch (FileUploadException e) {
            getResponse().setStatus(SERVER_ERROR_INTERNAL);
            final String msg = "Error reading form data. ";
            errorHandler.handleException(GENERAL_MESSAGE_ID, "File Intialisation Error", msg, e);
            auditLogHelper.auditAction(ERROR, authenticatedUser, "Edit Object Service: " + msg, e.toString());
        } catch (IOException e) {
            getResponse().setStatus(SERVER_ERROR_INTERNAL);
            final String msg = "Error reading file. ";
            errorHandler.handleException(GENERAL_MESSAGE_ID, "File Initalisation Error", msg, e);
            auditLogHelper.auditAction(ERROR, authenticatedUser, "Edit Object Service: " + msg, e.toString());
        } catch (OWLOntologyCreationException e) {
            getResponse().setStatus(SERVER_ERROR_INTERNAL);
            final String msg = "Error creating ontology. ";
            errorHandler.handleException(GENERAL_MESSAGE_ID, "Ontology Creation Error", msg, e);
            auditLogHelper.auditAction(ERROR, authenticatedUser, "Edit Object Service: " + msg, e.toString());
        }
    }

    private boolean parentNotChanged() {
        PoddObject parentFromOntology = loadParentFromOntology(objectURI);
        if (null == origParent) {
            return null == parentFromOntology;
        }
        boolean parentUnchanged = origParent.equals(parentFromOntology);
        if (!parentUnchanged) {
            getResponse().setStatus(CLIENT_ERROR_BAD_REQUEST);
            errorHandler.handleError(poddObject.getPid(), "Ontology Error",
                    "The Parent of an object can not be changed using this service.");
        }
        return parentUnchanged;
    }

    private boolean conceptNotChanged() {
        String objectType = "";
        // get the object type from the ontology
        OWLIndividual sbj = dataFactory.getOWLIndividual(objectURI);
        for (OWLClassAssertionAxiom assertion : ontologyHelper.getClassAssertions(sentOntology, sbj)) {
            OWLDescription owlDescription = assertion.getDescription();
            if (!owlDescription.isOWLThing()
                    && owlHelper.isPoddClasses(owlDescription, poddConcept.getCurrentDefinition())) {
                objectType = owlDescription.toString();
            }
        }
        boolean conceptUnchanged = poddConcept.getLocalName().equals(objectType);
        if (!conceptUnchanged) {
            getResponse().setStatus(CLIENT_ERROR_BAD_REQUEST);
            errorHandler.handleError(poddObject.getPid(), "Ontology Error",
                    "The Concept for an object can not be changed using this service.");
        }
        return conceptUnchanged;
    }
}