/* =============================================================================== * * Part of the InfoGlue Content Management Platform (www.infoglue.org) * * =============================================================================== * * Copyright (C) * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 2, as published by the * Free Software Foundation. See the file LICENSE.html for more information. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY, including the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc. / 59 Temple * Place, Suite 330 / Boston, MA 02111-1307 / USA. * * =============================================================================== */ package org.infoglue.cms.controllers.kernel.impl.simple; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import org.apache.log4j.Logger; import org.exolab.castor.jdo.Database; import org.infoglue.cms.entities.content.ContentVO; import org.infoglue.cms.entities.content.ContentVersion; import org.infoglue.cms.entities.content.ContentVersionVO; import org.infoglue.cms.entities.content.DigitalAssetVO; import org.infoglue.cms.entities.kernel.BaseEntityVO; import org.infoglue.cms.entities.management.LanguageVO; import org.infoglue.cms.security.InfoGluePrincipal; /** * This controller is made for cleaning old versions. */ @SuppressWarnings({"unused", "unchecked", "static-access"}) public class ContentCleanerController extends BaseController { private final static Logger logger = Logger.getLogger(ContentCleanerController.class); private static final LanguageController languageController = LanguageController.getController(); private static final ContentController contentController = ContentController.getContentController(); private static final ContentVersionController contentVersionController = ContentVersionController.getContentVersionController(); private static final DigitalAssetController digitalAssetController = DigitalAssetController.getController(); public static final int FACTOR_KB = 1024, FACTOR_MB = 1048576, FACTOR_GB = 1073741824; private float recoveredDiskSpaceCnt = 0.0f; private Long elapsedTime = 0l; private Integer deletedContentVersionsCnt = 0, deletedDigitalAssetsCnt = 0; public static ContentCleanerController getContentCleanerController() { return new ContentCleanerController(); } private ContentCleanerController() {} /** * This method will clean all content versions foreach language foreach content that exists in the db. * Except thoose content versions that are supposed to be retianed specified by hitSize. * @param hitSize2Retain Is used look up the last active content versions by language * for the given number, and also the last published if that one wasnt included in * the resulting query from the database. * @throws Exception */ public void cleanSweep(final int hitSize2Retain, InfoGluePrincipal principal) throws Exception { try { final Database db = CastorDatabaseService.getDatabase(); beginTransaction(db); final List<LanguageVO> languageVOList = languageController.getLanguageVOList(db); final List<ContentVO> contentVOList = contentController.getContentVOList(); commitTransaction(db); for (final ContentVO contentVO : contentVOList) { clean(contentVO, hitSize2Retain, languageVOList, principal); } } catch(Exception e) { e.printStackTrace(); //logger.error(e); } } /** * This method will clean a single content up on it's content versions for the specified content id. * @param contentId The content to perform a clean up on. * @param hitSize2Retain The number of content versions to retain. * @throws Exception */ public void clean(final Integer contentId, final int hitSize2Retain, InfoGluePrincipal principal) throws Exception { final Database db = CastorDatabaseService.getDatabase(); beginTransaction(db); final ContentVO contentVO = contentController.getContentVOWithId(contentId); final List<LanguageVO> languageVOList = languageController.getLanguageVOList(db); commitTransaction(db); clean(contentVO, hitSize2Retain, languageVOList, principal); } /** * This method will clean a single content up on it's content versions for the specified content id * and for the specified languages. * @param contentId The content to perform a clean up on. * @param hitSize2Retain The number of content versions to retain. * @param languageVOList The specified languages to clean this content for. * @throws Exception */ public void clean(final Integer contentId, final int hitSize2Retain, final List<LanguageVO> languageVOList, InfoGluePrincipal principal) throws Exception { final Database db = CastorDatabaseService.getDatabase(); beginTransaction(db); final ContentVO contentVO = contentController.getContentVOWithId(contentId); commitTransaction(db); clean(contentVO, hitSize2Retain, languageVOList, principal); } /** * This method will clean a single content up on it's content versions for the specified ContentVO object * and for the specified languages. If the ContentVO object is a branch this method will perform a recursively * clean up on all child Contents and their content versions. * @param contentVO The content to perform a clean up on. * @param hitSize2Retain The number of content versions to retain. * @param languageVOList The specified languages to clean this content for. * @throws Exception */ public void clean(final ContentVO contentVO, final int hitSize2Retain, final List<LanguageVO> languageVOList, InfoGluePrincipal principal) throws Exception { // Recursive clean for branches if (contentVO.getIsBranch()) { final List<ContentVO> childs = contentController.getContentChildrenVOList(contentVO.getContentId(), null, false); for (final ContentVO child : childs) { clean(child, hitSize2Retain, languageVOList, principal); } } // Start cleaning content for (final LanguageVO languageVO : languageVOList) { clean(contentVO, hitSize2Retain, languageVO, principal); } } /** * This method is called internally by either of the clean methods above. * This method is responsible for collecting content versions up on a content * and validateing wheter or not a clean is neccessary. * @param contentVO The content to perform a clean up on. * @param hitSize2Retain The number of content versions to retain. * @param languageVOList The specified languages to clean this content for. * @throws Exception */ private void clean(final ContentVO contentVO, final int hitSize2Retain, final LanguageVO languageVO, InfoGluePrincipal principal) throws Exception { final List<ContentVersionVO> contentVersionsList2Retain = collectContentVersionsList2Retain(contentVO, languageVO, hitSize2Retain); // Do this only when we have a sufficient number of content versions, if less there is no need to do this. // The list of content versions can hold both the <hitSize> quantity and one copy of the latest published content version, // this happens if none of the content versions found was the last published. if (contentVersionsList2Retain.size() >= hitSize2Retain) { cleanContent(contentVO, languageVO, contentVersionsList2Retain, principal); } } /** * This is the method where the actually cleaning process occurrs. * This method is called internally from the private clean method above. * @param contentVO The content to perform a clean up on. * @param languageVO The content language to collect content versions up on. * @param contentVersionsList2Retain The list of content versions to retain. * @throws Exception */ private void cleanContent(final ContentVO contentVO, final LanguageVO languageVO, final List<ContentVersionVO> contentVersionsList2Retain, InfoGluePrincipal principal) throws Exception { final long startTime = System.currentTimeMillis(); final Database db = CastorDatabaseService.getDatabase(); beginTransaction(db); // Retrive all conten verisons for this content final List<ContentVersionVO> contentVerisionList = contentVersionController.getContentVersionsWithParentAndLanguage(contentVO.getContentId(), languageVO.getLanguageId(), db); commitTransaction(db); logger.info(contentVerisionList.size() + " content versions found for contentId " + contentVO.getContentId()); logger.info("I will hunt them down and clean them all."); for (final ContentVersionVO contentVersion : contentVerisionList) { // Should this content version be retained? if (!isRetainedContentVersion(contentVersion, contentVersionsList2Retain)) { final Integer contentVersionId = contentVersion.getContentVersionId(); logger.info("Listing digital assets for content version:" + contentVersionId); // Retrive all digital assets for this content version final List<DigitalAssetVO> items = digitalAssetController.getDigitalAssetVOList(contentVersionId); logger.info(items.size() + " digital assets for content version " + contentVersionId + " found."); for (final DigitalAssetVO digitalAsset : items) { final Integer digitalAssetId = digitalAsset.getDigitalAssetId(); recoveredDiskSpaceCnt += digitalAsset.getAssetFileSize(); logger.info("\tDead Digital Asset: " + digitalAsset.getAssetFileName()); // Delete all digital assets and their references that belongs to this content version contentVersionController.deleteDigitalAssetRelation(contentVersionId, digitalAssetId, principal); digitalAssetController.delete(digitalAssetId); deletedDigitalAssetsCnt += 1; } // Delete the content version as well contentVersionController.forceDelete(contentVersion); deletedContentVersionsCnt += 1; logger.info("ContentVersion: " + contentVersion.getContentVersionId() + " Is Dead Meat."); } } elapsedTime += System.currentTimeMillis() - startTime; } /** * This method checks wheter a content version exist in the retained list. * @param contentVersion the content version to check. * @param contentVerisionList2Retain the lists that holds the retained content versions. * @return Wheterthis is a retained content version or not. */ private boolean isRetainedContentVersion(final ContentVersionVO contentVersion, final List<ContentVersionVO> contentVerisionList2Retain) { for (final ContentVersionVO retainedContentVersion : contentVerisionList2Retain) { if (contentVersion.getContentVersionId().intValue() == retainedContentVersion.getContentVersionId().intValue()) { return true; } } return false; } /** * This method collects the latest active content versions for the specified hitSize * or as many as there is if the number of content versions in the db are insufficient. * It is also responsible of making sure that the latest published content version * is collected and retained. * @param contentVO The content to collect content versions for. * @param languageVO The language up on the contenet versions should be collected for. * @param hitSize The number of content versions to retain. * @return A list containing the latest published content version for this content and * the latest active content versions for the specified hitSize or as many as there is if * the hitsSize number of content versions in the db are insufficient. * @throws Exception */ private List<ContentVersionVO> collectContentVersionsList2Retain(final ContentVO contentVO, final LanguageVO languageVO, final int hitSize) throws Exception { final Integer contentId = contentVO.getContentId(), languageId = languageVO.getLanguageId(); final Database db = CastorDatabaseService.getDatabase(); beginTransaction(db); // Retrive the latest content versions for hitSize final List<ContentVersionVO> contentVersionsList2Retain = contentVersionController.getLatestActiveContentVersionsForHitSize(contentId, languageId, hitSize, db); // If none of them is published, fetch the latest published to this list also if (!hasState(contentVersionsList2Retain, ContentVersionVO.PUBLISHED_STATE)) { final ContentVersion latestPublished = contentVersionController.getLatestPublishedContentVersion(contentId, languageId, db); if (latestPublished != null) { contentVersionsList2Retain.add(latestPublished.getValueObject()); } } commitTransaction(db); return contentVersionsList2Retain; } /** * This method checks if any of the content version in the contentVerisionsList2Retain * has a specific state. * @param contentVerisionsList2Retain The list to perform check against. * @param state The state to look for. * @return Wheter or not the list had a content version in a specific state. */ private boolean hasState(final List<ContentVersionVO> contentVerisionsList2Retain, final Integer state) { for (final ContentVersionVO contentVersion : contentVerisionsList2Retain) { if (contentVersion.getStateId().intValue() == state.intValue()) { return true; } } return false; } /** * @param factor * @return */ public float getCDSFactor(final int factor) { return recoveredDiskSpaceCnt / factor; } /** * @return */ public Integer getDeletedDigitalAsstesCounter() { return deletedDigitalAssetsCnt; } /** * @return */ public Integer getDeletedContentVersionsCounter() { return deletedContentVersionsCnt; } /** * @return */ public Long getElapsedTime() { return elapsedTime; } /* (non-Javadoc) * @see org.infoglue.cms.controllers.kernel.impl.simple.BaseController#getNewVO() */ public BaseEntityVO getNewVO() { return null; } }