org.eclipse.vorto.repository.internal.service.JcrModelRepository.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.vorto.repository.internal.service.JcrModelRepository.java

Source

/*******************************************************************************
 * Copyright (c) 2015, 2016 Bosch Software Innovations GmbH and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 *   
 * The Eclipse Public License is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * The Eclipse Distribution License is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *   
 * Contributors:
 * Bosch Software Innovations GmbH - Please refer to git log
 *******************************************************************************/
package org.eclipse.vorto.repository.internal.service;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.jcr.Binary;
import javax.jcr.Item;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.PropertyIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
import javax.jcr.query.Query;
import javax.jcr.query.QueryResult;
import javax.jcr.query.Row;
import javax.jcr.query.RowIterator;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.eclipse.vorto.repository.internal.service.utils.ModelReferencesHelper;
import org.eclipse.vorto.repository.internal.service.utils.ModelSearchUtil;
import org.eclipse.vorto.repository.internal.service.validation.DuplicateModelValidation;
import org.eclipse.vorto.repository.internal.service.validation.ModelReferencesValidation;
import org.eclipse.vorto.repository.model.ModelEMFResource;
import org.eclipse.vorto.repository.model.ModelId;
import org.eclipse.vorto.repository.model.ModelResource;
import org.eclipse.vorto.repository.model.ModelType;
import org.eclipse.vorto.repository.model.UploadModelResult;
import org.eclipse.vorto.repository.model.User;
import org.eclipse.vorto.repository.notification.INotificationService;
import org.eclipse.vorto.repository.notification.message.CheckinMessage;
import org.eclipse.vorto.repository.service.FatalModelRepositoryException;
import org.eclipse.vorto.repository.service.IModelRepository;
import org.eclipse.vorto.repository.service.ModelNotFoundException;
import org.eclipse.vorto.repository.service.ModelReferentialIntegrityException;
import org.eclipse.vorto.repository.service.UserRepository;
import org.eclipse.vorto.repository.validation.IModelValidator;
import org.eclipse.vorto.repository.validation.ValidationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

/**
 * Implementation of the repository using JCR
 * 
 * @author Alexander Edelmann
 *
 */
@Service
public class JcrModelRepository implements IModelRepository {

    @Autowired
    private Session session;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private ModelSearchUtil modelSearchUtil;

    public ModelSearchUtil getModelSearchUtil() {
        return modelSearchUtil;
    }

    public void setModelSearchUtil(ModelSearchUtil modelSearchUtil) {
        this.modelSearchUtil = modelSearchUtil;
    }

    public UserRepository getUserRepository() {
        return userRepository;
    }

    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Autowired
    private INotificationService notificationService;

    public INotificationService getNotificationService() {
        return notificationService;
    }

    public void setNotificationService(INotificationService notificationService) {
        this.notificationService = notificationService;
    }

    private List<IModelValidator> validators = new LinkedList<IModelValidator>();

    private File tmpDirectory = null;

    private static Logger logger = Logger.getLogger(JcrModelRepository.class);

    @Override
    public List<ModelResource> search(String queryExpression) {
        if (queryExpression == null || queryExpression.isEmpty()) {
            queryExpression = "*";
        }
        try {
            List<ModelResource> modelResources = new ArrayList<>();

            Query query = modelSearchUtil.createQueryFromExpression(session, queryExpression);

            logger.debug("Searching repository with expression " + query.getStatement());
            QueryResult result = query.execute();
            RowIterator rowIterator = result.getRows();

            while (rowIterator.hasNext()) {
                Row row = rowIterator.nextRow();
                Node currentNode = row.getNode();
                if (currentNode.hasProperty("vorto:type") && !isMappingNode(currentNode)) {
                    try {
                        modelResources.add(createModelResource(currentNode));
                    } catch (Exception ex) {
                        // model is corrupt ,ignoring....
                    }
                }
            }

            return modelResources;
        } catch (RepositoryException e) {
            throw new RuntimeException("Could not create query manager", e);
        }
    }

