/* * Copyright (C) 2003-2017 eXo Platform SAS. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.exoplatform.management.common.importop; import org.exoplatform.commons.utils.ActivityTypeUtils; import org.exoplatform.management.common.FileEntry; import org.exoplatform.management.common.exportop.JCRNodeExportTask; import org.exoplatform.services.cache.CacheService; import org.exoplatform.services.cache.ExoCache; import org.exoplatform.services.ecm.publication.PublicationService; import org.exoplatform.services.wcm.publication.WCMPublicationService; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import javax.jcr.ImportUUIDBehavior; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.Value; import javax.jcr.nodetype.NodeDefinition; import javax.jcr.nodetype.NodeType; import javax.jcr.query.Query; import javax.jcr.query.QueryManager; /** * The Class AbstractJCRImportOperationHandler. */ public abstract class AbstractJCRImportOperationHandler extends AbstractImportOperationHandler { /** The publication service. */ protected PublicationService publicationService; /** The wcm publication service. */ protected WCMPublicationService wcmPublicationService; /** The is NT recursive map. */ private Map<String, Boolean> isNTRecursiveMap = new HashMap<String, Boolean>(); /** * Import node. * * @param fileEntry the file entry * @param workspace the workspace * @param isCleanPublication the is clean publication * @return true, if successful * @throws Exception the exception */ protected final boolean importNode(FileEntry fileEntry, String workspace, boolean isCleanPublication) throws Exception { File xmlFile = fileEntry.getFile(); if (xmlFile == null || !xmlFile.exists()) { log.warn("Cannot import file" + xmlFile); return false; } FileInputStream fis = new FileInputStream(xmlFile); try { return importNode(fileEntry.getNodePath(), workspace, fis, fileEntry.getHistoryFile(), isCleanPublication); } finally { if (fis != null) { fis.close(); } if (xmlFile != null) { xmlFile.delete(); } } } /** * Import node. * * @param nodePath the node path * @param workspace the workspace * @param inputStream the input stream * @param historyFile the history file * @param isCleanPublication the is clean publication * @return true, if successful * @throws Exception the exception */ protected final boolean importNode(String nodePath, String workspace, InputStream inputStream, File historyFile, boolean isCleanPublication) throws Exception { String parentNodePath = nodePath.substring(0, nodePath.lastIndexOf("/")); parentNodePath = parentNodePath.replaceAll("//", "/"); // Delete old node Session session = getSession(workspace); try { if (session.itemExists(nodePath) && session.getItem(nodePath) instanceof Node) { log.info("Deleting the node " + workspace + ":" + nodePath); Node oldNode = (Node) session.getItem(nodePath); if (oldNode.isNodeType("exo:activityInfo") && activityManager != null) { String activityId = ActivityTypeUtils.getActivityId(oldNode); deleteActivity(activityId); } remove(oldNode, session); session.save(); session.refresh(false); } } catch (Exception e) { log.error("Error when trying to find and delete the node: '" + nodePath + "'. Ignore this node and continue.", e); return false; } finally { if (session != null) { session.logout(); } } // Import Node from Extracted Zip file session = getSession(workspace); FileInputStream historyFis1 = null, historyFis2 = null; try { log.info("Importing the node '" + nodePath + "'"); // Create the parent path Node currentNode = createJCRPath(session, parentNodePath); if (parentNodePath.isEmpty()) { parentNodePath = "/"; } session.refresh(false); session.importXML(parentNodePath, inputStream, ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW); session.save(); if (isCleanPublication) { // Clean publication information cleanPublication(parentNodePath, session); } else if (historyFile != null) { log.info("Importing history of the node " + nodePath); historyFis1 = new FileInputStream(historyFile); Map<String, String> mapHistoryValue = org.exoplatform.services.cms.impl.Utils.getMapImportHistory(historyFis1); historyFis2 = new FileInputStream(historyFile); org.exoplatform.services.cms.impl.Utils.processImportHistory(currentNode, historyFis2, mapHistoryValue); } return true; } catch (Exception e) { log.error("Error when trying to import node: " + nodePath, e); // Revert changes session.refresh(false); return false; } finally { if (session != null) { session.logout(); } if (historyFis1 != null) { historyFis1.close(); } if (historyFis2 != null) { historyFis2.close(); } } } /** * Removes the. * * @param node the node * @param session the session * @throws Exception the exception */ private void remove(Node node, Session session) throws Exception { if (node.hasNodes() && !isRecursiveDelete(node)) { NodeIterator subnodes = node.getNodes(); while (subnodes.hasNext()) { Node subNode = subnodes.nextNode(); remove(subNode, session); } } log.info("Delete sub node" + node.getPath()); node.remove(); session.save(); } /** * Checks if is recursive delete. * * @param node the node * @return true, if is recursive delete * @throws Exception the exception */ protected final boolean isRecursiveDelete(Node node) throws Exception { NodeType nodeType = node.getPrimaryNodeType(); NodeType[] nodeTypes = node.getMixinNodeTypes(); boolean recursive = isRecursiveNT(nodeType); if (!recursive && nodeTypes != null && nodeTypes.length > 0) { int i = 0; while (!recursive && i < nodeTypes.length) { recursive = isRecursiveNT(nodeTypes[i]); i++; } } return recursive; } /** * Checks if is recursive NT. * * @param nodeType the node type * @return true, if is recursive NT * @throws Exception the exception */ protected final boolean isRecursiveNT(NodeType nodeType) throws Exception { if (nodeType.getName().equals("exo:actionStorage")) { return true; } if (!isNTRecursiveMap.containsKey(nodeType.getName())) { boolean hasMandatoryChild = false; NodeDefinition[] nodeDefinitions = nodeType.getChildNodeDefinitions(); if (nodeDefinitions != null) { int i = 0; while (!hasMandatoryChild && i < nodeDefinitions.length) { hasMandatoryChild = nodeDefinitions[i].isMandatory(); i++; } } boolean recursive = hasMandatoryChild; isNTRecursiveMap.put(nodeType.getName(), recursive); } return isNTRecursiveMap.get(nodeType.getName()); } /** * Clean publication. * * @param parentPath the parent path * @param session the session * @throws Exception the exception */ protected final void cleanPublication(String parentPath, Session session) throws Exception { QueryManager manager = session.getWorkspace().getQueryManager(); String statement = "select * from nt:base where jcr:path LIKE '" + parentPath + "/%' and publication:liveRevision IS NOT NULL"; Query query = manager.createQuery(statement.toString(), Query.SQL); NodeIterator iter = query.execute().getNodes(); while (iter.hasNext()) { Node node = iter.nextNode(); cleanPublication(node); } if (session.itemExists(parentPath)) { Node node = (Node) session.getItem(parentPath); cleanPublication(node); } } /** * Clean publication. * * @param node the node * @throws Exception the exception */ protected final void cleanPublication(Node node) throws Exception { if (node.hasProperty("publication:currentState")) { log.info("\"" + node.getName() + "\" publication lifecycle has been cleaned up"); // See in case the content is enrolled for the first time but never // published in "source server", if yes, set manually "published" state if (node.hasProperty("publication:revisionData")) { Value[] values = node.getProperty("publication:revisionData").getValues(); if (values.length < 2) { String user = node.hasProperty("publication:lastUser") ? node.getProperty("publication:lastUser").getString() : null; node.setProperty("publication:revisionData", new String[] { node.getUUID() + ",published," + user }); } } node.setProperty("publication:liveRevision", ""); node.setProperty("publication:currentState", "published"); node.getSession().save(); if (publicationService.isNodeEnrolledInLifecycle(node)) publicationService.unsubcribeLifecycle(node); wcmPublicationService.updateLifecyleOnChangeContent(node, "default", "__system", "published"); node.save(); } } /** * Creates the JCR path. * * @param session the session * @param path the path * @return the node * @throws RepositoryException the repository exception */ protected final Node createJCRPath(Session session, String path) throws RepositoryException { String[] ancestors = path.split("/"); Node current = session.getRootNode(); for (int i = 0; i < ancestors.length; i++) { if (!"".equals(ancestors[i])) { if (current.hasNode(ancestors[i])) { current = current.getNode(ancestors[i]); } else { if (log.isInfoEnabled()) { log.info("Creating folder: " + ancestors[i] + " in node : " + current.getPath()); } current = current.addNode(ancestors[i], "nt:unstructured"); session.save(); } } } return current; } /** * Gets the node path. * * @param filePath the file path * @return the node path */ public String getNodePath(String filePath) { String[] fileParts = filePath.split(JCRNodeExportTask.JCR_DATA_SEPARATOR); if (fileParts.length != 2) { log.error("Cannot parse file: " + filePath); return null; } String nodePath = fileParts[1].trim().replaceFirst(".xml$", ""); if (!nodePath.startsWith("/")) { nodePath = "/" + nodePath; } return nodePath; } /** * Clear caches. * * @param cacheService the cache service * @param namePattern the name pattern */ @SuppressWarnings("rawtypes") public void clearCaches(CacheService cacheService, String namePattern) { for (Object o : cacheService.getAllCacheInstances()) { try { ExoCache exoCache = (ExoCache) o; if(exoCache.getName().contains(namePattern)) { exoCache.clearCache(); } } catch (Exception e) { if (log.isTraceEnabled()) { log.trace("An exception occurred: " + e.getMessage()); } } } } }