/* * Copyright 2017 Red Hat, Inc. and/or its affiliates. * * 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 org.kie.workbench.common.stunner.backend.service; import java.io.File; import java.io.FilenameFilter; import java.io.InputStream; import java.net.URI; import java.util.Collection; import java.util.HashMap; import javax.annotation.PostConstruct; import javax.enterprise.inject.Instance; import javax.inject.Inject; import javax.inject.Named; import javax.servlet.ServletContext; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.jboss.errai.bus.server.annotations.Service; import org.jboss.errai.bus.server.api.RpcContext; import org.kie.workbench.common.stunner.core.api.DefinitionManager; import org.kie.workbench.common.stunner.core.api.FactoryManager; import org.kie.workbench.common.stunner.core.backend.service.AbstractVFSDiagramService; import org.kie.workbench.common.stunner.core.definition.service.DefinitionSetService; import org.kie.workbench.common.stunner.core.diagram.Diagram; import org.kie.workbench.common.stunner.core.diagram.Metadata; import org.kie.workbench.common.stunner.core.diagram.MetadataImpl; import org.kie.workbench.common.stunner.core.graph.Graph; import org.kie.workbench.common.stunner.core.registry.BackendRegistryFactory; import org.kie.workbench.common.stunner.core.service.DiagramService; import org.kie.workbench.common.stunner.core.util.UUID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.uberfire.backend.server.util.Paths; import org.uberfire.backend.vfs.Path; import org.uberfire.io.IOService; import org.uberfire.java.nio.file.FileSystem; import org.uberfire.java.nio.file.FileSystemAlreadyExistsException; import org.uberfire.java.nio.file.StandardDeleteOption; @Service public class DiagramServiceImpl extends AbstractVFSDiagramService<Metadata, Diagram<Graph, Metadata>> implements DiagramService { private static final Logger LOG = LoggerFactory.getLogger(DiagramServiceImpl.class.getName()); private static final String METADATA_EXTENSION = "meta"; private static final String VFS_ROOT_PATH = "default://stunner"; private static final String VFS_DIAGRAMS_PATH = "diagrams"; private static final String APP_DIAGRAMS_PATH = "WEB-INF/diagrams"; private FileSystem fileSystem; private org.uberfire.java.nio.file.Path root; protected DiagramServiceImpl() { this(null, null, null, null, null); } @Inject public DiagramServiceImpl(final DefinitionManager definitionManager, final FactoryManager factoryManager, final Instance<DefinitionSetService> definitionSetServiceInstances, final @Named("ioStrategy") IOService ioService, final BackendRegistryFactory registryFactory) { super(definitionManager, factoryManager, definitionSetServiceInstances, ioService, registryFactory); } @PostConstruct public void init() { // Initialize caches. super.initialize(); // Initialize the application's VFS. initFileSystem(); // Register packaged diagrams into VFS. registerAppDefinitions(); // Load vfs diagrams and put into the parent registry. final Collection<Diagram<Graph, Metadata>> diagrams = getAllDiagrams(); if (null != diagrams) { diagrams.forEach(diagram -> getRegistry().register(diagram)); } } @Override public Path create(final Path path, final String name, final String defSetId) { return super.create(path, name, defSetId, buildMetadataInstance(path, defSetId, name)); } @Override protected Metadata buildMetadataInstance(final org.uberfire.backend.vfs.Path path, String defSetId, String title) { return new MetadataImpl.MetadataImplBuilder(defSetId, getDefinitionManager()) .setPath(path) .setTitle(title) .build(); } @Override protected InputStream loadMetadataForPath(final Path path) { return doLoadMetadataStreamByDiagramPath(path); } @Override protected Class<? extends Metadata> getMetadataType() { return Metadata.class; } @Override protected Metadata doSave(final Diagram diagram, final String raw, final String metadata) { try { getIoService().startBatch(fileSystem); final Path _path = diagram.getMetadata().getPath(); final String name = null != _path ? _path.getFileName() : getNewFileName(diagram); final org.uberfire.java.nio.file.Path path = null != _path ? Paths.convert(_path) : getDiagramsPath().resolve(name); // Serialize the diagram's raw data. LOG.debug("Serializing raw data: " + raw); getIoService().write(path, raw); final String metadataFileName = getMetadataFileName(name); final org.uberfire.java.nio.file.Path metadataPath = getDiagramsPath().resolve(metadataFileName); LOG.debug("Serializing raw metadadata: " + metadata); getIoService().write(metadataPath, metadata); diagram.getMetadata().setPath(Paths.convert(path)); } catch (Exception e) { LOG.error("Error serializing diagram with UUID [" + diagram.getName() + "].", e); } finally { getIoService().endBatch(); } return diagram.getMetadata(); } private String getNewFileName(final Diagram diagram) { final String defSetId = diagram.getMetadata().getDefinitionSetId(); final DefinitionSetService defSetService = getServiceById(defSetId); return UUID.uuid(8) + "." + defSetService.getResourceType().getSuffix(); } @Override protected boolean doDelete(final Path _path) { final org.uberfire.java.nio.file.Path path = Paths.convert(_path); if (getIoService().exists(path)) { getIoService().startBatch(fileSystem); try { getIoService().deleteIfExists(path, StandardDeleteOption.NON_EMPTY_DIRECTORIES); } catch (Exception e) { LOG.error("Error deleting diagram for path [" + path + "].", e); return false; } finally { getIoService().endBatch(); } } return true; } private InputStream doLoadMetadataStreamByDiagramPath(final Path dPath) { org.uberfire.java.nio.file.Path path = getDiagramsPath().resolve(getMetadataFileName(dPath.getFileName())); if (null != path) { try { return loadPath(path); } catch (Exception e) { LOG.warn("Cannot load metadata for [" + dPath.toString() + "].", e); } } return null; } private String getMetadataFileName(final String uri) { return uri + "." + METADATA_EXTENSION; } private Collection<Diagram<Graph, Metadata>> getAllDiagrams() { return getDiagramsByPath(root); } private void registerAppDefinitions() { deployAppDiagrams(APP_DIAGRAMS_PATH); } private void initFileSystem() { try { fileSystem = getIoService().newFileSystem(URI.create(VFS_ROOT_PATH), new HashMap<String, Object>() {{ put("init", Boolean.TRUE); put("internal", Boolean.TRUE); }}); } catch (FileSystemAlreadyExistsException e) { fileSystem = getIoService().getFileSystem(URI.create(VFS_ROOT_PATH)); } this.root = fileSystem.getRootDirectories().iterator().next(); } private void deployAppDiagrams(final String path) { ServletContext servletContext = RpcContext.getServletRequest().getServletContext(); if (null != servletContext) { String dir = servletContext.getRealPath(path); if (dir != null && new File(dir).exists()) { dir = dir.replaceAll("\\\\", "/"); findAndDeployDiagrams(dir); } } else { LOG.warn("No servlet context available. Cannot deploy the application diagrams."); } } private void findAndDeployDiagrams(final String directory) { if (!StringUtils.isBlank(directory)) { // Look for data sets deploy File[] files = new File(directory).listFiles(_deployFilter); if (files != null) { for (File f : files) { try { String name = f.getName(); if (isFileNameAccepted(name)) { // Register it into VFS storage. registerIntoVFS(f); } } catch (Exception e) { LOG.error("Error loading the application default diagrams.", e); } } } } } private void registerIntoVFS(final File file) { String name = file.getName(); org.uberfire.java.nio.file.Path actualPath = getDiagramsPath().resolve(name); boolean exists = getIoService().exists(actualPath); if (!exists) { getIoService().startBatch(fileSystem); try { String content = FileUtils.readFileToString(file); org.uberfire.java.nio.file.Path diagramPath = getDiagramsPath().resolve(file.getName()); getIoService().write(diagramPath, content); } catch (Exception e) { LOG.error("Error registering diagram into app's VFS", e); } finally { getIoService().endBatch(); } } else { LOG.warn("Diagram [" + name + "] already exists on VFS storage. This file should not be longer present here."); } } public org.uberfire.java.nio.file.Path getDiagramsPath() { return root.resolve(VFS_DIAGRAMS_PATH); } private boolean isFileNameAccepted(final String name) { if (name != null && name.trim().length() > 0) { return getExtensionsAccepted().stream().anyMatch(s -> name.endsWith("." + s)) || isMetadataFile(name); } return false; } private boolean isMetadataFile(final String name) { return null != name && name.endsWith("." + METADATA_EXTENSION); } private FilenameFilter _deployFilter = (dir, name) -> true; }