Java tutorial
/** * Copyright (C) 2010-2018 Structr GmbH * * This file is part of Structr <http://structr.org>. * * Structr is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * Structr 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Structr. If not, see <http://www.gnu.org/licenses/>. */ package org.structr.web.maintenance.deploy; import java.io.FileInputStream; import java.io.IOException; import java.nio.file.FileVisitResult; import java.nio.file.FileVisitor; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.structr.common.SecurityContext; import org.structr.common.error.FrameworkException; import org.structr.core.app.App; import org.structr.core.app.StructrApp; import org.structr.core.entity.AbstractNode; import org.structr.core.entity.Relation; import org.structr.core.graph.NodeInterface; import static org.structr.core.graph.NodeInterface.name; import org.structr.core.graph.Tx; import org.structr.core.property.GenericProperty; import org.structr.core.property.PropertyKey; import org.structr.core.property.PropertyMap; import org.structr.web.common.FileHelper; import org.structr.web.common.ImageHelper; import org.structr.web.entity.AbstractFile; import org.structr.web.entity.AbstractMinifiedFile; import org.structr.web.entity.File; import org.structr.web.entity.Folder; import org.structr.web.entity.Image; /** * */ public class FileImportVisitor implements FileVisitor<Path> { private static final Logger logger = LoggerFactory.getLogger(FileImportVisitor.class.getName()); private Map<String, Object> config = null; private SecurityContext securityContext = null; private Path basePath = null; private App app = null; private List<File> deferredFiles = null; private Map<String, Folder> folderCache = null; public FileImportVisitor(final Path basePath, final Map<String, Object> config) { this.securityContext = SecurityContext.getSuperUserInstance(); this.securityContext.setDoTransactionNotifications(false); this.basePath = basePath; this.config = config; this.app = StructrApp.getInstance(this.securityContext); this.deferredFiles = new ArrayList<>(); this.folderCache = new HashMap<>(); } @Override public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) throws IOException { if (!basePath.equals(dir)) { createFolder(dir); } return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException { if (attrs.isRegularFile()) { createFile(file, file.getFileName().toString()); } return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFileFailed(final Path file, final IOException exc) throws IOException { logger.warn("Exception while importing file {}: {}", new Object[] { file.toString(), exc.getMessage() }); return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException { return FileVisitResult.CONTINUE; } public void handleDeferredFiles() { final Class<Relation> relType = StructrApp.getConfiguration() .getRelationshipEntityClass("AbstractMinifiedFileMINIFICATIONFile"); final PropertyKey<Integer> positionKey = StructrApp.key(relType, "position"); if (!this.deferredFiles.isEmpty()) { for (File file : this.deferredFiles) { try (final Tx tx = app.tx(true, false, false)) { // set properties from files.json final PropertyMap fileProperties = getPropertiesForFileOrFolder(file.getPath()); final PropertyKey<Map<String, String>> sourcesPropertyKey = new GenericProperty( "minificationSources"); Map<String, String> sourcesConfig = fileProperties.get(sourcesPropertyKey); fileProperties.remove(sourcesPropertyKey); file.unlockSystemPropertiesOnce(); file.setProperties(securityContext, fileProperties); for (String positionString : sourcesConfig.keySet()) { final Integer position = Integer.parseInt(positionString); final String sourcePath = sourcesConfig.get(positionString); final AbstractFile source = FileHelper.getFileByAbsolutePath(securityContext, sourcePath); if (source != null) { app.create(app.get(AbstractMinifiedFile.class, file.getUuid()), (File) source, relType, new PropertyMap(positionKey, position)); } else { logger.warn( "Source file {} for minified file {} at position {} not found - please verify that it is included in the export", sourcePath, file.getPath(), positionString); } } tx.success(); } catch (FrameworkException fxe) { } } } } // ----- private methods ----- private Folder getExistingFolder(final String path) throws FrameworkException { if (this.folderCache.containsKey(path)) { return this.folderCache.get(path); } else { final Folder existingFolder = app.nodeQuery(Folder.class) .and(StructrApp.key(AbstractFile.class, "path"), path).getFirst(); if (existingFolder != null) { this.folderCache.put(path, existingFolder); } return existingFolder; } } private void createFolder(final Path folderObj) { final String folderPath = harmonizeFileSeparators("/", basePath.relativize(folderObj).toString()); try (final Tx tx = app.tx(true, false, false)) { if (getExistingFolder(folderPath) == null) { final PropertyMap folderProperties = new PropertyMap(AbstractNode.name, folderObj.getFileName().toString()); if (!basePath.equals(folderObj.getParent())) { final String parentPath = harmonizeFileSeparators("/", basePath.relativize(folderObj.getParent()).toString()); folderProperties.put(StructrApp.key(Folder.class, "parent"), getExistingFolder(parentPath)); } // set properties from files.json final PropertyMap properties = getPropertiesForFileOrFolder(folderPath); if (properties != null) { folderProperties.putAll(properties); } final Folder newFolder = app.create(Folder.class, folderProperties); this.folderCache.put(folderPath, newFolder); } tx.success(); } catch (Exception ex) { logger.error("Error occured while importing folder " + folderObj, ex); } } private void createFile(final Path path, final String fileName) throws IOException { String newFileUuid = null; try (final Tx tx = app.tx(true, false, false)) { final String fullPath = harmonizeFileSeparators("/", basePath.relativize(path).toString()); final PropertyMap fileProperties = getPropertiesForFileOrFolder(fullPath); if (fileProperties == null) { if (!fileName.startsWith(".")) { logger.info("Ignoring {} (not in files.json)", fullPath); } } else { Folder parent = null; if (!basePath.equals(path.getParent())) { final String parentPath = harmonizeFileSeparators("/", basePath.relativize(path.getParent()).toString()); parent = getExistingFolder(parentPath); } boolean skipFile = false; File file = app.nodeQuery(File.class).and(StructrApp.key(File.class, "parent"), parent) .and(File.name, fileName).getFirst(); if (file != null) { final Long checksumOfExistingFile = file.getChecksum(); final Long checksumOfNewFile = FileHelper.getChecksum(path.toFile()); if (checksumOfExistingFile != null && checksumOfNewFile != null && checksumOfExistingFile.equals(checksumOfNewFile)) { skipFile = true; } else { // remove existing file first! app.delete(file); } } if (!skipFile) { logger.info("Importing {}...", fullPath); try (final FileInputStream fis = new FileInputStream(path.toFile())) { // create file in folder structure file = FileHelper.createFile(securityContext, fis, null, File.class, fileName, parent); final String contentType = file.getContentType(); // modify file type according to content if (StringUtils.startsWith(contentType, "image") || ImageHelper.isImageType(file.getProperty(name))) { file.unlockSystemPropertiesOnce(); file.setProperties(securityContext, new PropertyMap(NodeInterface.type, Image.class.getSimpleName())); } newFileUuid = file.getUuid(); } } if (file != null) { if (fileProperties .containsKey(StructrApp.key(AbstractMinifiedFile.class, "minificationSources"))) { deferredFiles.add(file); } else { file.unlockSystemPropertiesOnce(); file.setProperties(securityContext, fileProperties); } } if (newFileUuid != null) { final File createdFile = app.get(File.class, newFileUuid); String type = createdFile.getType(); boolean isImage = createdFile instanceof Image; logger.debug("File {}: {}, isImage? {}", new Object[] { createdFile.getName(), type, isImage }); if (isImage) { try { ImageHelper.updateMetadata(createdFile); handleThumbnails((Image) createdFile); } catch (Throwable t) { logger.warn("Unable to update metadata: {}", t.getMessage()); } } } } tx.success(); } catch (FrameworkException ex) { logger.error("Error occured while reading file properties " + fileName, ex); } } private void handleThumbnails(final Image img) { final Class<Relation> thumbnailRel = StructrApp.getConfiguration() .getRelationshipEntityClass("ImageTHUMBNAILImage"); if (img.getProperty(StructrApp.key(Image.class, "isThumbnail"))) { // thumbnail image if (img.getIncomingRelationship(thumbnailRel) == null) { ImageHelper.findAndReconnectOriginalImage(img); } } else { // original image if (!img.getOutgoingRelationships(thumbnailRel).iterator().hasNext()) { ImageHelper.findAndReconnectThumbnails(img); } } } private PropertyMap getPropertiesForFileOrFolder(final String path) throws FrameworkException { final Object data = config.get(path); if (data != null && data instanceof Map) { return getPropertiesForFileOrFolder((Map<String, Object>) data); } return null; } private PropertyMap getPropertiesForFileOrFolder(final Map<String, Object> data) throws FrameworkException { return PropertyMap.inputTypeToJavaType(SecurityContext.getSuperUserInstance(), StructrApp.getConfiguration().getNodeEntityClass((String) data.get("type")), data); } private String harmonizeFileSeparators(final String... sources) { final StringBuilder buf = new StringBuilder(); for (final String src : sources) { buf.append(src); } int pos = buf.indexOf("\\"); while (pos >= 0) { buf.replace(pos, pos + 1, "/"); pos = buf.indexOf("\\"); } return buf.toString(); } }