    private boolean isMappingNode(Node node) throws RepositoryException {
        return node.hasProperty("vorto:type")
                && ModelType.valueOf(node.getProperty("vorto:type").getString()) == ModelType.Mapping;
    }

    private ModelResource createModelResource(Node node) throws RepositoryException {
        ModelResource resource = new ModelResource(ModelId.fromPath(node.getParent().getPath()),
                ModelType.valueOf(node.getProperty("vorto:type").getString()));
        resource.setDescription(node.getProperty("vorto:description").getString());
        resource.setDisplayName(node.getProperty("vorto:displayname").getString());
        resource.setCreationDate(node.getProperty("jcr:created").getDate().getTime());
        if (node.hasProperty("vorto:author")) {
            resource.setAuthor(node.getProperty("vorto:author").getString());
        }

        if (node.hasProperty("vorto:references")) {
            Value[] referenceValues = node.getProperty("vorto:references").getValues();
            if (referenceValues != null) {
                ModelReferencesHelper referenceHelper = new ModelReferencesHelper();
                for (Value referValue : referenceValues) {
                    String nodeUuid = referValue.getString();
                    Node referencedNode = session.getNodeByIdentifier(nodeUuid);
                    referenceHelper.addModelReference(
                            ModelId.fromPath(referencedNode.getParent().getPath()).getPrettyFormat());
                }
                resource.setReferences(referenceHelper.getReferences());
            }
        }

        PropertyIterator propIter = node.getReferences();
        while (propIter.hasNext()) {
            Property prop = propIter.nextProperty();
            Node referencedByFileNode = prop.getParent();
            final ModelId referencedById = ModelId.fromPath(referencedByFileNode.getParent().getPath());
            resource.getReferencedBy().add(referencedById);
        }

        return resource;
    }

    @Override
    public byte[] getModelContent(ModelId modelId, ContentType contentType) {
        try {
            Node folderNode = session.getNode(modelId.getFullPath());
            Node fileNode = (Node) folderNode.getNodes(modelId.getName() + "*").next();
            Node fileItem = (Node) fileNode.getPrimaryItem();
            InputStream is = fileItem.getProperty("jcr:data").getBinary().getStream();

            if (contentType == ContentType.XMI) {
                ModelEMFResource resource = (ModelEMFResource) ModelParserFactory.getParser(fileNode.getName())
                        .parse(is);
                return resource.toXMI();
            } else {
                return IOUtils.toByteArray(is);
            }
        } catch (PathNotFoundException e) {
            throw new ModelNotFoundException("Could not find model with the given model id", e);
        } catch (Exception e) {
            throw new FatalModelRepositoryException("Something went wrong accessing the repository", e);
        }
    }

    @Override
    public UploadModelResult upload(byte[] content, String fileName) {
        try {
            if (StringUtils.isEmpty(fileName)) {
                return UploadModelResult.invalid(new ValidationException("Filename is invalid", null));
            }

            ModelResource resource = ModelParserFactory.getParser(fileName)
                    .parse(new ByteArrayInputStream(content));

            for (IModelValidator validator : validators) {
                validator.validate(resource);
            }
            return UploadModelResult.valid(createUploadHandle(resource.getId(), content, fileName), resource);
        } catch (ValidationException validationException) {
            return UploadModelResult.invalid(validationException);
        }
    }

    private String createUploadHandle(ModelId id, byte[] content, String fileName) {
        try {
            String filePath = fileName.replace("\\", "/");
            File file = File.createTempFile("vorto", StringUtils.getFilename(filePath));
            IOUtils.write(content, new FileOutputStream(file));
            logger.debug("Created temporary file for upload : " + file.getName());
            return file.getName();
        } catch (IOException e) {
            throw new RuntimeException("Could not create temporary file for uploaded model", e);
        }
    }

