/* =============================================================================== * * 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.applications.managementtool; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; 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.applications.common.actions.InfoGlueAbstractAction; import org.infoglue.cms.controllers.kernel.impl.simple.AccessRightController; import org.infoglue.cms.controllers.kernel.impl.simple.AvailableServiceBindingController; import org.infoglue.cms.controllers.kernel.impl.simple.CastorDatabaseService; import org.infoglue.cms.controllers.kernel.impl.simple.ContentController; import org.infoglue.cms.controllers.kernel.impl.simple.DigitalAssetController; import org.infoglue.cms.controllers.kernel.impl.simple.GroupControllerProxy; import org.infoglue.cms.controllers.kernel.impl.simple.LanguageController; import org.infoglue.cms.controllers.kernel.impl.simple.RoleControllerProxy; import org.infoglue.cms.controllers.kernel.impl.simple.SiteNodeController; import org.infoglue.cms.controllers.kernel.impl.simple.SiteNodeVersionController; import org.infoglue.cms.controllers.kernel.impl.simple.UserControllerProxy; import org.infoglue.cms.entities.content.ContentVO; import org.infoglue.cms.entities.content.DigitalAssetVO; import org.infoglue.cms.entities.management.AccessRightGroupVO; import org.infoglue.cms.entities.management.AccessRightRoleVO; import org.infoglue.cms.entities.management.AccessRightUserVO; import org.infoglue.cms.entities.management.AvailableServiceBinding; import org.infoglue.cms.entities.management.AvailableServiceBindingVO; import org.infoglue.cms.entities.management.Language; import org.infoglue.cms.entities.management.LanguageVO; import org.infoglue.cms.entities.management.ValidationItem; import org.infoglue.cms.entities.structure.ServiceBinding; import org.infoglue.cms.entities.structure.SiteNode; import org.infoglue.cms.entities.structure.SiteNodeVersion; import org.infoglue.cms.exception.SystemException; import org.infoglue.cms.security.InfoGlueGroup; import org.infoglue.cms.security.InfoGluePrincipal; import org.infoglue.cms.security.InfoGlueRole; import org.infoglue.cms.util.CmsPropertyHandler; import org.infoglue.deliver.applications.actions.ViewPageAction; import org.infoglue.deliver.util.CacheController; import org.infoglue.deliver.util.HttpHelper; import webwork.action.Action; /** * @author Mattias Bogeblad * * This is a class responsible for doing basic system controls. It will check that the different parts of the system * works as it should after installation. */ public class InstallationValidatorAction extends InfoGlueAbstractAction { public final static Logger logger = Logger.getLogger(InstallationValidatorAction.class.getName()); private Boolean validateDB = new Boolean(true); private Boolean validateFileSystem = new Boolean(true); private Boolean validateSystemSettings = new Boolean(true); private Boolean validateEntities = new Boolean(true); private List validatedItems = new ArrayList(); /** * Just returns the input screen. */ public String doInput() { return Action.INPUT; } /** * This method does all the tests. Here follows a list of what it tries to do: * * 1. Insert a content, content version and a digital asset to the db. * 2. Read all the items created in 1. * 3. Dump the digital asset to file to ensure asset handling. * 4. Delete the items created in 1. * 5. Validate that the correct content types are available as well as other system settings. * 6. Validate all the tables and check so all entities can be written/read/updated/deleted. * 7. Print system settings? * 8. Check that indexes are in place. */ public String doExecute() throws Exception { Database db = CastorDatabaseService.getDatabase(); db.begin(); try { validateDB(db); validateDigitalAssetHandling(db); validateCacheNotification(); db.commit(); } catch(Exception e) { logger.error("An error occurred so we should not complete the transaction:" + e, e); db.rollback(); throw new SystemException(e.getMessage()); } finally { db.close(); } try { validateSiteNodes(); validateAccessRightsUser(); validateAccessRightsRole(); validateAccessRightsGroup(); } catch(Exception e) { logger.error("An error occurred so we should not complete the transaction:" + e, e); throw new SystemException(e.getMessage()); } /* try { validateAuthorization(); } catch(Exception e) { logger.error("An error occurred so we should not complete the transaction:" + e, e); throw new SystemException(e.getMessage()); } */ return Action.SUCCESS; } private void validateDB(Database db) { String name = "Database access"; String description = "We try to create, read and delete a language to validate the database communication and O/R setup."; try { createAndDeleteLanguage(db); addValidationItem(name, description, true, "Test succeeded"); } catch(Exception e) { addValidationItem(name, description, false, "An error occurred: " + e.getMessage()); } } private void validateDigitalAssetHandling(Database db) { String name = "Digital Asset handling"; String description = "We try to upload assets of different sizes and also tries to dump them afterwards to the filesystem."; try { DigitalAssetVO digitalAssetVO = createDigitalAsset(db); db.commit(); db.begin(); dumpDigitalAsset(digitalAssetVO, db); db.commit(); db.begin(); deleteDigitalAsset(digitalAssetVO, db); addValidationItem(name, description, true, "Test succeeded"); } catch(Exception e) { e.printStackTrace(); addValidationItem(name, description, false, "An error occurred: " + e.getMessage()); } } private void validateCacheNotification() { String name = "Cache notifications"; String description = "We try to verify the cache update action on all known servers."; try { List internalDeliveryUrls = CmsPropertyHandler.getInternalDeliveryUrls(); Iterator internalDeliveryUrlsIterator = internalDeliveryUrls.iterator(); while(internalDeliveryUrlsIterator.hasNext()) { String address = "" + internalDeliveryUrlsIterator.next() + "/UpdateCache!test.action"; try { HttpHelper httpHelper = new HttpHelper(); String response = httpHelper.getUrlContent(address, new HashMap(), null, 3000); if(response.indexOf("test ok") == -1) throw new Exception("Got wrong answer"); addValidationItem(name, description, true, "Test succeeded on " + address + ": " + response); } catch(Exception e) { e.printStackTrace(); addValidationItem(name, description, false, "Test failed on " + address + ":" + e.getMessage()); } } List publicDeliveryUrls = CmsPropertyHandler.getPublicDeliveryUrls(); Iterator publicDeliveryUrlsIterator = publicDeliveryUrls.iterator(); while(publicDeliveryUrlsIterator.hasNext()) { String address = "" + publicDeliveryUrlsIterator.next() + "/UpdateCache!test.action"; try { HttpHelper httpHelper = new HttpHelper(); String response = httpHelper.getUrlContent(address, new HashMap(), null, 3000); if(response.indexOf("test ok") == -1) throw new Exception("Got wrong answer"); addValidationItem(name, description, true, "Test succeeded on " + address + ": " + response); } catch(Exception e) { addValidationItem(name, description, false, "Test failed on " + address + ":" + e.getMessage()); } } } catch(Exception e) { addValidationItem(name, description, false, "An error occurred: " + e.getMessage()); } } /** * This method creates and deletes a Language. * * @param db * @throws Exception */ private void createAndDeleteLanguage(Database db) throws Exception { LanguageVO languageVO = new LanguageVO(); languageVO.setCharset("utf8"); languageVO.setLanguageCode("test"); languageVO.setName("test"); LanguageVO newLanguageVO = LanguageController.getController().create(languageVO); LanguageController.getController().delete(db, languageVO); } /** * This method creates a digital asset. * * @param db * @throws Exception */ private DigitalAssetVO createDigitalAsset(Database db) throws Exception { DigitalAssetVO digitalAssetVO = new DigitalAssetVO(); InputStream is = null; try { String contextPath = CmsPropertyHandler.getContextRootPath(); File file = new File(contextPath + "images" + File.separator + "workinganim.gif"); is = new FileInputStream(file); digitalAssetVO.setAssetContentType("image/gif"); digitalAssetVO.setAssetFileName(file.getName()); digitalAssetVO.setAssetFilePath("dummyPath"); digitalAssetVO.setAssetFileSize(new Integer((int)file.length())); digitalAssetVO.setAssetKey("validationAsset"); DigitalAssetController.getController().create(db, digitalAssetVO, is); } catch(Exception e) { e.printStackTrace(); throw e; } finally { if(is != null) is.close(); } return digitalAssetVO; } /** * This method dumps a digital asset. * * @param db * @throws Exception */ private void dumpDigitalAsset(DigitalAssetVO digitalAssetVO, Database db) throws Exception { String url = DigitalAssetController.getController().getDigitalAssetUrl(digitalAssetVO, db); if(url == null || url.length() == 0) { throw new SystemException("The system could not dump the asset to the filesystem and get the url."); } } /** * This method deletes a digital asset. * * @param db * @throws Exception */ private void deleteDigitalAsset(DigitalAssetVO digitalAssetVO, Database db) throws Exception { DigitalAssetController.getController().delete(digitalAssetVO.getId(), db); } private void validateSiteNodes() throws Exception { Database db = CastorDatabaseService.getDatabase(); db.begin(); try { String name = "Metainfo content ids on cmSiteNode"; String description = "This many sitenodes did not have the new property set - bad for performance."; try { List siteNodes = SiteNodeController.getController().getSiteNodesWithoutMetaInfoContentId(db); Iterator siteNodesIterator = siteNodes.iterator(); AvailableServiceBindingVO availableServiceBinding = AvailableServiceBindingController.getController().getAvailableServiceBindingVOWithName("Meta information", db); Integer metaInfoAvailableServiceBindingId = null; if(availableServiceBinding != null) metaInfoAvailableServiceBindingId = availableServiceBinding.getAvailableServiceBindingId(); while(siteNodesIterator.hasNext()) { SiteNode siteNode = (SiteNode)siteNodesIterator.next(); if(siteNode.getSiteNodeVersions() == null || siteNode.getSiteNodeVersions().size() == 0) { //No siteNode is an island (humhum) so we also have to create an siteNodeVersion for it. SiteNodeVersionController.createInitialSiteNodeVersion(db, siteNode, this.getInfoGluePrincipal()); } else { if(siteNode.getMetaInfoContentId() == null || siteNode.getMetaInfoContentId().intValue() == -1) { LanguageVO masterLanguage = LanguageController.getController().getMasterLanguage(siteNode.getRepository().getId(), db); Integer languageId = masterLanguage.getLanguageId(); Integer metaInfoContentId = null; SiteNodeVersion siteNodeVersion = SiteNodeVersionController.getController().getLatestActiveSiteNodeVersionReadOnly(db, siteNode.getId()); boolean hasMetaInfo = false; if(siteNodeVersion == null) { logger.error("Error:" + siteNode.getName() + "(" + siteNode.getId() + ") had no siteNodeVersions"); } else { Collection serviceBindings = siteNodeVersion.getServiceBindings(); Iterator serviceBindingIterator = serviceBindings.iterator(); while(serviceBindingIterator.hasNext()) { ServiceBinding serviceBinding = (ServiceBinding)serviceBindingIterator.next(); if(serviceBinding.getAvailableServiceBinding().getId().intValue() == metaInfoAvailableServiceBindingId.intValue()) { List boundContents = ContentController.getInTransactionBoundContents(db, serviceBinding.getServiceBindingId()); if(boundContents.size() > 0) { ContentVO content = (ContentVO)boundContents.get(0); metaInfoContentId = content.getId(); hasMetaInfo = true; break; } } } if(!hasMetaInfo) { ContentVO contentVO = SiteNodeController.getController().createSiteNodeMetaInfoContent(db, siteNode.getValueObject(), siteNode.getRepository().getId(), this.getInfoGluePrincipal(), null, new ArrayList()).getValueObject(); metaInfoContentId = contentVO.getId(); } siteNode.setMetaInfoContentId(metaInfoContentId); } } } } addValidationItem(name, description, true, "Fixed " + siteNodes.size() + " siteNodes."); } catch(Exception e) { e.printStackTrace(); addValidationItem(name, description, false, "An error occurred: " + e.getMessage()); } db.commit(); } catch(Exception e) { logger.error("An error occurred so we should not complete the transaction:" + e, e); db.rollback(); throw new SystemException(e.getMessage()); } finally { db.close(); } } private void validateAccessRightsUser() throws Exception { Database db = CastorDatabaseService.getDatabase(); db.begin(); try { String name = "AccessRightUser names"; String description = "Checks if the user names given exists in the current authorizationModule."; try { List invalidUsers = new ArrayList(); List users = UserControllerProxy.getController(db).getAllUsers(); List systemUserVOList = AccessRightController.getController().getAccessRightUserVOList(db); Iterator i = systemUserVOList.iterator(); while(i.hasNext()) { AccessRightUserVO accessRightUserVO = (AccessRightUserVO)i.next(); boolean isValid = false; Iterator userIterator = users.iterator(); while(userIterator.hasNext()) { InfoGluePrincipal principal = (InfoGluePrincipal)userIterator.next(); if(principal.getName().equalsIgnoreCase(accessRightUserVO.getUserName())) { isValid = true; break; } } if(!isValid) invalidUsers.add(accessRightUserVO.getUserName()); } addValidationItem(name, description, true, "Faulty " + invalidUsers.size() + " usernames."); } catch(Exception e) { e.printStackTrace(); addValidationItem(name, description, false, "An error occurred: " + e.getMessage()); } db.commit(); } catch(Exception e) { logger.error("An error occurred so we should not complete the transaction:" + e, e); db.rollback(); throw new SystemException(e.getMessage()); } finally { db.close(); } } private void validateAccessRightsRole() throws Exception { Database db = CastorDatabaseService.getDatabase(); db.begin(); try { String name = "AccessRightRole names"; String description = "Checks if the Role names given exists in the current authorizationModule."; try { List invalidRoles = new ArrayList(); List users = RoleControllerProxy.getController(db).getAllRoles(); List systemRoleVOList = AccessRightController.getController().getAccessRightRoleVOList(db); Iterator i = systemRoleVOList.iterator(); while(i.hasNext()) { AccessRightRoleVO accessRightRoleVO = (AccessRightRoleVO)i.next(); boolean isValid = false; Iterator userIterator = users.iterator(); while(userIterator.hasNext()) { InfoGlueRole role = (InfoGlueRole)userIterator.next(); if(role.getName().equalsIgnoreCase(accessRightRoleVO.getRoleName())) { isValid = true; break; } } if(!isValid) invalidRoles.add(accessRightRoleVO.getRoleName()); } addValidationItem(name, description, true, "Faulty " + invalidRoles.size() + " rolenames."); } catch(Exception e) { e.printStackTrace(); addValidationItem(name, description, false, "An error occurred: " + e.getMessage()); } db.commit(); } catch(Exception e) { logger.error("An error occurred so we should not complete the transaction:" + e, e); db.rollback(); throw new SystemException(e.getMessage()); } finally { db.close(); } } private void validateAccessRightsGroup() throws Exception { Database db = CastorDatabaseService.getDatabase(); db.begin(); try { String name = "AccessRightGroup names"; String description = "Checks if the user names given exists in the current authorizationModule."; try { List invalidGroups = new ArrayList(); List users = GroupControllerProxy.getController(db).getAllGroups(); List systemGroupVOList = AccessRightController.getController().getAccessRightGroupVOList(db); Iterator i = systemGroupVOList.iterator(); while(i.hasNext()) { AccessRightGroupVO accessRightGroupVO = (AccessRightGroupVO)i.next(); boolean isValid = false; Iterator userIterator = users.iterator(); while(userIterator.hasNext()) { InfoGlueGroup group = (InfoGlueGroup)userIterator.next(); if(group.getName().equalsIgnoreCase(accessRightGroupVO.getGroupName())) { isValid = true; break; } } if(!isValid) invalidGroups.add(accessRightGroupVO.getGroupName()); } addValidationItem(name, description, true, "Faulty " + invalidGroups.size() + " groupnames."); } catch(Exception e) { e.printStackTrace(); addValidationItem(name, description, false, "An error occurred: " + e.getMessage()); } db.commit(); } catch(Exception e) { logger.error("An error occurred so we should not complete the transaction:" + e, e); db.rollback(); throw new SystemException(e.getMessage()); } finally { db.close(); } } /** * This method validates the authorization plugin connection * @throws Exception */ private void validateAuthorization() throws Exception { String name = "Authorization module"; String description = "Testing if the authorization module can get the data it needs."; try { InfoGluePrincipal user = UserControllerProxy.getController().getUser("mattias"); addValidationItem(name, "Testing if the authorization module can get user: ", true, "Found " + user.getName()); /* List users = UserControllerProxy.getController().getAllUsers(); List roles = RoleControllerProxy.getController().getAllRoles(); List groups = GroupControllerProxy.getController().getAllGroups(); addValidationItem(name, "Testing if the authorization module can get users: ", true, "Found " + users.size() + " users."); addValidationItem(name, "Testing if the authorization module can get roles: ", true, "Found " + roles.size() + " roles."); addValidationItem(name, "Testing if the authorization module can get groups: ", true, "Found " + groups.size() + " groups."); */ } catch(Exception e) { e.printStackTrace(); addValidationItem(name, description, false, "An error occurred: " + e.getMessage()); } } /** * This method just adds an validation item to the list. * @param name * @param status */ private void addValidationItem(String name, String description, boolean status, String reason) { ValidationItem validationItem = new ValidationItem(); validationItem.setName(name); validationItem.setDescription(description); validationItem.setValidationResult(status); validationItem.setReason(reason); this.validatedItems.add(validationItem); } public Boolean getValidateDB() { return validateDB; } public void setValidateDB(Boolean validateDB) { this.validateDB = validateDB; } public Boolean getValidateEntities() { return validateEntities; } public void setValidateEntities(Boolean validateEntities) { this.validateEntities = validateEntities; } public Boolean getValidateFileSystem() { return validateFileSystem; } public void setValidateFileSystem(Boolean validateFileSystem) { this.validateFileSystem = validateFileSystem; } public Boolean getValidateSystemSettings() { return validateSystemSettings; } public void setValidateSystemSettings(Boolean validateSystemSettings) { this.validateSystemSettings = validateSystemSettings; } public List getValidatedItems() { return validatedItems; } }