diff --git a/dsl/src/main/kernel-module/Schema.groovy b/dsl/src/main/kernel-module/Schema.groovy index 08f015421..afaeddd27 100644 --- a/dsl/src/main/kernel-module/Schema.groovy +++ b/dsl/src/main/kernel-module/Schema.groovy @@ -51,3 +51,16 @@ Schema('LoggerConfig', 0) { } } } + +Schema('ModuleChanges', 0) { + struct(name: 'ModuleChanges', useSequence: true) { + struct(name: 'ResourceChangeDetails', useSequence: true, multiplicity: '0..*') { + field(name:'ResourceName', type: 'string') + field(name:'ResourceVersion', type: 'string') + struct(name: 'ResourceChange', useSequence: true, multiplicity: '0..*') { + field(name:'SchemaName', type: 'string') + field(name:'ChangeType', type: 'string', values: ['IDENTICAL', 'NEW', 'UPDATED', 'OVERWRITTEN', 'SKIPPED', 'REMOVED']) + } + } + } +} diff --git a/dsl/src/main/kernel-module/resources/boot/OD/ModuleChanges_0.xsd b/dsl/src/main/kernel-module/resources/boot/OD/ModuleChanges_0.xsd new file mode 100644 index 000000000..71ec53a38 --- /dev/null +++ b/dsl/src/main/kernel-module/resources/boot/OD/ModuleChanges_0.xsd @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dsl/src/main/kernel-module/resources/module.xml b/dsl/src/main/kernel-module/resources/module.xml index e9b6b1065..d379d0d71 100644 --- a/dsl/src/main/kernel-module/resources/module.xml +++ b/dsl/src/main/kernel-module/resources/module.xml @@ -6,5 +6,6 @@ + diff --git a/kernel/src/main/java/org/cristalise/kernel/entity/imports/ImportItem.java b/kernel/src/main/java/org/cristalise/kernel/entity/imports/ImportItem.java index fac81ad3a..b1872cb2d 100644 --- a/kernel/src/main/java/org/cristalise/kernel/entity/imports/ImportItem.java +++ b/kernel/src/main/java/org/cristalise/kernel/entity/imports/ImportItem.java @@ -169,10 +169,10 @@ public Path create(AgentPath agentPath, boolean reset) throws InvalidDataException, ObjectCannotBeUpdated, ObjectNotFoundException, CannotManageException, ObjectAlreadyExistsException, InvalidCollectionModification, PersistencyException { - log.info("create() - name:{}", name); - domainPath = new DomainPath(new DomainPath(initialPath), name); + log.info("create() - path:{}", domainPath); + if (domainPath.exists()) { ItemPath domItem = domainPath.getItemPath(); if (!getItemPath().equals(domItem)) { diff --git a/kernel/src/main/java/org/cristalise/kernel/lifecycle/ActivityDef.java b/kernel/src/main/java/org/cristalise/kernel/lifecycle/ActivityDef.java index 2244fce8a..f6eb1313d 100644 --- a/kernel/src/main/java/org/cristalise/kernel/lifecycle/ActivityDef.java +++ b/kernel/src/main/java/org/cristalise/kernel/lifecycle/ActivityDef.java @@ -244,12 +244,12 @@ protected DescriptionObject[] getBuiltInCollectionResource(BuiltInCollections co ArrayList retArr = new ArrayList(); if (itemPath == null) { - log.warn("getBuiltInCollectionResource(actName:"+getName()+", collection:"+collection+") - itemPath is null! CANNOT resolve data in ClusterStorage"); + log.debug("getBuiltInCollectionResource(actName:{}, collection:{}) - itemPath is null! CANNOT resolve data in ClusterStorage", getName(), collection); return retArr.toArray(new DescriptionObject[0]); //throw new InvalidDataException("actName:"+getName()+", collection:"+collection+" - itemPath is null! CANNOT resolve data in ClusterStorage"); } - log.info("getBuiltInCollectionResource(actName:"+getName()+") - Loading from collection:"+collection); + log.info("getBuiltInCollectionResource(actName:{}) - Loading from collection:{}", getName(), collection); Dependency resColl; diff --git a/kernel/src/main/java/org/cristalise/kernel/lifecycle/instance/Activity.java b/kernel/src/main/java/org/cristalise/kernel/lifecycle/instance/Activity.java index 805c31a27..5af16f17b 100644 --- a/kernel/src/main/java/org/cristalise/kernel/lifecycle/instance/Activity.java +++ b/kernel/src/main/java/org/cristalise/kernel/lifecycle/instance/Activity.java @@ -80,8 +80,7 @@ public class Activity extends WfVertex { protected static final String XPATH_TOKEN = "xpath:"; /** - * vector of errors (Strings) that is constructed each time verify() is - * launched + * vector of errors (Strings) that is constructed each time verify() was launched */ protected Vector mErrors; /** @@ -182,6 +181,30 @@ public String request(AgentPath agent, byte[] attachment, Object locker ) + throws AccessRightsException, + InvalidTransitionException, + InvalidDataException, + ObjectNotFoundException, + PersistencyException, + ObjectAlreadyExistsException, + ObjectCannotBeUpdated, + CannotManageException, + InvalidCollectionModification + { + boolean validateOutcome = Gateway.getProperties().getBoolean("Activity.validateOutcome", false); + return request(agent, delegate, itemPath, transitionID, requestData, attachmentType, attachment, validateOutcome, locker); + } + + public String request(AgentPath agent, + AgentPath delegate, + ItemPath itemPath, + int transitionID, + String requestData, + String attachmentType, + byte[] attachment, + boolean validateOutcome, + Object locker + ) throws AccessRightsException, InvalidTransitionException, InvalidDataException, @@ -218,42 +241,41 @@ else if (transition.getOutcome().isRequired()) setState(newState.getId()); setBuiltInProperty(AGENT_NAME, transition.getReservation(this, agent)); - try { - History hist = getWf().getHistory(locker); + History hist = null; - if (storeOutcome) { - Schema schema = transition.getSchema(getProperties()); - Outcome newOutcome = new Outcome(-1, outcome, schema); - // TODO: if we were ever going to validate outcomes on storage, it would be here. - //newOutcome.validateAndCheck(); + // Enables PredefinedSteps instances to call Activity.request() during bootstrap + if (getParent() != null) hist = getWf().getHistory(locker); + else hist = new History(itemPath, locker); - String viewpoint = resolveViewpointName(newOutcome); + if (storeOutcome) { + Schema schema = transition.getSchema(getProperties()); + Outcome newOutcome = new Outcome(-1, outcome, schema); - int eventID = hist.addEvent(agent, delegate, usedRole, getName(), getPath(), getType(), - schema, getStateMachine(), transitionID, viewpoint).getID(); - newOutcome.setID(eventID); + // This is used by PredefinedStep executed during bootstrap + if (validateOutcome) newOutcome.validateAndCheck(); - Gateway.getStorage().put(itemPath, newOutcome, locker); - if (attachment.length > 0) Gateway.getStorage().put(itemPath, new OutcomeAttachment(itemPath, newOutcome, attachmentType, attachment), locker); + String viewpoint = resolveViewpointName(newOutcome); - // update specific view if defined - if (!viewpoint.equals("last")) { - Gateway.getStorage().put(itemPath, new Viewpoint(itemPath, schema, viewpoint, eventID), locker); - } + int eventID = hist.addEvent(agent, delegate, usedRole, getName(), getPath(), getType(), + schema, getStateMachine(), transitionID, viewpoint).getID(); + newOutcome.setID(eventID); - // update the default "last" view - Gateway.getStorage().put(itemPath, new Viewpoint(itemPath, schema, "last", eventID), locker); + Gateway.getStorage().put(itemPath, newOutcome, locker); + if (attachment.length > 0) Gateway.getStorage().put(itemPath, new OutcomeAttachment(itemPath, newOutcome, attachmentType, attachment), locker); - updateItemProperties(itemPath, newOutcome, locker); - } - else { - updateItemProperties(itemPath, null, locker); - hist.addEvent(agent, delegate, usedRole, getName(), getPath(), getType(), getStateMachine(), transitionID); + // update specific view if defined + if (!viewpoint.equals("last")) { + Gateway.getStorage().put(itemPath, new Viewpoint(itemPath, schema, viewpoint, eventID), locker); } + + // update the default "last" view + Gateway.getStorage().put(itemPath, new Viewpoint(itemPath, schema, "last", eventID), locker); + + updateItemProperties(itemPath, newOutcome, locker); } - catch (PersistencyException ex) { - log.error("", ex); - throw ex; + else { + updateItemProperties(itemPath, null, locker); + hist.addEvent(agent, delegate, usedRole, getName(), getPath(), getType(), getStateMachine(), transitionID); } if (newState.isFinished() && !(getBuiltInProperty(BREAKPOINT).equals(Boolean.TRUE) && !oldState.isFinished())) { diff --git a/kernel/src/main/java/org/cristalise/kernel/lifecycle/instance/predefined/PredefinedStep.java b/kernel/src/main/java/org/cristalise/kernel/lifecycle/instance/predefined/PredefinedStep.java index b566f594c..5affea988 100644 --- a/kernel/src/main/java/org/cristalise/kernel/lifecycle/instance/predefined/PredefinedStep.java +++ b/kernel/src/main/java/org/cristalise/kernel/lifecycle/instance/predefined/PredefinedStep.java @@ -37,6 +37,7 @@ import org.cristalise.kernel.common.CannotManageException; import org.cristalise.kernel.common.InvalidCollectionModification; import org.cristalise.kernel.common.InvalidDataException; +import org.cristalise.kernel.common.InvalidTransitionException; import org.cristalise.kernel.common.ObjectAlreadyExistsException; import org.cristalise.kernel.common.ObjectCannotBeUpdated; import org.cristalise.kernel.common.ObjectNotFoundException; @@ -127,7 +128,12 @@ public void setIsPredefined(boolean isPredefined) { @Override public String getType() { - return getName(); + return this.getClass().getSimpleName(); + } + + @Override + public String getName() { + return this.getClass().getSimpleName(); } static public String getPredefStepSchemaName(String stepName) { @@ -285,4 +291,37 @@ public static void storeOutcomeEventAndViews(ItemPath itemPath, Outcome newOutco Gateway.getStorage().put(itemPath, newNumberView, null); } } + + /** + * Use this method to run a Predefined step during bootstrap + * + * @param agent + * @param itemPath + * @param requestData + * @return + * @throws AccessRightsException + * @throws InvalidTransitionException + * @throws InvalidDataException + * @throws ObjectNotFoundException + * @throws PersistencyException + * @throws ObjectAlreadyExistsException + * @throws ObjectCannotBeUpdated + * @throws CannotManageException + * @throws InvalidCollectionModification + */ + public String request(AgentPath agent, ItemPath itemPath, String requestData) + throws AccessRightsException, + InvalidTransitionException, + InvalidDataException, + ObjectNotFoundException, + PersistencyException, + ObjectAlreadyExistsException, + ObjectCannotBeUpdated, + CannotManageException, + InvalidCollectionModification + { + log.info("request({}) - Type:{}", itemPath, getType()); + this.setActive(true); + return request(agent, agent, itemPath, PredefinedStep.DONE, requestData, null, new byte[0], true, null); + } } diff --git a/kernel/src/main/java/org/cristalise/kernel/lifecycle/instance/predefined/PredefinedStepContainer.java b/kernel/src/main/java/org/cristalise/kernel/lifecycle/instance/predefined/PredefinedStepContainer.java index 0cdaf11f9..b4c217b15 100644 --- a/kernel/src/main/java/org/cristalise/kernel/lifecycle/instance/predefined/PredefinedStepContainer.java +++ b/kernel/src/main/java/org/cristalise/kernel/lifecycle/instance/predefined/PredefinedStepContainer.java @@ -34,7 +34,7 @@ public PredefinedStepContainer() { createChildren(); } - //TODO make this complete configure from the given class + //TODO make this complete configure from the given classes public void createChildren() { predInit("AddDomainPath", "Adds a new path to this item in the LDAP domain tree", new AddDomainPath()); predInit("RemoveDomainPath", "Removes an existing path to this item from the LDAP domain tree", new RemoveDomainPath()); @@ -59,6 +59,8 @@ public void createChildren() { predInit(UpdateCollectionsFromDescription.class.getSimpleName(), UpdateCollectionsFromDescription.description, new UpdateCollectionsFromDescription()); predInit(UpdateProperitesFromDescription.class.getSimpleName(), UpdateProperitesFromDescription.description, new UpdateProperitesFromDescription()); + + //UpdateImportReport class is not added to the container because it can only be used during bootstrap } public void predInit(Class clazz, String description, PredefinedStep act) { diff --git a/kernel/src/main/java/org/cristalise/kernel/lifecycle/instance/predefined/UpdateImportReport.java b/kernel/src/main/java/org/cristalise/kernel/lifecycle/instance/predefined/UpdateImportReport.java new file mode 100644 index 000000000..8b23c9a4f --- /dev/null +++ b/kernel/src/main/java/org/cristalise/kernel/lifecycle/instance/predefined/UpdateImportReport.java @@ -0,0 +1,65 @@ +/** + * This file is part of the CRISTAL-iSE kernel. + * Copyright (c) 2001-2015 The CRISTAL Consortium. All rights reserved. + * + * This library 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 3 of the License, or (at + * your option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; with out 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 library; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * http://www.fsf.org/licensing/licenses/lgpl.html + */ +package org.cristalise.kernel.lifecycle.instance.predefined; + +import static org.cristalise.kernel.graph.model.BuiltInVertexProperties.SCHEMA_NAME; + +import org.cristalise.kernel.common.AccessRightsException; +import org.cristalise.kernel.common.CannotManageException; +import org.cristalise.kernel.common.InvalidCollectionModification; +import org.cristalise.kernel.common.InvalidDataException; +import org.cristalise.kernel.common.ObjectAlreadyExistsException; +import org.cristalise.kernel.common.ObjectCannotBeUpdated; +import org.cristalise.kernel.common.ObjectNotFoundException; +import org.cristalise.kernel.common.PersistencyException; +import org.cristalise.kernel.lookup.AgentPath; +import org.cristalise.kernel.lookup.ItemPath; + +import lombok.extern.slf4j.Slf4j; + +/** + * {@value #description} + */ +@Slf4j +public class UpdateImportReport extends PredefinedStep { + + public static final String description = "Store the ModuleChanges report in Server Item and Modules. Can only be used during bootstrap."; + + public UpdateImportReport() { + super(); + setBuiltInProperty(SCHEMA_NAME, "ModuleChanges"); + } + + protected String runActivityLogic(AgentPath agent, ItemPath itemPath, int transitionID, String requestData, Object locker) + throws InvalidDataException, + InvalidCollectionModification, + ObjectAlreadyExistsException, + ObjectCannotBeUpdated, + ObjectNotFoundException, + PersistencyException, + CannotManageException, + AccessRightsException + { + log.debug("Called by {} on {}", agent.getAgentName(), itemPath); + // not much to do here + return requestData; + } +} diff --git a/kernel/src/main/java/org/cristalise/kernel/process/Bootstrap.java b/kernel/src/main/java/org/cristalise/kernel/process/Bootstrap.java index 5d2ca3041..ad444c1da 100644 --- a/kernel/src/main/java/org/cristalise/kernel/process/Bootstrap.java +++ b/kernel/src/main/java/org/cristalise/kernel/process/Bootstrap.java @@ -20,7 +20,6 @@ */ package org.cristalise.kernel.process; -import static org.cristalise.kernel.process.resource.ResourceImportHandler.Status.IDENTICAL; import static org.cristalise.kernel.property.BuiltInItemProperties.KERNEL_VERSION; import static org.cristalise.kernel.property.BuiltInItemProperties.NAME; import static org.cristalise.kernel.property.BuiltInItemProperties.TYPE; @@ -28,7 +27,9 @@ import static org.cristalise.kernel.security.BuiltInAuthc.SYSTEM_AGENT; import java.net.InetAddress; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.StringTokenizer; import java.util.UUID; @@ -39,6 +40,7 @@ import org.cristalise.kernel.lifecycle.instance.CompositeActivity; import org.cristalise.kernel.lifecycle.instance.Workflow; import org.cristalise.kernel.lifecycle.instance.predefined.PredefinedStep; +import org.cristalise.kernel.lifecycle.instance.predefined.UpdateImportReport; import org.cristalise.kernel.lifecycle.instance.predefined.server.ServerPredefinedStepContainer; import org.cristalise.kernel.lookup.AgentPath; import org.cristalise.kernel.lookup.DomainPath; @@ -61,8 +63,8 @@ * Bootstrap loads all Items defined in the kernel resource XMLs and the module XML */ @Slf4j -public class Bootstrap -{ +public class Bootstrap { + static DomainPath thisServerPath; static HashMap systemAgents = new HashMap(); public static boolean shutdown = false; @@ -162,9 +164,11 @@ public static void verifyBootDataItems() throws Exception { * @param reset * @throws InvalidItemPathException */ - private static void verifyBootDataItems(String bootList, String ns, boolean reset) throws InvalidItemPathException { + private static void verifyBootDataItems(String bootList, String ns, boolean reset) throws Exception { StringTokenizer str = new StringTokenizer(bootList, "\n\r"); + List kernelChanges = new ArrayList(); + while (str.hasMoreTokens() && !shutdown) { String thisItem = str.nextToken(); String[] idFilename = thisItem.split(","); @@ -178,17 +182,20 @@ private static void verifyBootDataItems(String bootList, String ns, boolean rese ResourceImportHandler importHandler = Gateway.getResourceImportHandler(BuiltInResources.getValue(itemType)); importHandler.importResource(ns, itemName, 0, itemPath, location, reset); - if (importHandler.getResourceStatus() != IDENTICAL) { - // - } + kernelChanges.add(importHandler.getResourceChangeDetails()); } catch (Exception e) { log.error("Error importing bootstrap items. Unsafe to continue.", e); AbstractMain.shutdown(1); } } - } + StringBuffer moduleChangesXML = new StringBuffer("\n"); + for (String oneChange: kernelChanges) moduleChangesXML.append(oneChange).append("\n"); + moduleChangesXML.append(""); + + new UpdateImportReport().request((AgentPath)SYSTEM_AGENT.getPath(), thisServerPath.getItemPath(), moduleChangesXML.toString()); + } /** * Checks for the existence of a agents and creates it if needed so it can be used diff --git a/kernel/src/main/java/org/cristalise/kernel/process/module/Module.java b/kernel/src/main/java/org/cristalise/kernel/process/module/Module.java index 666a2d64b..af58df57f 100644 --- a/kernel/src/main/java/org/cristalise/kernel/process/module/Module.java +++ b/kernel/src/main/java/org/cristalise/kernel/process/module/Module.java @@ -21,16 +21,17 @@ package org.cristalise.kernel.process.module; import static org.cristalise.kernel.collection.BuiltInCollections.CONTENTS; -import static org.cristalise.kernel.process.resource.ResourceImportHandler.Status.IDENTICAL; import static org.cristalise.kernel.property.BuiltInItemProperties.COMPLEXITY; import static org.cristalise.kernel.property.BuiltInItemProperties.MODULE; import static org.cristalise.kernel.property.BuiltInItemProperties.NAME; import static org.cristalise.kernel.property.BuiltInItemProperties.NAMESPACE; import static org.cristalise.kernel.property.BuiltInItemProperties.TYPE; import static org.cristalise.kernel.property.BuiltInItemProperties.VERSION; +import static org.cristalise.kernel.security.BuiltInAuthc.SYSTEM_AGENT; import java.io.File; import java.util.ArrayList; +import java.util.List; import java.util.Properties; import org.apache.commons.lang3.StringUtils; @@ -46,6 +47,8 @@ import org.cristalise.kernel.entity.imports.ImportRole; import org.cristalise.kernel.entity.proxy.AgentProxy; import org.cristalise.kernel.entity.proxy.ItemProxy; +import org.cristalise.kernel.lifecycle.instance.predefined.UpdateImportReport; +import org.cristalise.kernel.lookup.AgentPath; import org.cristalise.kernel.lookup.Path; import org.cristalise.kernel.persistency.ClusterType; import org.cristalise.kernel.process.AbstractMain; @@ -126,10 +129,9 @@ private void replaceProp(Property newProp) { } properties.add(newProp); } - + private void addItemToContents(Path itemPath) { ImportDependency contents = dependencyList.get(0); - contents.dependencyMemberList.add(new ImportDependencyMember(itemPath.toString())); } @@ -142,13 +144,18 @@ private void addItemToContents(Path itemPath) { * @throws Exception All possible exceptions */ public void importAll(ItemProxy serverEntity, AgentProxy systemAgent, boolean reset) throws Exception { - if (!Bootstrap.shutdown) importResources(systemAgent, reset); - if (!Bootstrap.shutdown) importRoles( systemAgent, reset); - if (!Bootstrap.shutdown) importAgents( systemAgent, reset); - if (!Bootstrap.shutdown) importItems( systemAgent, reset); + String moduleChanges = ""; + + if (!Bootstrap.shutdown) moduleChanges = importResources(systemAgent, reset); + if (!Bootstrap.shutdown) importRoles( systemAgent, reset); + if (!Bootstrap.shutdown) importAgents(systemAgent, reset); + if (!Bootstrap.shutdown) importItems( systemAgent, reset); //Finally create this Module Item if (!Bootstrap.shutdown) this.create(systemAgent.getPath(), reset); + + if (StringUtils.isNotBlank(moduleChanges)) + new UpdateImportReport().request((AgentPath)SYSTEM_AGENT.getPath(), itemPath, moduleChanges); } /** @@ -203,23 +210,28 @@ private void importRoles(AgentProxy systemAgent, boolean reset) throws Exception * @param systemAgent * @param reset */ - private void importResources(AgentProxy systemAgent, boolean reset) { + private String importResources(AgentProxy systemAgent, boolean reset) throws Exception { + List moduleChanges = new ArrayList(); + for (ModuleResource thisRes : imports.getResources()) { - if (Bootstrap.shutdown) return; + if (Bootstrap.shutdown) return ""; try { thisRes.setNamespace(ns); addItemToContents(thisRes.create(systemAgent.getPath(), reset)); - - if (thisRes.getStatus() != IDENTICAL) { - - } + moduleChanges.add(thisRes.getResourceChangeDetails()); } catch (Exception ex) { log.error("Error importing module resources. Unsafe to continue.", ex); AbstractMain.shutdown(1); } } + + StringBuffer moduleChangesXML = new StringBuffer("\n"); + for (String oneChange: moduleChanges) moduleChangesXML.append(oneChange).append("\n"); + moduleChangesXML.append(""); + + return moduleChangesXML.toString(); } /** diff --git a/kernel/src/main/java/org/cristalise/kernel/process/module/ModuleActivity.java b/kernel/src/main/java/org/cristalise/kernel/process/module/ModuleActivity.java index dbf4581ca..12332d8fc 100644 --- a/kernel/src/main/java/org/cristalise/kernel/process/module/ModuleActivity.java +++ b/kernel/src/main/java/org/cristalise/kernel/process/module/ModuleActivity.java @@ -85,6 +85,7 @@ public Path create(AgentPath agentPath, boolean reset) try { ResourceImportHandler importHandler = Gateway.getResourceImportHandler(type); domainPath = importHandler.importResource(ns, name, version, itemPath, getResourceLocation(), reset); + resourceChangeDetails = importHandler.getResourceChangeDetails(); itemPath = domainPath.getItemPath(); } catch (Exception e) { diff --git a/kernel/src/main/java/org/cristalise/kernel/process/module/ModuleManager.java b/kernel/src/main/java/org/cristalise/kernel/process/module/ModuleManager.java index f2296a0bd..e4adeb77d 100644 --- a/kernel/src/main/java/org/cristalise/kernel/process/module/ModuleManager.java +++ b/kernel/src/main/java/org/cristalise/kernel/process/module/ModuleManager.java @@ -228,32 +228,27 @@ public void runScripts(String event) { public void registerModules() throws ModuleException { ItemProxy serverItem; + DomainPath serverItemDP = new DomainPath("/servers/"+Gateway.getProperties().getString("ItemServer.name")); + try { - serverItem = Gateway.getProxyManager().getProxy(new DomainPath("/servers/"+Gateway.getProperties().getString("ItemServer.name"))); - } + serverItem = Gateway.getProxyManager().getProxy(serverItemDP); + } catch (ObjectNotFoundException e) { - throw new ModuleException("Cannot find local server name."); + throw new ModuleException("Cannot find local server Item:"+serverItemDP); } - log.info("registerModules() - Registering modules"); - boolean reset = Gateway.getProperties().getBoolean("Module.reset", false); for (Module thisMod : modules) { if (Bootstrap.shutdown) return; - log.info("registerModules() - Registering module "+thisMod.getName()); - try { - String thisResetKey = "Module."+thisMod.getNamespace()+".reset"; - boolean thisReset = reset; + reset = Gateway.getProperties().getBoolean("Module."+thisMod.getNamespace()+".reset", reset); - if (Gateway.getProperties().containsKey(thisResetKey)) { - thisReset = Gateway.getProperties().getBoolean(thisResetKey); - } + log.info("registerModules() - Registering module ns:'{}' with reset:{}", thisMod.getNamespace(), reset); thisMod.setModuleXML(modulesXML.get(thisMod.getNamespace())); - thisMod.importAll(serverItem, agent, thisReset); + thisMod.importAll(serverItem, agent, reset); } catch (Exception e) { log.error("", e); diff --git a/kernel/src/main/java/org/cristalise/kernel/process/module/ModuleResource.java b/kernel/src/main/java/org/cristalise/kernel/process/module/ModuleResource.java index 85568cb6b..01d4c5e63 100644 --- a/kernel/src/main/java/org/cristalise/kernel/process/module/ModuleResource.java +++ b/kernel/src/main/java/org/cristalise/kernel/process/module/ModuleResource.java @@ -20,10 +20,6 @@ */ package org.cristalise.kernel.process.module; -import lombok.Getter; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; - import org.apache.commons.lang3.StringUtils; import org.cristalise.kernel.common.CannotManageException; import org.cristalise.kernel.common.InvalidDataException; @@ -35,7 +31,10 @@ import org.cristalise.kernel.process.Gateway; import org.cristalise.kernel.process.resource.BuiltInResources; import org.cristalise.kernel.process.resource.ResourceImportHandler; -import org.cristalise.kernel.process.resource.ResourceImportHandler.Status; + +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; @Getter @Setter @Slf4j public class ModuleResource extends ModuleImport { @@ -43,7 +42,9 @@ public class ModuleResource extends ModuleImport { public int version; public BuiltInResources type; public String resourceLocation; - private Status status; + + @Getter + String resourceChangeDetails = null; public ModuleResource() { // if not given, version defaults to 0 @@ -94,14 +95,15 @@ public Path create(AgentPath agentPath, boolean reset) try { ResourceImportHandler importHandler = Gateway.getResourceImportHandler(type); - domainPath = importHandler.importResource(ns, name, version, itemPath, getResourceLocation(), reset); - status = importHandler.getResourceStatus(); + resourceChangeDetails = importHandler.getResourceChangeDetails(); + + if (itemPath == null) itemPath = domainPath.getItemPath(); return domainPath; } catch (Exception e) { - log.error("", e); + log.error("Exception verifying module resource {}/{}", ns, name, e); throw new CannotManageException("Exception verifying module resource " + ns + "/" + name); } } diff --git a/kernel/src/main/java/org/cristalise/kernel/process/resource/DefaultResourceImportHandler.java b/kernel/src/main/java/org/cristalise/kernel/process/resource/DefaultResourceImportHandler.java index b6c005569..9673075a2 100644 --- a/kernel/src/main/java/org/cristalise/kernel/process/resource/DefaultResourceImportHandler.java +++ b/kernel/src/main/java/org/cristalise/kernel/process/resource/DefaultResourceImportHandler.java @@ -23,16 +23,21 @@ import static org.cristalise.kernel.process.resource.BuiltInResources.QUERY_RESOURCE; import static org.cristalise.kernel.process.resource.BuiltInResources.SCHEMA_RESOURCE; import static org.cristalise.kernel.process.resource.BuiltInResources.SCRIPT_RESOURCE; -import static org.cristalise.kernel.process.resource.ResourceImportHandler.Status.CHANGED; import static org.cristalise.kernel.process.resource.ResourceImportHandler.Status.IDENTICAL; import static org.cristalise.kernel.process.resource.ResourceImportHandler.Status.NEW; import static org.cristalise.kernel.process.resource.ResourceImportHandler.Status.OVERWRITTEN; -import static org.cristalise.kernel.process.resource.ResourceImportHandler.Status.UNCHANGED; +import static org.cristalise.kernel.process.resource.ResourceImportHandler.Status.SKIPPED; +import static org.cristalise.kernel.process.resource.ResourceImportHandler.Status.UPDATED; import static org.cristalise.kernel.property.BuiltInItemProperties.MODULE; import static org.cristalise.kernel.property.BuiltInItemProperties.NAME; import static org.cristalise.kernel.security.BuiltInAuthc.SYSTEM_AGENT; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Set; import org.cristalise.kernel.collection.Collection; @@ -60,7 +65,12 @@ import org.cristalise.kernel.querying.Query; import org.cristalise.kernel.scripting.Script; import org.cristalise.kernel.utils.DescriptionObject; +import org.cristalise.kernel.utils.FileStringUtility; import org.cristalise.kernel.utils.LocalObjectLoader; +import org.cristalise.kernel.utils.ObjectProperties; +import org.mvel2.templates.CompiledTemplate; +import org.mvel2.templates.TemplateCompiler; +import org.mvel2.templates.TemplateRuntime; import lombok.extern.slf4j.Slf4j; @@ -71,7 +81,7 @@ public class DefaultResourceImportHandler implements ResourceImportHandler { DomainPath typeRootPath; PropertyDescriptionList props; - private Status status = IDENTICAL; + String resourceChangeDetails = ""; public DefaultResourceImportHandler(BuiltInResources resType) throws Exception { type = resType; @@ -191,35 +201,38 @@ public DomainPath importResource(String ns, String itemName, int version, ItemPa private DomainPath verifyResource(String ns, String itemName, int version, ItemPath itemPath, Set outcomes, boolean reset) throws Exception { - log.info("verifyResource() - Item '{}' of type '{}' verion '{}'", itemName, getName(), version); + if (outcomes.size() == 0) { + log.warn("verifyResource() - NO Outcome was found nothing stored for Item '{}' of type '{}' version '{}'", itemName, getName(), version); + return null; + } + + log.debug("verifyResource() - Item '{}' of type '{}' version '{}'", itemName, getName(), version); // Find or create Item for Resource ItemProxy thisProxy; DomainPath modDomPath = getPath(itemName, ns); if (modDomPath.exists()) { - log.info("verifyResource() - Found "+getName()+" "+itemName + "."); + log.debug("verifyResource() - Found "+getName()+" "+itemName + "."); thisProxy = verifyPathAndModuleProperty(ns, itemName, itemPath, modDomPath, modDomPath); } else { - if (itemPath == null) itemPath = new ItemPath(); - - log.info("verifyResource() - "+getName()+" "+itemName+" not found. Creating new."); + log.debug("verifyResource() - "+getName()+" "+itemName+" not found. Creating new."); + if (itemPath == null) itemPath = new ItemPath(); //itemPath can be hardcoded in the bootstrap for example thisProxy = createResourceItem(itemName, ns, itemPath); } - - if (outcomes.size() == 0) - log.warn("verifyResource() - NO Outcome was found nothing stored for Item '{}' of type '{}' verion '{}'", itemName, getName(), version); + + List> resourceChangesList = new ArrayList>(); // Verify/Import Outcomes, creating events and views as necessary for (Outcome newOutcome : outcomes) { - status = checkToStoreOutcomeVersion(thisProxy, newOutcome, version, reset); + Status status = checkToStoreOutcomeVersion(thisProxy, newOutcome, version, reset); - log.info("checkToStoreOutcomeVersion() - {} item:{} schema:{} version:{} ", status.name(), thisProxy.getName(), newOutcome.getSchema().getName(), version); + log.info("verifyResource() - Outcome {} of item:{} schema:{} version:{} ", status.name(), thisProxy.getName(), newOutcome.getSchema().getName(), version); - if (status != IDENTICAL || status != UNCHANGED) { + if (status != IDENTICAL && status != SKIPPED) { // validate it, but not for kernel objects (ns == null) because those are to validate the rest if (ns != null) newOutcome.validateAndCheck(); @@ -234,6 +247,17 @@ private DomainPath verifyResource(String ns, String itemName, int version, ItemP Gateway.getStorage().put(thisProxy.getPath(), col, null); } } + + Map c = new HashMap(); + c.put("SchemaName", newOutcome.getSchema().getName()); + c.put("ChangeType", status.name()); + resourceChangesList.add(c); + } + + resourceChangeDetails = convertToResourceChangeDetails(itemName, version, resourceChangesList); + + if (log.isTraceEnabled()) { + log.trace("verifyResource() - resourceChangeDetails:{}", resourceChangeDetails.replace("\n", "").replaceAll(">\\s*<", "><")); } Gateway.getStorage().commit(null); @@ -300,12 +324,12 @@ private Status checkToStoreOutcomeVersion(ItemProxy item, Outcome newOutcome, in } else { if (currentData.getEvent().getStepPath().equals("Bootstrap")) { - return CHANGED; + return UPDATED; } else { // Use system property 'Module.reset' or 'Module..reset' to control if bootstrap should overwrite the resource if (reset) return OVERWRITTEN; - else return UNCHANGED; + else return SKIPPED; } } } @@ -341,7 +365,8 @@ private ItemProxy createResourceItem(String itemName, String ns, ItemPath itemPa ca = (CompositeActivity) ((CompositeActivityDef)LocalObjectLoader.getActDef(getWorkflowName(), 0)).instantiate(); } catch (ObjectNotFoundException ex) { - log.error("Module resource workflow "+getWorkflowName()+" not found. Using empty.", ex); + // FIXME check if this could be a real error + log.warn("Module resource workflow "+getWorkflowName()+" not found. Using empty.", ex); } Gateway.getCorbaServer().createItem(itemPath); @@ -354,8 +379,30 @@ private ItemProxy createResourceItem(String itemName, String ns, ItemPath itemPa return newItemProxy; } + /** + * Creates an XML fragment defined in Schema ModuleChanges struct ResourceChangeDetails + * + * @param name of the resource item could be UUID + * @param version of the resource Item + * @param resourceChangesList the change list which was computed during verifyResource() + * @return the xml fragment + * @throws IOException template file was not found + */ + private String convertToResourceChangeDetails(String name, int version, List> resourceChangesList) throws IOException { + String templ = FileStringUtility.url2String(ObjectProperties.class.getResource("resources/templates/ResourceChangeDetails_xsd.tmpl")); + CompiledTemplate expr = TemplateCompiler.compileTemplate(templ); + + Map vars = new HashMap(); + + vars.put("resourceName", name); + vars.put("resourceVersion", version); + vars.put("resourceChanges", resourceChangesList); + + return (String)TemplateRuntime.execute(expr, vars); + } + @Override - public Status getResourceStatus() { - return status; + public String getResourceChangeDetails() { + return resourceChangeDetails; } } diff --git a/kernel/src/main/java/org/cristalise/kernel/process/resource/ResourceImportHandler.java b/kernel/src/main/java/org/cristalise/kernel/process/resource/ResourceImportHandler.java index 75e3b5343..f14c57342 100644 --- a/kernel/src/main/java/org/cristalise/kernel/process/resource/ResourceImportHandler.java +++ b/kernel/src/main/java/org/cristalise/kernel/process/resource/ResourceImportHandler.java @@ -32,7 +32,7 @@ public interface ResourceImportHandler { - public enum Status {IDENTICAL, NEW, CHANGED, OVERWRITTEN, UNCHANGED}; + public enum Status {IDENTICAL, NEW, UPDATED, OVERWRITTEN, SKIPPED, REMOVED}; /** * Returns the DomainPath for a specific resource @@ -164,5 +164,9 @@ public DomainPath importResource(String ns, String itemName, int version, ItemPa public DomainPath importResource(String ns, String itemName, int version, ItemPath itemPath, Set outcomes, boolean reset) throws Exception; - public Status getResourceStatus(); + /** + * read teh XML string that was created during the createResource() and importResource() methods + * @return xml string + */ + public String getResourceChangeDetails(); } diff --git a/kernel/src/main/resources/boot/OD/ModuleChanges.xsd b/kernel/src/main/resources/boot/OD/ModuleChanges.xsd new file mode 100644 index 000000000..71ec53a38 --- /dev/null +++ b/kernel/src/main/resources/boot/OD/ModuleChanges.xsd @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/kernel/src/main/resources/boot/allbootitems.txt b/kernel/src/main/resources/boot/allbootitems.txt index d7fcd4074..9353a877f 100644 --- a/kernel/src/main/resources/boot/allbootitems.txt +++ b/kernel/src/main/resources/boot/allbootitems.txt @@ -25,6 +25,7 @@ 00000000-0000-0000-0000-000000000115,OD/SimpleElectonicSignature 00000000-0000-0000-0000-000000000116,OD/SystemProperties 00000000-0000-0000-0000-000000000117,OD/LoggerConfig +00000000-0000-0000-0000-000000000118,OD/ModuleChanges 00000000-0000-0000-0000-000000000200,SC/ServerNewEntity 00000000-0000-0000-0000-000000000201,SC/CreateNewNumberedVersionFromLast 00000000-0000-0000-0000-000000000202,SC/SetLastNumberedVersionToLast diff --git a/kernel/src/main/resources/templates/ResourceChangeDetails_xsd.tmpl b/kernel/src/main/resources/templates/ResourceChangeDetails_xsd.tmpl new file mode 100644 index 000000000..c5b0e806f --- /dev/null +++ b/kernel/src/main/resources/templates/ResourceChangeDetails_xsd.tmpl @@ -0,0 +1,10 @@ + + @{resourceName} + @{resourceVersion} +@foreach{change: resourceChanges} + + @{change.SchemaName} + @{change.ChangeType} + +@end{} + \ No newline at end of file