    @Override
    public void checkin(String handleId, final String author) {
        InputStream contentAsStream;
        final File uploadedFile;
        try {
            uploadedFile = loadUploadedFile(handleId);
            logger.info("Found temporary file for handleId : " + uploadedFile.getName());
            contentAsStream = new FileInputStream(uploadedFile);
        } catch (FileNotFoundException e1) {
            throw new ModelNotFoundException("Could not find uploaded model with the specified handle", e1);
        }

        try {
            final byte[] content = IOUtils.toByteArray(new FileInputStream(uploadedFile));
            final ModelResource resource = ModelParserFactory.getParser(uploadedFile.getName())
                    .parse(contentAsStream);

            logger.info("Checkin for " + resource.getId().getPrettyFormat()
                    + " was approved. Proceeding with checkin...");
            Node folderNode = createNodeForModelId(resource.getId());
            Node fileNode = folderNode.addNode(resource.getId().getName() + resource.getModelType().getExtension(),
                    "nt:file");
            fileNode.addMixin("vorto:meta");
            fileNode.addMixin("mix:referenceable");
            fileNode.setProperty("vorto:author", author);
            Node contentNode = fileNode.addNode("jcr:content", "nt:resource");
            Binary binary = session.getValueFactory().createBinary(new ByteArrayInputStream(content));
            contentNode.setProperty("jcr:data", binary);
            session.save();
            logger.info("Checkin successful.");
            FileUtils.deleteQuietly(uploadedFile);

            // Email Notification
            notifyWatchers(resource, author);

        } catch (Exception e) {
            throw new FatalModelRepositoryException("Problem checking in uploaded model", e);
        }

    }

    private void notifyWatchers(ModelResource resource, String author) {

        resource.setAuthor(author);

        for (User recipient : userRepository.findAll()) {
            if (recipient.getHasWatchOnRepository()) {
                Map<String, Object> context = new HashMap<>(2);
                context.put("user", recipient);
                context.put("model", resource);
                notificationService.sendNotification(new CheckinMessage(recipient, resource));
            }
        }
    }

    private File loadUploadedFile(String handleId) {
        return new File(tmpDirectory, handleId);
    }

    private Node createNodeForModelId(ModelId id) throws RepositoryException {
        StringBuilder pathBuilder = new StringBuilder();
        Iterator<String> modelIdIterator = id.iterator();
        Node rootNode = session.getRootNode();
        while (modelIdIterator.hasNext()) {
            String nextPathFragment = modelIdIterator.next();
            pathBuilder.append(nextPathFragment).append("/");
            try {
                rootNode.getNode(pathBuilder.toString());
            } catch (PathNotFoundException pathNotFound) {
                Node addedNode = rootNode.addNode(pathBuilder.toString(), "nt:folder");
                addedNode.setPrimaryType("nt:folder");
            }
        }

        return rootNode.getNode(id.getFullPath().substring(1));
    }

    @Override
    public ModelResource getById(ModelId modelId) {
        try {
            Node folderNode = session.getNode(modelId.getFullPath());

            Node modelFileNode = folderNode.getNodes("*.type | *.fbmodel | *.infomodel | *.mapping").nextNode();

            ModelResource modelResource = createModelResource(modelFileNode);
            if (modelResource.getModelType() == ModelType.InformationModel) {
                NodeIterator imageNodeIterator = folderNode.getNodes("img.png*");
                if (imageNodeIterator.hasNext()) {
                    modelResource.setHasImage(true);
                }
                for (ModelId referencedById : modelResource.getReferencedBy()) {
                    ModelEMFResource emfResource = getEMFResource(referencedById);
                    modelResource.addTargetPlatform(emfResource.getTargetPlatform());
                }
            }
            return modelResource;
        } catch (PathNotFoundException e) {
            return null;
        } catch (RepositoryException e) {
            throw new RuntimeException("Retrieving Content of Resource: Problem accessing repository", e);
        }
    }

    @Override
    public void removeModel(ModelId modelId) {
        try {
            ModelResource modelResource = getById(modelId);
            if (!modelResource.getReferencedBy().isEmpty()) {
                throw new ModelReferentialIntegrityException(
                        "Cannot remove model because it is referenced by other model(s)",
                        modelResource.getReferencedBy());
            }
            Item item = session.getItem(modelId.getFullPath());
            item.remove();
            session.save();
        } catch (RepositoryException e) {
            throw new FatalModelRepositoryException("Problem occured removing the model", e);
        }
    }

