/* * (C) Copyright 2015 Netcentric AG. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package biz.netcentric.cq.tools.actool.history.impl; import java.io.ByteArrayInputStream; import java.io.File; import java.util.Map; import java.util.Set; import java.util.TreeSet; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.RepositoryException; import javax.jcr.Session; import org.apache.commons.lang.StringUtils; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Properties; import org.apache.felix.scr.annotations.Property; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.Service; import org.apache.jackrabbit.commons.JcrUtils; import org.apache.sling.commons.osgi.PropertiesUtil; import org.apache.sling.jcr.api.SlingRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.day.cq.commons.jcr.JcrConstants; import biz.netcentric.cq.tools.actool.comparators.TimestampPropertyComparator; import biz.netcentric.cq.tools.actool.helper.Constants; import biz.netcentric.cq.tools.actool.history.AcHistoryService; import biz.netcentric.cq.tools.actool.history.AcInstallationLog; @Service @Component(metatype = true, label = "AC History Service", immediate = true, description = "Service that writes & fetches Ac installation histories") @Properties({ @Property(label = "ACL number of histories to save", name = "AceService.nrOfSavedHistories", value = "5") }) public class AcHistoryServiceImpl implements AcHistoryService { private static final Logger LOG = LoggerFactory.getLogger(AcHistoryServiceImpl.class); public static final String INSTALLED_CONFIGS_NODE_NAME = "installedConfigs"; private static final int NR_OF_HISTORIES_TO_SAVE_DEFAULT = 5; private int nrOfSavedHistories; @Reference private SlingRepository repository; @Activate public void activate(@SuppressWarnings("rawtypes") final Map properties) throws Exception { this.nrOfSavedHistories = PropertiesUtil.toInteger( properties.get("AceService.nrOfSavedHistories"), NR_OF_HISTORIES_TO_SAVE_DEFAULT); } @Override public void persistHistory(AcInstallationLog installLog) { if (nrOfSavedHistories == 0) { installLog.addVerboseMessage(LOG, "History hasn't been persisted, configured number of histories is " + nrOfSavedHistories); return; } Session session = null; try { session = repository.loginService(Constants.USER_AC_SERVICE, null); Node historyNode = HistoryUtils.persistHistory(session, installLog, this.nrOfSavedHistories); String mergedAndProcessedConfig = installLog.getMergedAndProcessedConfig(); if (StringUtils.isNotBlank(mergedAndProcessedConfig)) { JcrUtils.putFile(historyNode, "mergedConfig.yaml", "text/yaml", new ByteArrayInputStream(mergedAndProcessedConfig.getBytes())); } if (installLog.isSuccess()) { persistInstalledConfigurations(session, historyNode, installLog); } session.save(); } catch (RepositoryException e) { LOG.error("RepositoryException: ", e); } finally { if (session != null) { session.logout(); } } } @Override public String[] getInstallationLogPaths() { Session session = null; try { session = repository.loginService(Constants.USER_AC_SERVICE, null); return HistoryUtils.getHistoryInfos(session); } catch (RepositoryException e) { LOG.error("RepositoryException: ", e); } finally { if (session != null) { session.logout(); } } return null; } @Override public String getLogHtml(Session session, String path) { return HistoryUtils.getLogHtml(session, path); } @Override public String getLogTxt(Session session, String path) { return HistoryUtils.getLogTxt(session, path); } @Override public String getLastInstallationHistory() { Session session = null; String history = ""; try { session = repository.loginService(Constants.USER_AC_SERVICE, null); Node statisticsRootNode = HistoryUtils.getAcHistoryRootNode(session); NodeIterator it = statisticsRootNode.getNodes(); if (it.hasNext()) { Node lastHistoryNode = it.nextNode(); if (lastHistoryNode != null) { history = getLogHtml(session, lastHistoryNode.getName()); } } else { history = "no history found!"; } } catch (RepositoryException e) { LOG.error("RepositoryException: ", e); } finally { if (session != null) { session.logout(); } } return history; } private void persistInstalledConfigurations(final Session session, final Node historyNode, AcInstallationLog installLog) { try { Map<String, String> configFileContentsByName = installLog.getConfigFileContentsByName(); if (configFileContentsByName == null) { return; } String commonPrefix = StringUtils.getCommonPrefix(configFileContentsByName.keySet().toArray(new String[configFileContentsByName.size()])); for (String fullConfigFilePath : configFileContentsByName.keySet()) { File targetPathFile = new File( INSTALLED_CONFIGS_NODE_NAME + "/" + StringUtils.substringAfter(fullConfigFilePath, commonPrefix)); File targetPathParentDir = targetPathFile.getParentFile(); Node configFolder = JcrUtils.getOrCreateByPath(historyNode, targetPathParentDir != null ? targetPathParentDir.getPath() : targetPathFile.getPath(), false, JcrConstants.NT_FOLDER, JcrConstants.NT_FOLDER, false); ByteArrayInputStream configFileInputStream = new ByteArrayInputStream( configFileContentsByName.get(fullConfigFilePath).getBytes() ); JcrUtils.putFile(configFolder, targetPathFile.getName(), "text/yaml", configFileInputStream); } installLog.addVerboseMessage(LOG, "Saved installed configuration files under : " + historyNode.getPath() + "/" + INSTALLED_CONFIGS_NODE_NAME); } catch (RepositoryException e) { installLog.addError(LOG, "Exception while saving history node " + historyNode, e); } } public String showHistory(int n) { Session session = null; String history = ""; try { session = repository.loginService(Constants.USER_AC_SERVICE, null); Node statisticsRootNode = HistoryUtils .getAcHistoryRootNode(session); NodeIterator it = statisticsRootNode.getNodes(); int cnt = 1; while (it.hasNext()) { Node historyNode = it.nextNode(); if (historyNode != null && cnt == n) { history = getLogTxt(session, historyNode.getName()); } cnt++; } } catch (RepositoryException e) { LOG.error("RepositoryException: ", e); } finally { if (session != null) { session.logout(); } } return history; } @Override public void persistAcePurgeHistory(AcInstallationLog installLog) { Session session = null; try { session = repository.loginService(Constants.USER_AC_SERVICE, null); Node acHistoryRootNode = HistoryUtils.getAcHistoryRootNode(session); NodeIterator nodeIterator = acHistoryRootNode.getNodes(); Set<Node> historyNodes = new TreeSet<Node>( new TimestampPropertyComparator()); Node newestHistoryNode = null; while (nodeIterator.hasNext()) { historyNodes.add(nodeIterator.nextNode()); } if (!historyNodes.isEmpty()) { newestHistoryNode = historyNodes.iterator().next(); persistPurgeAceHistory(session, installLog, newestHistoryNode); session.save(); } } catch (RepositoryException e) { LOG.error("Exception: ", e); } finally { if (session != null) { session.logout(); } } } private static Node persistPurgeAceHistory(final Session session, AcInstallationLog installLog, final Node historyNode) throws RepositoryException { Node purgeHistoryNode = historyNode.addNode( "purge_" + System.currentTimeMillis(), HistoryUtils.NODETYPE_NT_UNSTRUCTURED); // if there is already a purge history node, order the new one before so // the newest one is always on top NodeIterator nodeIt = historyNode.getNodes(); Node previousPurgeNode = null; while (nodeIt.hasNext()) { Node currNode = nodeIt.nextNode(); // get previous purgeHistory node if (currNode.getName().contains("purge_")) { previousPurgeNode = currNode; break; } } if (previousPurgeNode != null) { historyNode.orderBefore(purgeHistoryNode.getName(), previousPurgeNode.getName()); } installLog.addMessage(LOG, "Saved history in node: " + purgeHistoryNode.getPath()); HistoryUtils.setHistoryNodeProperties(purgeHistoryNode, installLog); return historyNode; } }