    @PostConstruct
    public void createValidators() {
        try {
            this.tmpDirectory = File.createTempFile("vorto", null).getParentFile();
        } catch (IOException e) {
            throw new RuntimeException("Could not initialize tmp directory");
        }
        this.validators.add(new DuplicateModelValidation(this));
        this.validators.add(new ModelReferencesValidation(this));
    }

    public void setSession(Session session) {
        this.session = session;
    }

    @Override
    public List<ModelResource> getMappingModelsForTargetPlatform(ModelId modelId, String targetPlatform) {
        List<ModelResource> mappingResources = new ArrayList<>();
        ModelResource modelResource = getById(modelId);
        if (modelResource != null) {
            for (ModelId referenceeModelId : modelResource.getReferencedBy()) {
                ModelResource referenceeModelResources = getById(referenceeModelId);
                if (referenceeModelResources.getModelType() == ModelType.Mapping
                        && isTargetPlatformMapping(referenceeModelResources, targetPlatform)) {
                    mappingResources.add(referenceeModelResources);
                }
            }
            for (ModelId referencedModelId : modelResource.getReferences()) {
                mappingResources.addAll(getMappingModelsForTargetPlatform(referencedModelId, targetPlatform));
            }
        }
        return mappingResources;
    }

    private boolean isTargetPlatformMapping(ModelResource model, String targetPlatform) {
        try {
            ModelEMFResource emfResource = getEMFResource(model.getId());
            return emfResource.matchesTargetPlatform(targetPlatform);
        } catch (Exception e) {
            throw new FatalModelRepositoryException("Something went wrong accessing the repository", e);
        }
    }

    private ModelEMFResource getEMFResource(ModelId modelId) {
        try {
            Node folderNode = session.getNode(modelId.getFullPath());
            Node fileNode = (Node) folderNode.getNodes().next();
            Node fileItem = (Node) fileNode.getPrimaryItem();
            InputStream is = fileItem.getProperty("jcr:data").getBinary().getStream();
            return (ModelEMFResource) ModelParserFactory.getParser(fileNode.getName()).parse(is);
        } catch (Exception e) {
            throw new FatalModelRepositoryException("Something went wrong accessing the repository", e);
        }
    }

    @Override
    public void addModelImage(ModelId modelId, byte[] imageContent) {
        try {
            Node modelFolderNode = session.getNode(modelId.getFullPath());
            Node contentNode = null;
            if (modelFolderNode.hasNode("img.png")) {
                Node imageNode = (Node) modelFolderNode.getNode("img.png");
                contentNode = (Node) imageNode.getPrimaryItem();
            } else {
                Node imageNode = modelFolderNode.addNode("img.png", "nt:file");
                contentNode = imageNode.addNode("jcr:content", "nt:resource");
            }

            Binary binary = session.getValueFactory().createBinary(new ByteArrayInputStream(imageContent));
            contentNode.setProperty("jcr:data", binary);
            session.save();
        } catch (PathNotFoundException e) {
            throw new ModelNotFoundException("Problem when trying to add image to model", e);
        } catch (RepositoryException e) {
            throw new FatalModelRepositoryException("Something severe went wrong when accessing the repository", e);
        }
    }

    @Override
    public byte[] getModelImage(ModelId modelId) {
        try {
            Node folderNode = session.getNode(modelId.getFullPath());
            if (folderNode.hasNode("img.png")) {
                Node imageNode = folderNode.getNode("img.png");
                Node fileItem = (Node) imageNode.getPrimaryItem();
                InputStream is = fileItem.getProperty("jcr:data").getBinary().getStream();
                return IOUtils.toByteArray(is);
            }
        } catch (PathNotFoundException e) {
            throw new ModelNotFoundException("Problem when trying to retrieve image for model", e);
        } catch (RepositoryException e) {
            throw new FatalModelRepositoryException("Something severe went wrong when accessing the repository", e);
        } catch (IOException e) {
            throw new FatalModelRepositoryException("Something severe went wrong when trying to read image content",
                    e);
        }
        return null;
    }
}