From 44d378da590af44323395956922f37e81422ff34 Mon Sep 17 00:00:00 2001 From: tosmers Date: Mon, 18 May 2015 18:20:18 +0000 Subject: [PATCH] =?UTF-8?q?[FEATURE][UPDATE]=20Die=20Funktionalit=C3=A4t?= =?UTF-8?q?=20ein=20Modul=20=C3=BCber=20"ccm=20unload..."=20zu=20deinstall?= =?UTF-8?q?ieren=20ist=20jetzt=20verf=C3=BCgtbar=20(Achtung!=20funktionier?= =?UTF-8?q?t=20noch=20nicht).=20Daf=C3=BCr=20vorgenommene=20Modifikationen?= =?UTF-8?q?:=20-=20neue=20Klasse=20Unload.java,=20die=20die=20Funktion=20d?= =?UTF-8?q?es=20"ccm=20unload"=20bereitstellt=20-=20neue=20Klassen=20LoadC?= =?UTF-8?q?enter.java=20und=20LoadCenterDelegate.java=20die=20das=20Design?= =?UTF-8?q?pattern=20"Delegate"=20umsetzen,=20um=20Coderedundanz=20zwische?= =?UTF-8?q?n=20Load.java=20und=20Unload.java=20zu=20beseitigen=20-=20neue?= =?UTF-8?q?=20sql-skripte=20im=20Modul=20ExternalLink=20oracle-se-drop.sql?= =?UTF-8?q?=20und=20postgres-drop.sql,=20zum=20L=C3=B6schung=20der=20Exter?= =?UTF-8?q?nalLink=20spezifischen=20Tabellen=20in=20der=20Datenbank=20-=20?= =?UTF-8?q?s=C3=A4mtliche=20Codemodifikationen=20und=20Java-Doc-Erg=C3=A4n?= =?UTF-8?q?zungen=20in=20beteiligten=20Klassen=20f=C3=BCr=20die=20unload-P?= =?UTF-8?q?rozedur=20-=20neue=20Klasse=20ExternalLinkUnloader.java=20(bish?= =?UTF-8?q?er=20noch=20wenig/keine=20Funktionalit=C3=A4t)=20und=20Abstract?= =?UTF-8?q?ContentTypeUnloader.java,=20die=20zusammen=20daf=C3=BCr=20sorge?= =?UTF-8?q?n=20sollen,=20die=20ContentTyp-Instanzen=20(z.B.=20von=20Extern?= =?UTF-8?q?alLink)=20aus=20den=20Tabellen=20der=20Datenbank=20zu=20entfern?= =?UTF-8?q?en.=20(->=20work=20in=20progress)=20-=20Erg=C3=A4nzung=20des=20?= =?UTF-8?q?unload-Datenskripts=20in=20ccm-cms-types-externallink.load=20-?= =?UTF-8?q?=20Erg=C3=A4nzung=20der=20build-ccm.xml=20um=20die=20unload-Bef?= =?UTF-8?q?ehle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [WORK IN PROGRESS] Das Entfernen von Instanzen derjenigen ContentTypes aus den Tabellen der Datenbank, die deinstalliert werden sollen über "ccm unload". git-svn-id: https://svn.libreccm.org/ccm/trunk@3414 8810af33-2d31-482b-a856-94f89814c4df --- .../oracle-se-drop.sql | 20 + .../postgres-drop.sql | 22 + .../src/ccm-cms-types-externallink.load | 1 + .../cms/contenttypes/ExternalLink.java | 6 +- .../contenttypes/ExternalLinkInitializer.java | 2 +- .../cms/contenttypes/ExternalLinkLoader.java | 4 +- .../contenttypes/ExternalLinkUnloader.java | 63 ++ .../ui/ExternalLinkPropertiesStep.java | 2 +- .../ui/ExternalLinkPropertyForm.java | 2 +- .../util/ExternalLinkGlobalizationUtil.java | 2 +- .../AbstractContentTypeLoader.java | 30 +- .../AbstractContentTypeUnloader.java | 170 +++ .../contenttypes/ContentTypeInitializer.java | 5 +- .../com/arsdigita/kernel/KernelExcursion.java | 1 + .../com/arsdigita/loader/PackageLoader.java | 13 +- .../src/com/arsdigita/packaging/Load.java | 977 +++++++++--------- .../com/arsdigita/packaging/LoadCenter.java | 107 ++ .../packaging/LoadCenterDelegate.java | 695 +++++++++++++ .../src/com/arsdigita/packaging/Loader.java | 211 +++- .../com/arsdigita/packaging/LoaderInfo.java | 38 +- .../com/arsdigita/packaging/MasterTool.java | 2 +- ccm-core/src/com/arsdigita/packaging/Set.java | 10 +- .../src/com/arsdigita/packaging/Unload.java | 363 ++++++- .../src/com/arsdigita/packaging/Upgrade.java | 8 +- .../com/arsdigita/runtime/AbstractScript.java | 1 + tools-ng/ecdc/scriptlib/build-ccm.xml | 59 +- 26 files changed, 2214 insertions(+), 600 deletions(-) create mode 100644 ccm-cms-types-externallink/sql/ccm-cms-types-externallink/oracle-se-drop.sql create mode 100644 ccm-cms-types-externallink/sql/ccm-cms-types-externallink/postgres-drop.sql create mode 100644 ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/ExternalLinkUnloader.java create mode 100644 ccm-cms/src/com/arsdigita/cms/contenttypes/AbstractContentTypeUnloader.java create mode 100644 ccm-core/src/com/arsdigita/packaging/LoadCenter.java create mode 100644 ccm-core/src/com/arsdigita/packaging/LoadCenterDelegate.java diff --git a/ccm-cms-types-externallink/sql/ccm-cms-types-externallink/oracle-se-drop.sql b/ccm-cms-types-externallink/sql/ccm-cms-types-externallink/oracle-se-drop.sql new file mode 100644 index 000000000..cdb23b3a5 --- /dev/null +++ b/ccm-cms-types-externallink/sql/ccm-cms-types-externallink/oracle-se-drop.sql @@ -0,0 +1,20 @@ +-- +-- Copyright (C) 2005 Red Hat Inc. 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 2.1 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; 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 library; if not, write to the Free Software +-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +-- +-- $Id: oracle-se-drop.sql 1 2015-04-15 13:47:30Z tosmers $ +@ ddl/oracle-se/drop-constraints.sql +@ ddl/oracle-se/drop-tables.sql \ No newline at end of file diff --git a/ccm-cms-types-externallink/sql/ccm-cms-types-externallink/postgres-drop.sql b/ccm-cms-types-externallink/sql/ccm-cms-types-externallink/postgres-drop.sql new file mode 100644 index 000000000..2a0d6023f --- /dev/null +++ b/ccm-cms-types-externallink/sql/ccm-cms-types-externallink/postgres-drop.sql @@ -0,0 +1,22 @@ +-- +-- Copyright (C) 2005 Red Hat Inc. 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 2.1 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; 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 library; if not, write to the Free Software +-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +-- +-- $Id: postgres-drop.sql 1 2015-04-15 13:47:30Z tosmers $ +begin; +\i ddl/postgres/drop-constraints.sql +\i ddl/postgres/drop-tables.sql +end; \ No newline at end of file diff --git a/ccm-cms-types-externallink/src/ccm-cms-types-externallink.load b/ccm-cms-types-externallink/src/ccm-cms-types-externallink.load index eeb52f55e..ce3052146 100644 --- a/ccm-cms-types-externallink/src/ccm-cms-types-externallink.load +++ b/ccm-cms-types-externallink/src/ccm-cms-types-externallink.load @@ -12,5 +12,6 @@ + diff --git a/ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/ExternalLink.java b/ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/ExternalLink.java index 9b9657994..459deafef 100644 --- a/ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/ExternalLink.java +++ b/ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/ExternalLink.java @@ -33,7 +33,7 @@ import java.math.BigDecimal; * * The item stores a description text about the link/resource and an URL. * - * @author tosmers + * @author Tobias Osmers * @version $Revision: #1 $ $Date: 2015/02/22 $ */ public class ExternalLink extends ContentPage { @@ -105,7 +105,7 @@ public class ExternalLink extends ContentPage { /** * Creates a new domain object for an subtype of ExternalLink. * - * @param type The subtype for witch a new domain object will be created. + * @param type The subtype for which a new domain object will be created. */ public ExternalLink(final String type) { super(type); @@ -159,7 +159,7 @@ public class ExternalLink extends ContentPage { /** * Set the value weather the comment should be shown. * - * @param showComment The value weather the comment should be shown. + * @param show The value weather the comment should be shown. */ public void setShowComment(final String show) { set(SHOW_COMMENT, show); diff --git a/ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/ExternalLinkInitializer.java b/ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/ExternalLinkInitializer.java index fcb7fe8fe..43a8feafe 100644 --- a/ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/ExternalLinkInitializer.java +++ b/ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/ExternalLinkInitializer.java @@ -29,7 +29,7 @@ import com.arsdigita.cms.contenttypes.ContentTypeInitializer; * This is done by runtimeRuntime startup method which runs the init() methods * of all initializers (this one just using the parent implementation). * - * @author tosmers + * @author Tobias Osmers * @version $Revision: #1 $ $Date: 2015/02/22 $ */ public class ExternalLinkInitializer extends ContentTypeInitializer { diff --git a/ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/ExternalLinkLoader.java b/ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/ExternalLinkLoader.java index 820b2bb94..2fa0402fb 100644 --- a/ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/ExternalLinkLoader.java +++ b/ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/ExternalLinkLoader.java @@ -39,7 +39,7 @@ import java.io.InputStream; * from AbstractConfig). They will (and can) not be persisted into * an registry object (file). * - * @author tosmers + * @author Tobias Osmers * @version $Revision: #1 $ $Date: 2015/02/22 $ */ public class ExternalLinkLoader extends AbstractContentTypeLoader { @@ -68,7 +68,7 @@ public class ExternalLinkLoader extends AbstractContentTypeLoader { } /** - * Provides the of ExternalLink contenttype property definitions. + * Provides the ExternalLink's contenttype property definitions. * * The file defines the types name as displayed in content center * select box and the authoring steps. These are loaded into database. diff --git a/ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/ExternalLinkUnloader.java b/ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/ExternalLinkUnloader.java new file mode 100644 index 000000000..d04f1003d --- /dev/null +++ b/ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/ExternalLinkUnloader.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2015 University of Bremen. 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 2.1 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; 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 library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.cms.contenttypes; + +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author Tobias Osmers + * @version $Revision: #1 $ $Date: 2015/04/08 $ + */ +public class ExternalLinkUnloader extends AbstractContentTypeUnloader { + + /** + * Defines the xml file containing the ExternalLink content types + * property definitions. + */ + private static final String[] TYPES = { + "/WEB-INF/content-types/com/arsdigita/cms/contenttypes/ExternalLink.xml" + }; + + /** + * Provides the ExternalLink's contenttype property definitions. + * + * The file defines the types name as displayed in content center + * select box and the authoring steps. These are loaded into database. + * + * Implements the method of the parent class. + * + * @return String array of fully qualified file names + */ + @Override + public String[] getTypes() { + return TYPES; + } + + /** + * ?????????? + * + * @return + */ + public List getAllInstances() { + return new ArrayList(); + } + +} diff --git a/ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/ui/ExternalLinkPropertiesStep.java b/ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/ui/ExternalLinkPropertiesStep.java index 9e2ec00f9..f9a132e14 100644 --- a/ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/ui/ExternalLinkPropertiesStep.java +++ b/ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/ui/ExternalLinkPropertiesStep.java @@ -33,7 +33,7 @@ import com.arsdigita.toolbox.ui.DomainObjectPropertySheet; * Authoring step to view/edit the simple attributes of the * ExternalLink content type (and its subclasses). * - * @author tosmers + * @author Tobias Osmers * @version $Revision: #1 $ $Date: 2015/02/22 $ */ public class ExternalLinkPropertiesStep extends SimpleEditStep { diff --git a/ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/ui/ExternalLinkPropertyForm.java b/ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/ui/ExternalLinkPropertyForm.java index 4c4a09655..b804a41e8 100644 --- a/ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/ui/ExternalLinkPropertyForm.java +++ b/ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/ui/ExternalLinkPropertyForm.java @@ -41,7 +41,7 @@ import com.arsdigita.cms.ui.authoring.BasicPageForm; *
* This form can be extended to create forms for ExternalLink subclasses. * - * @author tosmers + * @author Tobias Osmers * @version $Revision: #1 $ $Date: 2015/02/22 $ */ public class ExternalLinkPropertyForm extends BasicPageForm diff --git a/ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/util/ExternalLinkGlobalizationUtil.java b/ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/util/ExternalLinkGlobalizationUtil.java index 1024cd94f..1dea95dd3 100644 --- a/ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/util/ExternalLinkGlobalizationUtil.java +++ b/ccm-cms-types-externallink/src/com/arsdigita/cms/contenttypes/util/ExternalLinkGlobalizationUtil.java @@ -28,7 +28,7 @@ import com.arsdigita.globalization.GlobalizedMessage; * globalize methods and forwards to GlobalizedMessage, shortening the * method invocation in the various application classes. * - * @author tosmers + * @author Tobias Osmers * @version $Revision: #1 $ $Date: 2015/02/22 $ */ public class ExternalLinkGlobalizationUtil implements Globalized { diff --git a/ccm-cms/src/com/arsdigita/cms/contenttypes/AbstractContentTypeLoader.java b/ccm-cms/src/com/arsdigita/cms/contenttypes/AbstractContentTypeLoader.java index 4724e25b4..257509491 100755 --- a/ccm-cms/src/com/arsdigita/cms/contenttypes/AbstractContentTypeLoader.java +++ b/ccm-cms/src/com/arsdigita/cms/contenttypes/AbstractContentTypeLoader.java @@ -65,19 +65,21 @@ import org.apache.log4j.Logger; */ public abstract class AbstractContentTypeLoader extends PackageLoader { - /** Internal logger instance to faciliate debugging. Enable logging output - * by editing /WEB-INF/conf/log4j.properties int hte runtime environment - * and set com.arsdigita.cms.contenttypes.AbstractContentTypeLoader=DEBUG - * by uncommenting or adding the line. */ + /** + * Internal logger instance to faciliate debugging. Enable logging output + * by editing /WEB-INF/conf/log4j.properties int hte runtime environment + * and set com.arsdigita.cms.contenttypes.AbstractContentTypeLoader=DEBUG + * by uncommenting or adding the line. + */ private static final Logger s_log = Logger.getLogger( - AbstractContentTypeLoader.class); + AbstractContentTypeLoader.class); /** * The run method is invoked to execute the loader step. Before calling * this method any required parameters registered by the noargs * constructer should be set. * - * Overwrites the parent's class abstract method adding the tast specific + * Overwrites the parent's class abstract method adding the task specific * createTypes() method. * * @param ctx @@ -97,8 +99,12 @@ public abstract class AbstractContentTypeLoader extends PackageLoader { } /** + * Parses the content-types specified in the "contentType".xml-file and + * stores them into a list. Then retrieves all content-sections into + * a dataCollection and adds all the content-types stored in the list + * to the sections in that dataCollection. * - * @param ctx + * @param ctx The context to the unload-script */ private void createTypes(ScriptContext ctx) { @@ -111,13 +117,12 @@ public abstract class AbstractContentTypeLoader extends PackageLoader { List types = handler.getContentTypes(); Session ssn = ctx.getSession(); - DataCollection sections = ssn.retrieve(ContentSection - .BASE_DATA_OBJECT_TYPE); + DataCollection sections = ssn.retrieve( + ContentSection.BASE_DATA_OBJECT_TYPE); while (sections.next()) { ContentSection section = (ContentSection) - DomainObjectFactory.newInstance( - sections.getDataObject()); + DomainObjectFactory.newInstance(sections.getDataObject()); if (!isLoadableInto(section)) { continue; } @@ -176,7 +181,8 @@ public abstract class AbstractContentTypeLoader extends PackageLoader { * content types's. * Must be implemented by each content type loader to provide its * specific definition files. - * @return + * + * @return This content type's property definitions through the ".xml"-file */ protected abstract String[] getTypes(); diff --git a/ccm-cms/src/com/arsdigita/cms/contenttypes/AbstractContentTypeUnloader.java b/ccm-cms/src/com/arsdigita/cms/contenttypes/AbstractContentTypeUnloader.java new file mode 100644 index 000000000..3a36b6d73 --- /dev/null +++ b/ccm-cms/src/com/arsdigita/cms/contenttypes/AbstractContentTypeUnloader.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2003-2004 Red Hat Inc. 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 2.1 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; 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 library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.cms.contenttypes; + +import com.arsdigita.cms.ContentSection; +import com.arsdigita.cms.ContentType; +import com.arsdigita.domain.DomainObjectFactory; +import com.arsdigita.kernel.Kernel; +import com.arsdigita.kernel.KernelExcursion; +import com.arsdigita.loader.PackageLoader; +import com.arsdigita.persistence.DataCollection; +import com.arsdigita.persistence.Session; +import com.arsdigita.runtime.ScriptContext; +import com.arsdigita.xml.XML; +import java.util.Iterator; +import java.util.List; +import org.apache.log4j.Logger; + +/** + * + * + * @author Tobias Osmers + * @version $Revision: #1 $ $Date: 2015/05/18 $ + */ +public abstract class AbstractContentTypeUnloader extends PackageLoader { + /** + * Internal logger instance to faciliate debugging. Enable logging output + * by editing /WEB-INF/conf/log4j.properties int hte runtime environment + * and set com.arsdigita.cms.contenttypes.AbstractContentTypeLoader=DEBUG + * by uncommenting or adding the line. + */ + private static final Logger s_log = Logger.getLogger( + AbstractContentTypeLoader.class); + + /** + * The run method is invoked to execute the unloader step. Before calling + * this method any required parameters registered by the noargs + * constructer should be set. + * + * Overwrites the parent's class abstract method adding the task specific + * sweep() method. + * + * @param ctx The context to the unload-script + */ + @Override + public void run(final ScriptContext ctx) { + new KernelExcursion() { + + @Override + protected void excurse() { + setEffectiveParty(Kernel.getSystemParty()); + + sweepTypes(ctx); + + } + }.run(); + } + + /** + * Parses the content-types specified in the "contentType".xml-file and + * stores them into a list. Then retrieves all content-sections into + * a dataCollection and removes all the content-types stored in the list + * from the sections in that dataCollection. + * + * @param ctx The context to the unload-script + */ + private void sweepTypes(ScriptContext ctx) { + XMLContentTypeHandler handler = new XMLContentTypeHandler(); + // Retrieve the content type definition file(s) + String[] contentTypes = getTypes(); + for (String contentType : contentTypes) { + XML.parseResource(contentType, handler); + } + + List types = handler.getContentTypes(); + Session ssn = ctx.getSession(); + DataCollection sections = ssn.retrieve( + ContentSection.BASE_DATA_OBJECT_TYPE); + + while (sections.next()) { + ContentSection section = (ContentSection) + DomainObjectFactory.newInstance(sections.getDataObject()); + if (!isLoadableInto(section)) { + continue; + } + + for (Iterator it = types.iterator(); it.hasNext();) { + final ContentType type = (ContentType) it.next(); + + //Is the order important?? (here: same as in load step) + section.removeContentType(type); + } + } + //TODO: still to be implemented + } + + /** + * Provides a list of contenttype property definitions. + * + * In the file there are definitions of the type's name as displayed in + * content center select box and the authoring steps. These are loaded into + * database. + * + * It is a XML file and by convention named after the content type or the + * module's base name which implements one or more content types. It is + * usually something like + *
+     * "/WEB-INF/content-types/com/arsdigita/cms/contenttypes/Event.xml"
+     * 
+ * The path is fixed by convention and the name is the same as the + * content types's. + * Must be implemented by each content type loader to provide its + * specific definition files. + * + * @return This content type's property definitions through the ".xml"-file + */ + protected abstract String[] getTypes(); + + /** + * Checks, if its possible to load into the given section. + * + * @param The section to be checked + * @return true, if its possible to load into the section, otherwise false + */ + private boolean isLoadableInto(ContentSection section) { + if (section == null) { + throw new NullPointerException("section"); + } + + if (getContentSections().size() > 0) { + return getContentSections().contains(section.getName()); + } else { + return ContentSection.getConfig().getDefaultContentSection(). + equals(section.getName()); + } + } + + /** + * Returns a list of content sections into which the content type should be + * installed. + * + *

If this returns an empty list, then the content type will be loaded + * into the section specified by {@link + * com.arsdigita.cms.ContentSectionConfig#get + * @return DefaultContentSection()}.

+ * + *

The default implementation returns an empty list.

+ * + * @post return != null + */ + protected List getContentSections() { + return java.util.Collections.EMPTY_LIST; + } +} diff --git a/ccm-cms/src/com/arsdigita/cms/contenttypes/ContentTypeInitializer.java b/ccm-cms/src/com/arsdigita/cms/contenttypes/ContentTypeInitializer.java index 18f8bef87..4804f8cf0 100755 --- a/ccm-cms/src/com/arsdigita/cms/contenttypes/ContentTypeInitializer.java +++ b/ccm-cms/src/com/arsdigita/cms/contenttypes/ContentTypeInitializer.java @@ -116,8 +116,7 @@ public abstract class ContentTypeInitializer extends CompoundInitializer { new ContentPageMetadataProvider()); final String[] stylesheets = getStylesheets(); - for (int i = 0; i < stylesheets.length; i++) { - String stylesheet = stylesheets[i]; + for (String stylesheet : stylesheets) { ContentType.registerXSLFile(type, stylesheet); } } catch (com.arsdigita.domain.DataObjectNotFoundException e) { @@ -135,7 +134,7 @@ public abstract class ContentTypeInitializer extends CompoundInitializer { * Has to be overwritten by each specific content type to provide its * TraversalXML if it uses one. * - * @return Fully qualified file name (relative to docuemnt / context root) + * @return Fully qualified file name (relative to document / context root) * to traversal adapter. */ public String getTraversalXML() { diff --git a/ccm-core/src/com/arsdigita/kernel/KernelExcursion.java b/ccm-core/src/com/arsdigita/kernel/KernelExcursion.java index 3b47bded1..51c99e072 100755 --- a/ccm-core/src/com/arsdigita/kernel/KernelExcursion.java +++ b/ccm-core/src/com/arsdigita/kernel/KernelExcursion.java @@ -46,6 +46,7 @@ public abstract class KernelExcursion implements Runnable { private static final Logger s_log = Logger.getLogger (KernelExcursion.class); + @Override public final void run() { s_log.debug("Running excursion"); diff --git a/ccm-core/src/com/arsdigita/loader/PackageLoader.java b/ccm-core/src/com/arsdigita/loader/PackageLoader.java index ad1d16cd3..16916de37 100755 --- a/ccm-core/src/com/arsdigita/loader/PackageLoader.java +++ b/ccm-core/src/com/arsdigita/loader/PackageLoader.java @@ -54,6 +54,14 @@ public abstract class PackageLoader extends AbstractScript { private final static Logger s_log = Logger.getLogger(PackageLoader.class); + /** + * Checks if the given table exists in the database specified + * by the given connection. + * + * @param conn The specified connection to the database + * @param table The table name + * @return true if the table exists, otherwise false + */ public static boolean exists(Connection conn, String table) { try { DatabaseMetaData md = conn.getMetaData(); @@ -100,6 +108,7 @@ public abstract class PackageLoader extends AbstractScript { public static void load(Connection conn, String script) { SQLLoader loader = new SQLLoader(conn) { + @Override protected Reader open(String name) { String resourceName = name.replace('\\', '/'); ClassLoader cload = getClass().getClassLoader(); @@ -128,8 +137,8 @@ public abstract class PackageLoader extends AbstractScript { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); Writer w = new OutputStreamWriter(baos); - for (int i = 0; i < args.length; i++) { - w.write(args[i]); + for (String arg : args) { + w.write(arg); w.write("\n"); } diff --git a/ccm-core/src/com/arsdigita/packaging/Load.java b/ccm-core/src/com/arsdigita/packaging/Load.java index cc54641bf..6f7e3807e 100755 --- a/ccm-core/src/com/arsdigita/packaging/Load.java +++ b/ccm-core/src/com/arsdigita/packaging/Load.java @@ -19,19 +19,12 @@ package com.arsdigita.packaging; import com.arsdigita.loader.PackageLoader; -import com.arsdigita.persistence.ConnectionSource; -import com.arsdigita.persistence.DedicatedConnectionSource; -import com.arsdigita.persistence.OID; import com.arsdigita.persistence.Session; import com.arsdigita.persistence.SessionManager; -import com.arsdigita.persistence.metadata.MetadataRoot; -import com.arsdigita.persistence.pdl.PDLCompiler; import com.arsdigita.runtime.InteractiveParameterReader; import com.arsdigita.runtime.ConfigRegistry; -import com.arsdigita.runtime.RegistryConfig; import com.arsdigita.runtime.RuntimeConfig; import com.arsdigita.runtime.Runtime; -import com.arsdigita.util.UncheckedWrapperException; import com.arsdigita.util.JavaPropertyReader; import com.arsdigita.util.jdbc.Connections; import com.arsdigita.util.parameter.CompoundParameterReader; @@ -39,26 +32,15 @@ import com.arsdigita.util.parameter.Parameter; import com.arsdigita.util.parameter.ParameterContext; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.Writer; import java.sql.Connection; import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; import java.util.List; -import java.util.Map; import java.util.Properties; -import java.util.Scanner; -import java.util.Set; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.Options; @@ -114,14 +96,17 @@ import org.apache.log4j.Logger; * May be necessary for an update which contains new modules already configured * in the registry but missing in the (old) database. * - * @author Rafael H. Schloming <rhs@mit.edu> + * @author Tobias Osmers + * @version $Revision: #3 $ $Date: 2015/04/27 $ */ +class Load extends Command implements LoadCenter { -class Load extends Command { - + private static final LoadCenterDelegate delegate = new LoadCenterDelegate(); + private static final Logger logger = Logger.getLogger(Load.class); private static final Options OPTIONS = getOptions(); + //Initializes all available option-flags in an option-set static { logger.debug("Static initalizer starting..."); OPTIONS.addOption @@ -204,11 +189,14 @@ class Load extends Command { /** * Invoked from the central tool "MasterTool" to execute the load process. * - * @param args - * @return + * @param args The parameters and option-flags + * @return true if successful, false otherwise */ + @Override public boolean run(String[] args) { + //Takes the option-set and the arguments and parses + //them into a command-line CommandLine line; try { line = new PosixParser().parse(OPTIONS, args); @@ -217,153 +205,84 @@ class Load extends Command { return false; } - // fill with command line arguments which is a list of packages by their - // package-keys to load. - List packages = line.getArgList(); - - if (line.hasOption("packagekeys-file")) { - String file = line.getOptionValue("packagekeys-file"); - logger.debug("File with package keys: " + file ); - try { - Scanner sc = new Scanner(new File(file)); - while (sc.hasNext()) { - packages.add(sc.next()); - } - } catch (IOException e) { - System.err.println(e.getMessage()); - return false; - } - } - - if (packages.isEmpty()) { - usage(OPTIONS, System.err, "PACKAGE-KEYS"); - return false; - } - - - + //[--usage || --help] + //If set, prints an info-output and returns the function + //with true if (line.hasOption("usage") || line.hasOption("help")) { usage(OPTIONS, System.out, "PACKAGE-KEYS"); return true; } - - final boolean all = !(line.hasOption("config") - || line.hasOption("schema") - || line.hasOption("data") - || line.hasOption("init")); - - // RegistryConfig contains a list of package-keys of loaded packages - RegistryConfig rc = new RegistryConfig(); // Create a new (empty) config object. - rc.load(); // Load config values from file - List loaded = Arrays.asList(rc.getPackages()); // retrieve list of installed packages - - Map loaders = new HashMap(); - List keys = new ArrayList(); - keys.addAll(packages); // from command line parameters packages to be installed - boolean err = false; - while (!keys.isEmpty()) { - String key = (String) keys.remove(0); - if (loaders.containsKey(key)) { continue; } - Loader l = Loader.get(key); - if (l == null) { - System.err.println("unable to locate package: " + key); - err = true; - } else { - loaders.put(key, l); - if (line.hasOption("recursive")) { - keys.addAll(l.getInfo().getRequiredPackages()); - } - } - } - if (err) { return false; } - - Loader[] sorted = (Loader[]) loaders.values().toArray - (new Loader[loaders.size()]); - sort(sorted); - - if (all) { - List missing = new ArrayList(); - addTo(missing, getRequiredPackages(sorted)); - missing.removeAll(getProvidedPackages(sorted)); - missing.removeAll(loaded); - List conflicts = new ArrayList(loaded); - conflicts.retainAll(getProvidedPackages(sorted)); - if (!missing.isEmpty()) { - System.err.println("required packages: " + missing); - } - if (!conflicts.isEmpty()) { - System.err.println("conflicting packages: " + conflicts); - } - if (missing.size() + conflicts.size() > 0) { + + //Gets all packages which will be unloaded and assures + //that this list is not empty. + List packages; + try { + packages = getAllPackages(line); + if (packages.isEmpty()) { + usage(OPTIONS, System.err, "PACKAGE-KEYS"); return false; } + } catch (IOException e) { + System.err.println(e.getMessage()); + return false; + } + + //Gets all unloaders corresponding to the packages which + //will be unloaded. + Loader[] loaders; + try { + loaders = getAllLoaders(line, packages, LoadType.LOAD); + } catch (Error e) { + System.err.println(e.getMessage()); + return false; } - ParameterMap contexts = new ParameterMap(); + //Determines if all steps have to be performed. + final boolean all = hasAllOptions(line); + + //Checks that there are no missing or conflicting packages. + if (!noMissingAndConflictingPackages(loaders, all)) { + return false; + } - Properties parameters = new Properties(); + //Gets all parameters set in a file or given to by the + //agrument line and adds the parameters to the cpr. CompoundParameterReader cpr = new CompoundParameterReader(); - if (line.hasOption("parameter-file")) { - String file = line.getOptionValue("parameter-file"); - try { - InputStream fis = new FileInputStream(file); - parameters.load(fis); - fis.close(); - } catch (IOException e) { - System.err.println(e.getMessage()); - return false; - } - // deprecated, use JavaPropertyReader instead - // cpr.add(new JavaPropertyLoader(parameters)); + Properties parameters = new Properties(); + try { + getAllParameters(line, parameters); cpr.add(new JavaPropertyReader(parameters)); + } catch (IOException e) { + System.err.println(e.getMessage()); + return false; } - if (line.hasOption("parameters")) { - Properties props = props(line.getOptionValues("parameters")); - // deprecated, use JavaPropertyReader instead - // cpr.add(new JavaPropertyLoader(props)); - cpr.add(new JavaPropertyReader(props)); - parameters.putAll(props); - } + + //Adds an appropriate entry to the cpr for interactive + //parameter reading. if (line.hasOption("interactive")) { cpr.add(new InteractiveParameterReader(System.in, System.out)); } Config config = null; + try { - if (all || line.hasOption("config")) { - ConfigRegistry reg = new ConfigRegistry(); - for (int i = 0; i < sorted.length; i++) { - if (!reg.getPackages().contains(sorted[i].getKey())) { - reg.initialize(sorted[i].getKey()); - } - } - config = new Config(reg); - config.load(System.err); + //Initializes all packages in the registry and creates + //a configuration. + config = getConfig(line, loaders, config, all); + //Collects the configuration-parameters and data-scripts + //in the contexts + ParameterMap contexts = getContexts(line, loaders, config, all); - Parameter param = config.getParameter("waf.config.packages"); - ParameterContext ctx = config.getContainer(param); - String[] pkgs = (String[]) ctx.get(param); - for (int i = 0; i < sorted.length; i++) { - if (!contains(pkgs, sorted[i].getKey())) { - pkgs = concat(pkgs, new String[] { sorted[i].getKey() }); - } - } - ctx.set(param, pkgs); - - contexts.addContexts(config.getContexts()); - } - - if (all || line.hasOption("data")) { - for (int i = 0; i < sorted.length; i++) { - contexts.addContexts(sorted[i].getScripts()); - } - } - - if (!contexts.load(new JavaPropertyReader(parameters), System.err)) { + //Loads the collected configuration-parameters and data- + //scripts. Saves the configuration. + if (!contexts.load(new JavaPropertyReader(parameters), System.err) || + !saveConfig(config)) { return false; } + //Creates a parameter-editor which guides through the steps + //of setting config key values. if (line.hasOption("interactive")) { ParameterEditor editor = new ParameterEditor(contexts, System.in, System.out); @@ -372,240 +291,282 @@ class Load extends Command { return false; } - if (!saveConfig(config)) { - return false; - } - Session ssn = null; + + //All --schema and --data specific tasks. if (all || line.hasOption("schema") || line.hasOption("data")) { - Check checkdb = new CheckDB(); - checkdb.run(null); - if (checkdb.getStatus() == null - || checkdb.getStatus().equals(Check.FAIL)) { + //Checks for existence and accessibility of a database. + if (!checkDatabase()) { + rollbackConfig(config, packages); + return false; + } + + //Opens/aquires a connection to the database. + Connection conn = + Connections.acquire(RuntimeConfig.getConfig().getJDBCURL()); + + //Checks the schema, looks for missing or conflicting + //tables and checks the initializers + if (!checkSchema(line, loaders, all) + || !noMissingAndConflictingTables(line, loaders, conn, all) + || !checkInits(line, loaders, conn, all)) { + rollbackConfig(config, packages); + return false; + } + + //Loads the schema. + try { + loadSchema(line, loaders, conn, all); + } catch (SQLException e) { + System.err.println(e.getMessage()); rollbackConfig(config,packages); return false; } - if (all || line.hasOption("schema")) { - boolean passed = true; - for (int i = 0; i < sorted.length; i++) { - passed &= sorted[i].checkSchema(); - } - if (!passed) { - rollbackConfig(config,packages); - return false; - } - } - - Connection conn = - Connections.acquire(RuntimeConfig.getConfig().getJDBCURL()); - - List required = new ArrayList(); - addTo(required, getRequiredTables(sorted)); - List provided = new ArrayList(); - - if (all || line.hasOption("schema")) { - required.removeAll(getProvidedTables(sorted)); - addTo(provided, getProvidedTables(sorted)); - } else if (line.hasOption("data")) { - addTo(required, getProvidedTables(sorted)); - } - - List missing = getMissing(conn, required); - List conflicts = getConflicts(conn, provided); - - if (!missing.isEmpty()) { - System.err.println("required tables: " + missing); - } - - if (!conflicts.isEmpty()) { - System.err.println("conflicting tables (already exist): " + - conflicts); - } - - if (conflicts.size() > 0 || missing.size() > 0) { + //Checks and loads the data. + if (!checkAndLoadData(line, loaders, ssn, all, cpr)) { rollbackConfig(config,packages); return false; } - - if (PackageLoader.exists(conn, "inits") - && (line.hasOption("init") || all)) { - final boolean success = checkInitializerDependencies - (sorted); - - if (!success) { - rollbackConfig(config,packages); - return false; - } - } - - if (all || line.hasOption("schema")) { - for (int i = 0; i < sorted.length; i++) { - sorted[i].loadSchema(conn); - } - try { - conn.commit(); - } catch (SQLException e) { - System.err.println(e.getMessage()); - rollbackConfig(config,packages); - return false; - } - } - - - if (all || line.hasOption("data")) { - if (ssn == null) { - new Runtime().startup(); - ssn = SessionManager.getSession(); - } - - boolean passed = true; - for (int i = 0; i < sorted.length; i++) { - passed &= sorted[i].checkData(ssn); - } - if (!passed) { - rollbackConfig(config,packages); - return false; - } - - for (int i = 0; i < sorted.length; i++) { - sorted[i].loadData(ssn, cpr); - } - } } + + //All --init specific tasks. if (all || line.hasOption("init")) { - if (ssn == null) { - new Runtime().startup(); - ssn = SessionManager.getSession(); - } - for (int i = 0; i < sorted.length; i++) { - sorted[i].loadInits(ssn); - } + loadInits(loaders, ssn); } } catch (Throwable t) { t.printStackTrace(System.err); rollbackConfig(config,packages); return false; } + return true; } - private boolean checkInitializerDependencies(final Loader[] sorted) { - final List required = new ArrayList(); - final List provided = new ArrayList(); - addTo(required, getRequiredInitializers(sorted)); - required.removeAll(getProvidedInitializers(sorted)); - addTo(provided, getProvidedInitializers(sorted)); - - final Session boot = session(); - final List missing = getMissing(boot, required); - final List conflicts = getConflicts(boot, provided); - - if (!missing.isEmpty()) { - System.err.println("required initializers: " + missing); - return false; - } - - if (!conflicts.isEmpty()) { - System.err.println("conflicting initializers: " + conflicts); - return false; - } - - return true; + /** + * Gets all packages to be unloaded either from the command-line or a file, + * if the option flag [--packagekeys-file FILE] has been set and puts them + * in a list of packages (by their package-keys). + * + * @param line The command-line with all options and arguments + * @return The list of packages to be unloaded + * @throws IOException + */ + @Override + public List getAllPackages(CommandLine line) throws IOException { + return delegate.getAllPackages(line); } - - private boolean saveConfig(Config config) { - if (config != null) { - try { - config.save(); - } catch (IOException e) { - System.err.println(e.getMessage()); - return false; - } - } - return true; + + /** + * Gets all loaders to the given package-list and sorts them before re- + * turning. Creates a map that assigns to every package-key an equivalent + * loader. This loader contains a bunch of informations (required, + * provided, scripts) provided by an ".load"-file. Then all loaders from + * the map (pkg-key -> loader) are composed into an array of loaders and + * sorted. + * + * @param line The command-line with all options and arguments + * @param packages The list of packages to be loaded + * @param loadType Weather packages are been loaded or unloaded + * @return A sorted list of loaders + * @throws Error + */ + @Override + public Loader[] getAllLoaders(CommandLine line, List packages, + LoadType loadType) throws Error { + return delegate.getAllLoaders(line, packages, loadType); } - - private void rollbackConfig(Config config, List packages) { - if (config != null) { - Parameter param = config.getParameter("waf.config.packages"); - ParameterContext ctx = config.getContainer(param); - String[] pkgs = (String[]) ctx.get(param); - LinkedList original = new LinkedList(); - for (int i = 0; i < pkgs.length; i++) { - boolean isnew = false; - for (int j = 0; j < packages.size(); j++) { - // Operator == compares object identity. - // comparison here refers to package names, so an - // object comparison will never be true. - // instead: equals() - // if (pkgs[i].toString() == packages.get(j).toString()) { - if (pkgs[i].toString().equals(packages.get(j).toString())) { - isnew = true; - } - } - if (!isnew) { - original.add(pkgs[i]); + + /** + * Determines if all steps (config, schema, data, inits) have to be + * performed. + * + * @param line The command-line with all options and arguments + * @return True if all options need to be performed + */ + @Override + public boolean hasAllOptions(CommandLine line) { + return delegate.hasAllOptions(line); + } + + /** + * Creates a new (empty) config object and loads config values from a file. + * Then retrieves a list of installed packages. RegistryConfig contains a + * list of package-keys of loaded packages. + * + * If all steps need to be performed, it checks if there are no more missing + * or conflicting packages. Missing means, that a package required by a + * soon-to-be-loaded-package, has not been loaded yet. Conflicting means, + * that a package which will soon be provided by a soon-to-be-loaded-package + * has already been loaded. Either way leads to a problem, therefore + * returning false. + * @param loaders The loaders to the packages being loaded + * @param all Weather all steps (config, schema, data, inits) must be + * performed + * @return True if there are no missing or conflicting packages, otherwise + * false + */ + private boolean noMissingAndConflictingPackages(Loader[] loaders, + boolean all) { + return delegate.noMissingAndConflictingPackages(loaders, all); + } + + /** + * Collects all parameters either set in a file or given per line-argument + * and stores them in a parameter-variable. + * + * @param line The command-line with all options and arguments + * @param parameters The properties to store the collected parameters + * @throws IOException + */ + private static void getAllParameters(CommandLine line, Properties parameters) + throws IOException { + if (line.hasOption("parameter-file")) { + String file = line.getOptionValue("parameter-file"); + InputStream fis = new FileInputStream(file); + parameters.load(fis); + fis.close(); + } + if (line.hasOption("parameters")) { + Properties params = argsToProperties(line.getOptionValues("parameters")); + parameters.putAll(params); + } + } + + /** + * [SUPPORT] + * Converts a list of parameter-arguments into properties. + * + * used in: getAllParameters, + * Set.java -> run + * + * @param args List of parameter arguments + * @return A Properties-Object + * @throws IOException + */ + public static Properties argsToProperties(String[] args) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Writer w = new OutputStreamWriter(baos); + for (String arg : args) { + w.write(arg); + w.write("\n"); + } + w.flush(); + Properties params = new Properties(); + params.load(new ByteArrayInputStream(baos.toByteArray())); + return params; + } + + /** + * Initializes all package-keys in the registry and creates a specified + * configuration. + * + * @param line The command-line with all options and arguments + * @param loaders The loaders to the packages being loaded + * @param config The configuration-file + * @param all Weather all steps (config, schema, data, inits) must be + * performed + * @return A configuration for all packages + */ + private static Config getConfig(CommandLine line, Loader[] loaders, Config + config, boolean all) { + if (all || line.hasOption("config")) { + ConfigRegistry reg = new ConfigRegistry(); + for (Loader loader : loaders) { + if (!reg.getPackages().contains(loader.getKey())) { + reg.initialize(loader.getKey()); } } - String[] orig = new String[original.size()]; - for (int i = 0; i < original.size(); i++) { - orig[i] = (String)original.get(i); - } - ctx.set(param, orig); - saveConfig(config); + config = new Config(reg); } + return config; } - - private static List getMissing(Connection conn, List tables) { - List missing = new ArrayList(); - for (Iterator it = tables.iterator(); it.hasNext(); ) { - String table = (String) it.next(); - if (!PackageLoader.exists(conn, table)) { - missing.add(table); + + + + /** + * Collects the contexts in a parameter-map. First setting packages in + * a parameter-context and adding this parameter-context to the parameter- + * map. Second adding all data-scripts to the context in the parameter- + * map. + * + * @param line The command-line with all options and arguments + * @param loaders The loaders to the packages being loaded + * @param config The configuration-file + * @param all Weather all steps (config, schema, data, inits) must be + * performed + * @return The context in a parameter-map + */ + private static ParameterMap getContexts(CommandLine line, Loader[] + loaders, Config config, boolean all) { + ParameterMap contexts = new ParameterMap(); + if (all || line.hasOption("config")) { + setParameterContext(loaders, config); + //Adds the configuration to the context + contexts.addContexts(config.getContexts()); + } + if (all || line.hasOption("data")) { + for (Loader loader : loaders) { + //Adds the data-scripts to the context + contexts.addContexts(loader.getScripts()); } } - return missing; + return contexts; } - - private static List getConflicts(Connection conn, List tables) { - List conflicts = new ArrayList(); - for (Iterator it = tables.iterator(); it.hasNext(); ) { - String table = (String) it.next(); - if (PackageLoader.exists(conn, table)) { - conflicts.add(table); + + /** + * [SUPPORT] + * Sets all packages from the loaders-list in a configuration-file to the + * key "waf.config.packages". + * + * used in: getContexts + * + * @param loaders The loaders to the packages being loaded + * @param config The configuration-file + */ + private static void setParameterContext(Loader[] loaders, Config config) { + config.load(System.err); + Parameter param = config.getParameter("waf.config.packages"); + ParameterContext ctx = config.getContainer(param); + String[] pkgs = (String[]) ctx.get(param); + for (Loader loader : loaders) { + if (!contains(pkgs, loader.getKey())) { + pkgs = concat(pkgs, new String[]{loader.getKey()}); } } - return conflicts; + ctx.set(param, pkgs); } - - private static final String INIT = "com.arsdigita.runtime.Initializer"; - - private static List getMissing(Session ssn, List inits) { - List missing = new ArrayList(); - for (Iterator it = inits.iterator(); it.hasNext(); ) { - String init = (String) it.next(); - OID oid = new OID(ssn.getMetadataRoot().getObjectType(INIT), init); - if (ssn.retrieve(oid) == null) { - missing.add(init); + + /** + * [SUPPORT] + * Checks if a String-array contains a certain String. + * + * used in: setParameterContext + * + * @param array The String-array + * @param str The String to be checked of being contained by the array + * @return True on success, otherwise false + */ + private static boolean contains(String[] array, String str) { + for (String s : array) { + if (s.equals(str)) { + return true; } } - return missing; + return false; } - - private static List getConflicts(Session ssn, List inits) { - List conflicts = new ArrayList(); - for (Iterator it = inits.iterator(); it.hasNext(); ) { - String init = (String) it.next(); - OID oid = new OID(ssn.getMetadataRoot().getObjectType(INIT), init); - if (ssn.retrieve(oid) != null) { - conflicts.add(init); - } - } - return conflicts; - } - + + /** + * [SUPPORT] + * Concatenates two String-array to one. + * + * used in: setParameterContext + * + * @param a first String-array + * @param b second String-array + * @return The concatenated String-array + */ private static String[] concat(String[] a, String[] b) { if (a == null) { return b; } if (b == null) { return a; } @@ -614,166 +575,188 @@ class Load extends Command { System.arraycopy(b, 0, result, a.length, b.length); return result; } - - private static boolean contains(String[] array, String str) { - for (int i = 0; i < array.length; i++) { - if (array[i].equals(str)) { - return true; + + /** + * Saves the configurations made during the load process. + * + * @param config The configurations + * @return true on success, otherwise false. + */ + private boolean saveConfig(Config config) { + return delegate.saveConfig(config); + } + + /** + * Checks existence and accessibility of the database and does a rollback + * if necessary. + * + * @return True on success, otherwise false + */ + @Override + public boolean checkDatabase() { + return delegate.checkDatabase(); + } + + /** + * Sets back the configuration to the original packages. Goes through + * all packages from the configuration-context and removes the ones + * contained in the list of packages which will be loaded. + * + * @param config The configuration + * @param packages The packages to be loaded + * @return True on success, otherwise false + */ + @Override + public boolean rollbackConfig(Config config, List packages) { + return delegate.rollbackConfig(config, packages); + } + + /** + * Lists the required and provided tables and checks if there are no more + * missing or conflicting tables. + * Missing means, that a table required by a soon-to-be-created-table, has + * not been created yet. Conflicting means, that a table which will soon be + * provided by a soon-to-be-created-table has already been created. Either + * way leads to a problem, therefore returning false. + * + * @param line The command-line with all options and arguments + * @param loaders The loaders to the packages being loaded + * @param conn The connection to the database + * @param all Weather all steps (config, schema, data, inits) must be + * performed + * @return True if there are no missing or conflicting tables, otherwise + * false + */ + private boolean noMissingAndConflictingTables(CommandLine line, Loader[] + loaders, Connection conn, boolean all) { + return delegate.noMissingAndConflictingTables(line, loaders, conn, all); + } + + /** + * Checks if the table "inits" exists in the given database connection and + * then checks the initializer dependencies in the list of loaders from the + * soon-to-be-loaded packages. + * + * @param line The command-line with all options and arguments + * @param loaders The loaders to the packages being loaded + * @param conn The connection to the database + * @param all Weather all steps (config, schema, data, inits) must be + * performed + * @return True on success, otherwise false + */ + private boolean checkInits(CommandLine line, Loader[] loaders, Connection + conn, boolean all) { + boolean success = true; + if (PackageLoader.exists(conn, "inits") + && (line.hasOption("init") || all)) { + success = checkInitializerDependencies(loaders, "loader"); + } + return success; + } + + /** + * [SUPPORT] + * Checks the initializer dependencies set in the ".load"-file. + * + * used in: checkInits + * + * @param loaders A list of loaders to the corresponding packages + * to-be-loaded + * @param sessionName Name of the session + * @return true on success, otherwise false + */ + @Override + public boolean checkInitializerDependencies(final Loader[] loaders, + String sessionName) { + return delegate.checkInitializerDependencies(loaders, sessionName); + } + + /** + * Checks the schema of the packages. + * + * @param line The command-line with all options and arguments + * @param loaders The loaders to the packages being loaded + * @param Weather all steps (config, schema, data, inits) must be + * performed + * @return True on success, otherwise false + */ + private boolean checkSchema(CommandLine line, Loader[] loaders, boolean all) { + boolean passed = true; + if (all || line.hasOption("schema")) { + for (Loader loader : loaders) { + passed &= loader.checkSchema(); } } - - return false; + return passed; } - - private static Session session() { - Session ssn = SessionManager.getSession("loader"); - if (ssn == null) { - String pdl = "/com/arsdigita/runtime/Initializer.pdl"; - MetadataRoot root = new MetadataRoot(); - PDLCompiler compiler = new PDLCompiler(); - compiler.parse - (new InputStreamReader - (Load.class.getResourceAsStream(pdl)), - pdl); - compiler.emit(root); - ConnectionSource source = new DedicatedConnectionSource - (RuntimeConfig.getConfig().getJDBCURL()); - ssn = SessionManager.open("loader", root, source); - } - - return ssn; - } - - static Properties props(String[] args) { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - Writer w = new OutputStreamWriter(baos); - for (int i = 0; i < args.length; i++) { - w.write(args[i]); - w.write("\n"); + + /** + * Loads all schemas of the packages into the database through the opened + * connection. + * + * @param line The command-line with all options and arguments + * @param loaders The loaders to the packages being loaded + * @param conn The connection to the database + * @param all Weather all steps (config, schema, data, inits) must be + * performed + * @throws SQLException + */ + private void loadSchema(CommandLine line, Loader[] loaders, Connection conn, + boolean all) throws SQLException { + if (all || line.hasOption("schema")) { + for (Loader loader : loaders) { + loader.loadSchema(conn); } - - w.flush(); - - Properties props = new Properties(); - props.load(new ByteArrayInputStream(baos.toByteArray())); - return props; - } catch (IOException e) { - throw new UncheckedWrapperException(e); + conn.commit(); } } - - private static void sort(Loader[] loaders) { - Set all = new HashSet(); - for (int i = 0; i < loaders.length; i++) { - all.addAll(loaders[i].getProvided()); - } - - Set provided = new HashSet(); - List sorted = new ArrayList(); - List in = new ArrayList(Arrays.asList(loaders)); - int before; - do { - before = in.size(); - for (Iterator it = in.iterator(); it.hasNext(); ) { - Loader loader = (Loader) it.next(); - Set required = loader.getRequired(); - required.retainAll(all); - if (provided.containsAll(required)) { - sorted.add(loader); - provided.addAll(loader.getProvided()); - it.remove(); + + /** + * Checks and Loads the data of the packages into the database. + * + * @param line The command-line with all options and arguments + * @param loaders The loaders to the packages being loaded + * @param ssn The session for the database-connection + * @param all Weather all steps (config, schema, data, inits) must be + * performed + * @return True on success, otherwise false + */ + private boolean checkAndLoadData(CommandLine line, Loader[] loaders, Session + ssn, boolean all, CompoundParameterReader cpr) { + boolean passed = true; + if (all || line.hasOption("data")) { + //Starts new session for the db-connection + if (ssn == null) { + new Runtime().startup(); + ssn = SessionManager.getSession(); + } + for (Loader loader : loaders) { + passed &= loader.checkData(ssn); + } + if (passed) { + for (Loader loader : loaders) { + loader.loadData(ssn, cpr); } } - } while (in.size() < before); - - if (in.size() > 0) { - throw new IllegalStateException - ("circular dependencies: " + in); } - - int index = 0; - for (Iterator it = sorted.iterator(); it.hasNext(); ) { - loaders[index++] = (Loader) it.next(); + return passed; + } + + /** + * Records/Loads the initializers and classes into the database through the + * started session. + * + * @param loaders The loaders to the packages being loaded + * @param ssn The session for the database-connection + */ + private void loadInits(Loader[] loaders, Session ssn) { + //Starts new session for the db-connection + if (ssn == null) { + new Runtime().startup(); + ssn = SessionManager.getSession(); } + for (Loader loader : loaders) { + loader.loadInits(ssn); + } } - - private static final int REQ_TABLE = 0; - private static final int REQ_INITIALIZER = 1; - private static final int REQ_PACKAGE = 2; - private static final int PROV_TABLE = 3; - private static final int PROV_INITIALIZER = 4; - private static final int PROV_PACKAGE = 5; - - private static List get(Loader[] loaders, int type) { - ArrayList result = new ArrayList(); - - for (int i = 0; i < loaders.length; i++) { - LoaderInfo info = loaders[i].getInfo(); - - List c; - - switch (type) { - case REQ_TABLE: - c = info.getRequiredTables(); - break; - case REQ_INITIALIZER: - c = info.getRequiredInitializers(); - break; - case REQ_PACKAGE: - c = info.getRequiredPackages(); - break; - case PROV_TABLE: - c = info.getProvidedTables(); - break; - case PROV_INITIALIZER: - c = info.getProvidedInitializers(); - break; - case PROV_PACKAGE: - c = new ArrayList(); - c.add(loaders[i].getKey()); - break; - default: - throw new IllegalArgumentException("unknown type: " + type); - } - - addTo(result, c); - } - - return result; - } - - private static List getRequiredTables(Loader[] loaders) { - return get(loaders, REQ_TABLE); - } - - private static List getProvidedTables(Loader[] loaders) { - return get(loaders, PROV_TABLE); - } - - private static List getRequiredInitializers(Loader[] loaders) { - return get(loaders, REQ_INITIALIZER); - } - - private static List getProvidedInitializers(Loader[] loaders) { - return get(loaders, PROV_INITIALIZER); - } - - private static List getRequiredPackages(Loader[] loaders) { - return get(loaders, REQ_PACKAGE); - } - - private static List getProvidedPackages(Loader[] loaders) { - return get(loaders, PROV_PACKAGE); - } - - private static void addTo(List a, List b) { - for (Iterator it = b.iterator(); it.hasNext(); ) { - Object o = it.next(); - if (!a.contains(o)) { - a.add(o); - } - } - } - -} +} \ No newline at end of file diff --git a/ccm-core/src/com/arsdigita/packaging/LoadCenter.java b/ccm-core/src/com/arsdigita/packaging/LoadCenter.java new file mode 100644 index 000000000..463bc755a --- /dev/null +++ b/ccm-core/src/com/arsdigita/packaging/LoadCenter.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2003-2004 Red Hat Inc. 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 2.1 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; 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 library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.packaging; + +import java.io.IOException; +import java.util.List; +import org.apache.commons.cli.CommandLine; + +/** + * Interface used for the "delegate-design-pattern" and therefore sets all + * method-declarations which have to be implemented by the helper class + * "LoadCenterDeligate" known as the delegate to support the "load"- + * and "unload"-commands. + * + * The "delegate-design-pattern" is a possibility to imitate multiple + * inheritance and thus reduce the redundancy. + * + * @author Tobias Osmers + * @version $Revision: #1 $ $Date: 2015/04/22 $ + */ +public interface LoadCenter { + enum LoadType {LOAD, UNLOAD}; + + /** + * Gets all packages to be unloaded either from the command-line or a file, + * if the option flag [--packagekeys-file FILE] has been set and puts them + * in a list of packages (by their package-keys). + * + * @param line The command-line with all options and arguments + * @return The list of packages to be unloaded + * @throws IOException + */ + List getAllPackages(CommandLine line) throws IOException; + + /** + * Gets all loaders to the given package-list and sorts them before re- + * turning. Creates a map that assigns to every package-key an equivalent + * loader. This loader contains a bunch of informations (required, + * provided, scripts) provided by an ".load"-file. Then all loaders from + * the map (pkg-key -> loader) are composed into an array of loaders and + * sorted. + * + * @param line The command-line with all options and arguments + * @param packages The list of packages to be loaded + * @param loadType Weather packages are been loaded or unloaded + * @return A sorted list of loaders + * @throws Error + */ + Loader[] getAllLoaders(CommandLine line, List packages, + LoadType loadType) throws Error; + + /** + * Determines if all steps (config, schema, data, inits) have to be + * performed. + * + * @param line The command-line with all options and arguments + * @return True if all options need to be performed + */ + boolean hasAllOptions(CommandLine line); + + /** + * Checks existence and accessibility of the database and does a rollback + * if necessary. + * + * @return True on success, otherwise false + */ + boolean checkDatabase(); + + /** + * Sets back the configuration to the original packages. Goes through + * all packages from the configuration-context and removes the ones + * contained in the list of packages which will be loaded. + * + * @param config The configuration + * @param packages The packages to be loaded + * @return True on success, otherwise false + */ + boolean rollbackConfig(Config config, List packages); + + /** + * Checks the initializer dependencies set in the ".load"-file. + * + * @param loaders A list of loaders to the corresponding packages + * to-be-loaded + * @param sessionName Name of the session + * @return true on success, otherwise false + */ + boolean checkInitializerDependencies(final Loader[] loaders, + String sessionName); + +} diff --git a/ccm-core/src/com/arsdigita/packaging/LoadCenterDelegate.java b/ccm-core/src/com/arsdigita/packaging/LoadCenterDelegate.java new file mode 100644 index 000000000..27f53d3de --- /dev/null +++ b/ccm-core/src/com/arsdigita/packaging/LoadCenterDelegate.java @@ -0,0 +1,695 @@ +/* + * Copyright (C) 2003-2004 Red Hat Inc. 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 2.1 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; 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 library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.packaging; + +import com.arsdigita.loader.PackageLoader; +import com.arsdigita.persistence.ConnectionSource; +import com.arsdigita.persistence.DedicatedConnectionSource; +import com.arsdigita.persistence.OID; +import com.arsdigita.persistence.Session; +import com.arsdigita.persistence.SessionManager; +import com.arsdigita.persistence.metadata.MetadataRoot; +import com.arsdigita.persistence.pdl.PDLCompiler; +import com.arsdigita.runtime.ConfigRegistry; +import com.arsdigita.runtime.RegistryConfig; +import com.arsdigita.runtime.RuntimeConfig; +import com.arsdigita.util.parameter.Parameter; +import com.arsdigita.util.parameter.ParameterContext; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.sql.Connection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Scanner; +import java.util.Set; +import org.apache.commons.cli.CommandLine; +import org.apache.log4j.Logger; + +/** + * The helper class for the "delegate-design-pattern" known as the "delegate" + * whom is given the responsibility to execute tasks for the "load"- and + * "unload"-command. + * + * Contains all implementations defined in the interface as well as their needed + * support methods. Furthermore this class implements some methods with support + * methods only used by the load.java-class. + * + * + * @author Tobias Osmers + * @version $Revision: #2 $ $Date: 2015/04/27 $ + */ +public class LoadCenterDelegate implements LoadCenter { + + private static final Logger logger = Logger.getLogger(Unload.class); + private static final String INIT = "com.arsdigita.runtime.Initializer"; + + private final InfoGetter infoGetter = new InfoGetter(); + + /** + * Gets all packages to be unloaded either from the command-line or a file, + * if the option flag [--packagekeys-file FILE] has been set and puts them + * in a list of packages (by their package-keys). + * + * @param line The command-line with all options and arguments + * @return The list of packages to be unloaded + * @throws IOException + */ + @Override + public List getAllPackages(CommandLine line) throws IOException { + List packages = line.getArgList(); + //[--packagekeys-file FILE] + if (line.hasOption("packagekeys-file")) { + String file = line.getOptionValue("packagekeys-file"); + logger.debug("File with package keys: " + file ); + Scanner sc = new Scanner(new File(file)); + while (sc.hasNext()) { + packages.add(sc.next()); + } + } + return packages; + } + + /** + * Gets all loaders to the given package-list and sorts them before re- + * turning. Creates a map that assigns to every package-key an equivalent + * loader. This loader contains a bunch of informations (required, + * provided, scripts) provided by an ".load"-file. Then all loaders from + * the map (pkg-key -> loader) are composed into an array of loaders and + * sorted. + * + * @param line The command-line with all options and arguments + * @param packages The list of packages to be loaded + * @param loadType Weather packages are been loaded or unloaded + * @return A sorted list of loaders + * @throws Error + */ + @Override + public Loader[] getAllLoaders(CommandLine line, List packages, + LoadType loadType) throws Error { + Map pkgLoaderMap = new HashMap(); + List packageKeys = new ArrayList(); + packageKeys.addAll(packages); + while (!packageKeys.isEmpty()) { + String packageKey = (String) packageKeys.remove(0); + if (pkgLoaderMap.containsKey(packageKey)) { + continue; + } + Loader loader = Loader.get(packageKey, loadType); + if (loader == null) { + throw new Error("unable to locate package: " + packageKey); + } else { + pkgLoaderMap.put(packageKey, loader); + //[--recursive] + //If set, all packages required for this package will + //be added to the list of packages being unloaded. + if (line.hasOption("recursive")) { + packageKeys.addAll(loader.getInfo().getRequiredPackages()); + } + } + } + Loader[] loaders = (Loader[]) pkgLoaderMap.values().toArray + (new Loader[pkgLoaderMap.size()]); + + sort(loaders, loadType); + + return loaders; + } + + /** + * [SUPPORT] + * Sorts a given list of loaders, so that the internal order of which + * package when to load/unload is consistent with the packages available. + * Load: If a package A requires package B, package B has to be loaded + * already. + * Unload: If a package A provides package B, package B has to be unloaded + * first. + * Set all: Set of packages to be loaded and its provided packages + * List in: List of packages to be loaded (input) + * Set required: Set of packages required by the packages from in + * List sorted: List of loaders to be loaded + * Set provided: Set of provided packages and to be loaded packages + * Loaders[] loaders: List of packages to be loaded in the right order + * + * used in: getAllLoaders + * + * @param loaders A list of loaders (to be loaded packages) + * @param loadType Weather packages are been loaded or unloaded + */ + private static void sort(Loader[] loaders, LoadType loadType) { + Set all = new HashSet(); + for (Loader loader : loaders) { + all.addAll(loader.getProvided()); + } + Set provided = new HashSet(); + List sorted = new ArrayList(); + List in = new ArrayList(Arrays.asList(loaders)); + int before; + do { + before = in.size(); + for (Iterator it = in.iterator(); it.hasNext(); ) { + Loader loader = (Loader) it.next(); + Set required = loader.getRequired(); + //Only possible to load + required.retainAll(all); + //If the already provided packages contain all + //required ones its save to add this one too. + //Ensures that the order of loading packages is + //right. + if (provided.containsAll(required)) { + sorted.add(loader); + provided.addAll(loader.getProvided()); + it.remove(); + } + } + } while (in.size() < before); + if (in.size() > 0) { + throw new IllegalStateException + ("circular dependencies: " + in); + } + int index = 0; + for (Iterator it = sorted.iterator(); it.hasNext(); ) { + loaders[index++] = (Loader) it.next(); + } + + //In case packages are being unloaded + if (loadType == LoadType.UNLOAD) { + reverseArray(loaders); + } + } + + /** + * [SUPPORT] + * Reverses the list of loaders to ensure the right order for unloading. + * + * used in: sort + * + * @param loaders The loaders for the packages being unloaded + */ + private static void reverseArray(Loader[] loaders) { + for (int i = 0; i < loaders.length / 2; i++) { + Loader temp = loaders[i]; + loaders[i] = loaders[loaders.length - 1 - i]; + loaders[loaders.length - 1 - i] = temp; + } + } + + /** + * Determines if all steps (config, schema, data, inits) have to be + * performed. + * + * @param line The command-line with all options and arguments + * @return True if all options need to be performed + */ + @Override + public boolean hasAllOptions(CommandLine line) { + return !(line.hasOption("config") + || line.hasOption("schema") + || line.hasOption("data") + || line.hasOption("init")); + } + + /** + * Creates a new (empty) config object and loads config values from a file. + * Then retrieves a list of installed packages. RegistryConfig contains a + * list of package-keys of loaded packages. + * + * If all steps need to be performed, it checks if there are no more missing + * or conflicting packages. Missing means, that a package required by a + * soon-to-be-loaded-package, has not been loaded yet. Conflicting means, + * that a package which will soon be provided by a soon-to-be-loaded-package + * has already been loaded. Either way leads to a problem, therefore + * returning false. + * @param loaders The loaders to the packages being loaded + * @param all Weather all steps (config, schema, data, inits) must be + * performed + * @return True if there are no missing or conflicting packages, otherwise + * false + */ + public boolean noMissingAndConflictingPackages(Loader[] loaders, + boolean all) { + RegistryConfig rc = new RegistryConfig(); + rc.load(); + List loaded = Arrays.asList(rc.getPackages()); + if (all) { + List missing = new ArrayList(); + addTo(missing, InfoGetter.getRequiredPackages(loaders)); + missing.removeAll(InfoGetter.getProvidedPackages(loaders)); + missing.removeAll(loaded); + List conflicts = new ArrayList(loaded); + conflicts.retainAll(InfoGetter.getProvidedPackages(loaders)); + if (!missing.isEmpty()) { + System.err.println("required packages: " + missing); + } + if (!conflicts.isEmpty()) { + System.err.println("conflicting packages: " + conflicts); + } + if (missing.size() + conflicts.size() > 0) { + return false; + } + } + return true; + } + + /** + * [SUPPORT] + * Adds the elements of the second list to the first list. + * + * used in: noMissingOrConflictingPackages, + * checkInitializerDependencies + * + * + * @param a The first list + * @param b The second list + */ + private static void addTo(List a, List b) { + for (Iterator it = b.iterator(); it.hasNext(); ) { + Object o = it.next(); + if (!a.contains(o)) { + a.add(o); + } + } + } + + /** + * Saves the configurations made during the load process. + * + * used in: rollbackConfig, Load.java + * + * @param config The configurations + * @return true on success, otherwise false. + */ + public boolean saveConfig(Config config) { + if (config != null) { + try { + config.save(); + } catch (IOException e) { + System.err.println(e.getMessage()); + return false; + } + } + return true; + } + + /** + * Checks existence and accessibility of the database and does a rollback + * if necessary. + * + * @return True on success, otherwise false + */ + @Override + public boolean checkDatabase() { + Check checkdb = new CheckDB(); + checkdb.run(null); + if (checkdb.getStatus() == null || + checkdb.getStatus().equals(Check.FAIL)) { + return false; + } + return true; + } + + /** + * Sets back the configuration to the original packages. Goes through + * all packages from the configuration-context and removes the ones + * contained in the list of packages which will be loaded. + * + * @param config The configuration + * @param packages The packages to be loaded + * @return True on success, otherwise false + */ + @Override + public boolean rollbackConfig(Config config, List packages) { + if (config == null) { + config = new Config(new ConfigRegistry()); + config.load(System.err); + } + Parameter param = config.getParameter("waf.config.packages"); + ParameterContext ctx = config.getContainer(param); + String[] pkgs = (String[]) ctx.get(param); + LinkedList original = new LinkedList(); + for (String pkg : pkgs) { + boolean isnew = false; + for (Object package1 : packages) { + // Operator == compares object identity. + // comparison here refers to package names, so an + // object comparison will never be true. + // instead: equals() + // if (pkgs[i].toString() == packages.get(j).toString()) { + if (pkg.equals(package1.toString())) { + isnew = true; + } + } + if (!isnew) { + original.add(pkg); + } + } + String[] orig = new String[original.size()]; + for (int i = 0; i < original.size(); i++) { + orig[i] = (String)original.get(i); + } + ctx.set(param, orig); + return saveConfig(config); + } + + /** + * Lists the required and provided tables and checks if there are no more + * missing or conflicting tables. + * Missing means, that a table required by a soon-to-be-created-table, has + * not been created yet. Conflicting means, that a table which will soon be + * provided by a soon-to-be-created-table has already been created. Either + * way leads to a problem, therefore returning false. + * + * @param line The command-line with all options and arguments + * @param loaders The loaders to the packages being loaded + * @param conn The connection to the database + * @param all Weather all steps (config, schema, data, inits) must be + * performed + * @return True if there are no missing or conflicting tables, otherwise + * false + */ + public boolean noMissingAndConflictingTables(CommandLine line, Loader[] + loaders, Connection conn, boolean all) { + List required = new ArrayList(); + addTo(required, InfoGetter.getRequiredTables(loaders)); + List provided = new ArrayList(); + + if (all || line.hasOption("schema")) { + required.removeAll(InfoGetter.getProvidedTables(loaders)); + addTo(provided, InfoGetter.getProvidedTables(loaders)); + } else if (line.hasOption("data")) { + addTo(required, InfoGetter.getProvidedTables(loaders)); + } + + List missing = getMissing(conn, required); + List conflicts = getConflicts(conn, provided); + if (!missing.isEmpty()) { + System.err.println("required tables: " + missing); + } + if (!conflicts.isEmpty()) { + System.err.println( + "conflicting tables (already exist): " + conflicts); + } + if (conflicts.size() > 0 || missing.size() > 0) { + return false; + } + return true; + } + + /** + * [SUPPORT] + * Returns all tables, required by the tables in the given list, which + * haven't been created yet in the database for the given connection, thus + * all missing tables. + * + * used in: noMissingAndConflictingTables + * + * @param conn The connection to the database + * @param tables The list of tables to be installed + * @return A list of missing tables + */ + private static List getMissing(Connection conn, List tables) { + List missing = new ArrayList(); + for (Iterator it = tables.iterator(); it.hasNext(); ) { + String table = (String) it.next(); + if (!PackageLoader.exists(conn, table)) { + missing.add(table); + } + } + return missing; + } + + /** + * [SUPPORT] + * Returns all tables, provided by the tables in the given list, which have + * already been created in the database for the given connection, thus + * all conflicting tables. + * + * used in: noMissingAndConflictingTables + * + * @param conn The connection to the database + * @param tables The list of tables to be installed + * @return A list of conflicting tables + */ + private static List getConflicts(Connection conn, List tables) { + List conflicts = new ArrayList(); + for (Iterator it = tables.iterator(); it.hasNext(); ) { + String table = (String) it.next(); + if (PackageLoader.exists(conn, table)) { + conflicts.add(table); + } + } + return conflicts; + } + + /** + * Checks the initializer dependencies set in the ".load"-file. + * + * @param loaders A list of loaders to the corresponding packages + * to-be-loaded + * @param sessionName Name of the session + * @return true on success, otherwise false + */ + @Override + public boolean checkInitializerDependencies(final Loader[] loaders, + String sessionName) { + final List required = new ArrayList(); + final List provided = new ArrayList(); + addTo(required, InfoGetter.getRequiredInitializers(loaders)); + required.removeAll(InfoGetter.getProvidedInitializers(loaders)); + addTo(provided, InfoGetter.getProvidedInitializers(loaders)); + + final Session boot = session(sessionName); + final List missing = getMissing(boot, required); + final List conflicts = getConflicts(boot, provided); + + if (!missing.isEmpty()) { + System.err.println("required initializers: " + missing); + return false; + } + if (!conflicts.isEmpty()) { + System.err.println("conflicting initializers: " + conflicts); + return false; + } + return true; + } + + /** + * [SUPPORT] + * Returns the session to the given name, if existing. If not, opens a new + * Session for the database-connection with the given name. + * + * used in: checkInitializerDependencies + * + * @return A session for the database-connection + */ + private static Session session(String name) { + Session ssn = SessionManager.getSession(name); + if (ssn == null) { + String pdl = "/com/arsdigita/runtime/Initializer.pdl"; + MetadataRoot root = new MetadataRoot(); + PDLCompiler compiler = new PDLCompiler(); + compiler.parse + (new InputStreamReader + (Load.class.getResourceAsStream(pdl)), + pdl); + compiler.emit(root); + ConnectionSource source = new DedicatedConnectionSource + (RuntimeConfig.getConfig().getJDBCURL()); + ssn = SessionManager.open(name, root, source); + } + return ssn; + } + + /** + * [SUPPORT] + * Returns a list of initializers, required by the initializers in the + * given list, which haven't been initialized yet, thus are missing. + * + * used in: checkInitializerDependencies + * + * @param ssn The session for the db-connection + * @param inits List of initializers + * @return List of missing initializers + */ + private static List getMissing(Session ssn, List inits) { + List missing = new ArrayList(); + for (Iterator it = inits.iterator(); it.hasNext(); ) { + String init = (String) it.next(); + OID oid = new OID(ssn.getMetadataRoot().getObjectType(INIT), init); + if (ssn.retrieve(oid) == null) { + missing.add(init); + } + } + return missing; + } + + /** + * [SUPPORT] + * Returns all initializers, provided by initializers in the given list, + * which have already been initialized, thus all conflicting initializers. + * + * used in: checkInitializerDependencies + * + * @param ssn The session for the db-connection + * @param inits List of initializers + * @return List of conflicting initializers + */ + private static List getConflicts(Session ssn, List inits) { + List conflicts = new ArrayList(); + for (Iterator it = inits.iterator(); it.hasNext(); ) { + String init = (String) it.next(); + OID oid = new OID(ssn.getMetadataRoot().getObjectType(INIT), init); + if (ssn.retrieve(oid) != null) { + conflicts.add(init); + } + } + return conflicts; + } + + + /** + * Hidden class to get information from the loaders + */ + private static class InfoGetter { + //Enum for the information type wished to retrieve. + private static enum InfoType { + REQ_TABLE, REQ_INITIALIZER, REQ_PACKAGE, + PROV_TABLE, PROV_INITIALIZER, PROV_PACKAGE; + } + + /** + * Returns the required packages to the given loaders and is used, + * when checking for missing or conflicting packages. + * + * @param loaders List of loaders + * @return List of required packages + */ + private static List getRequiredPackages(Loader[] loaders) { + return get(loaders, InfoType.REQ_PACKAGE); + } + + /** + * Returns the provided packages to the given loaders and is used, + * when checking for missing or conflicting packages. + * + * @param loaders List of loaders + * @return List of provided packages + */ + private static List getProvidedPackages(Loader[] loaders) { + return get(loaders, InfoType.PROV_PACKAGE); + } + + /** + * Returns the required tables to the given loaders and is used when + * checking for missing or conflicting tables. + * + * @param loaders List of loaders + * @return List of required tables + */ + private static List getRequiredTables(Loader[] loaders) { + return get(loaders, InfoType.REQ_TABLE); + } + + /** + * Returns the provided tables to the given loaders and is used when + * checking for missing or conflicting tables. + * + * @param loaders List of loaders + * @return List of provided tables + */ + private static List getProvidedTables(Loader[] loaders) { + return get(loaders, InfoType.PROV_TABLE); + } + + /** + * Returns the required initializers to the given loaders and is used, + * when checking for missing or conflicting initializers. + * + * supports: checkInitializerDependencies + * + * @param loaders List of loaders + * @return List of required initializers + */ + private static List getRequiredInitializers(Loader[] loaders) { + return get(loaders, InfoType.REQ_INITIALIZER); + } + + /** + * Returns the provided initializers to the given loaders and is used, + * when checking for missing or conflicting initializers. + * + * supports: checkInitializerDependencies + * + * @param loaders List of loaders + * @return List of provided initializers + */ + private static List getProvidedInitializers(Loader[] loaders) { + return get(loaders, InfoType.PROV_INITIALIZER); + } + + /** + * Main-Getter, to retrieve the informations provided by the ".load"-file + * and stored in a loader. + * + * @param loaders List of loaders of the packages to be loaded + * @param informationType type of the information + * @return + */ + private static List get(Loader[] loaders, InfoType infoType) { + ArrayList result = new ArrayList(); + + for (Loader loader : loaders) { + LoaderInfo info = loader.getInfo(); + List c; + switch (infoType) { + case REQ_TABLE: + c = info.getRequiredTables(); + break; + case REQ_INITIALIZER: + c = info.getRequiredInitializers(); + break; + case REQ_PACKAGE: + c = info.getRequiredPackages(); + break; + case PROV_TABLE: + c = info.getProvidedTables(); + break; + case PROV_INITIALIZER: + c = info.getProvidedInitializers(); + break; + case PROV_PACKAGE: + c = new ArrayList(); + c.add(loader.getKey()); + break; + default: + throw new IllegalArgumentException( + "unknown type: " + infoType.toString()); + } + addTo(result, c); + } + return result; + } + } +} \ No newline at end of file diff --git a/ccm-core/src/com/arsdigita/packaging/Loader.java b/ccm-core/src/com/arsdigita/packaging/Loader.java index 127aeb09e..5fb9b496a 100755 --- a/ccm-core/src/com/arsdigita/packaging/Loader.java +++ b/ccm-core/src/com/arsdigita/packaging/Loader.java @@ -22,6 +22,7 @@ import com.arsdigita.db.DbHelper; import com.arsdigita.kernel.Kernel; import com.arsdigita.kernel.KernelExcursion; import com.arsdigita.loader.PackageLoader; +import com.arsdigita.packaging.LoadCenter.LoadType; import com.arsdigita.persistence.DataAssociation; import com.arsdigita.persistence.DataObject; import com.arsdigita.persistence.OID; @@ -50,62 +51,101 @@ import org.apache.log4j.Logger; /** * Loader * - * Helper class for load which actually performs the loading of - * the database schema and of the initial content. + * Helper class for load and unload which actually performs the loading and + * unloading of the database schema, the data and of the initial content. * - * @author Rafael H. Schloming <rhs@mit.edu> + * @author Rafael H. Schloming <rhs@mit.edu> tosmers; * @version $Revision: #13 $ $Date: 2004/08/16 $ - * @version $Id: Loader.java 2115 2011-01-13 17:11:50Z pboy $ + * @version $Id: Loader.java 2116 2015-04-15 14:18:40Z tosmers $ */ class Loader { private static final Logger s_log = Logger.getLogger(Loader.class); private static final String INIT = "com.arsdigita.runtime.Initializer"; - public static Loader get(String pkg) { + private String m_key; + private LoaderInfo m_info; + private Checklist m_checks; + private List m_scripts; + + + /** + * Constructor. Creates a loader to the given parameters. + * + * @param key The corresponding package-key to the loader + * @param info The informations from the ".load"-file + * @param checks A Checklist + */ + public Loader(String key, LoaderInfo info, Checklist checks, + LoadType scriptType) { + m_key = key; + m_info = info; //all the stuff form .load-file + m_checks = checks; + m_scripts = new ArrayList(); + for (Iterator it = m_info.getDataScripts(scriptType).iterator(); + it.hasNext();) { + String script = (String) it.next(); + m_scripts.add(Classes.newInstance(script)); + } + } + + /** + * Gets a loader to the given package-key via an input-stream if + * a corresponding ".load"-file to the package-key exists. The + * loader contains the informations stored in the ".load"-file. + * + * @param pkg The package-key + * @return A Loader to the given package-key + */ + public static Loader get(String pkg, LoadType scriptType) { ClassLoader ldr = Loader.class.getClassLoader(); InputStream is = ldr.getResourceAsStream(pkg + ".load"); if (is == null) { s_log.error(String.format("Failed to find '%s.load'.", pkg)); return null; } + //Contains all relevante data from the .load-file. LoaderInfo info = new LoaderInfo(is); try { is.close(); } catch (IOException e) { throw new UncheckedWrapperException(e); } - return new Loader(pkg, info, Checklist.get(pkg)); - } - private String m_key; - private LoaderInfo m_info; - private Checklist m_checks; - private List m_scripts; - - public Loader(String key, LoaderInfo info, Checklist checks) { - m_key = key; - m_info = info; - m_checks = checks; - m_scripts = new ArrayList(); - for (Iterator it = m_info.getDataScripts().iterator(); - it.hasNext();) { - String script = (String) it.next(); - m_scripts.add(Classes.newInstance(script)); - } + return new Loader(pkg, info, Checklist.get(pkg), scriptType); } + /** + * Getter for the package-key. + * + * @return The package-key + */ public String getKey() { return m_key; } + /** + * Getter for the loader-informations. + * + * @return The loader-informations + */ public LoaderInfo getInfo() { return m_info; } + /** + * List of scripts for the loader, provided by the ".load"-file. + * + * @return A list of scripts + */ public List getScripts() { return m_scripts; } + /** + * Checks the schema, set in the ".load"-file. + * + * @return true on success, otherwise false + */ public boolean checkSchema() { if (m_checks == null) { return true; @@ -114,6 +154,11 @@ class Loader { } } + /** + * Loads the schema, set in the ".load"-file. + * + * @param conn The connection to the database + */ public void loadSchema(Connection conn) { int db = DbHelper.getDatabase(conn); String dir = DbHelper.getDatabaseDirectory(db); @@ -124,7 +169,29 @@ class Loader { PackageLoader.load(conn, script + "/" + dir + "-create.sql"); } } + + /** + * Unloads the schema, set in the ".load"-file. + * + * @param conn The connection to the database + */ + public void unloadSchema(Connection conn) { + int db = DbHelper.getDatabase(conn); + String dir = DbHelper.getDatabaseDirectory(db); + List scripts = m_info.getSchemaScripts(); + for (Iterator it = scripts.iterator(); it.hasNext();) { + String script = (String) it.next(); + s_log.info("Unloading schema for " + script); + PackageLoader.load(conn, script + "/" + dir + "-drop.sql"); + } + } + /** + * Checks the data, set in the ".load"-file. + * + * @param ssn The session + * @return true on success, otherwise false + */ public boolean checkData(Session ssn) { if (m_checks == null) { return true; @@ -133,6 +200,12 @@ class Loader { } } + /** + * Loads the data, set in the ".load"-file. + * + * @param ssn The session + * @param prd The ParameterReader + */ public void loadData(Session ssn, ParameterReader prd) { final List inits = m_info.getProvidedInitializers(); CompoundInitializer ini = new CompoundInitializer(); @@ -149,10 +222,10 @@ class Loader { // final ScriptContext ctx = new ScriptContext(ssn, loader); final ScriptContext ctx = new ScriptContext(ssn, prd); new KernelExcursion() { - + @Override protected void excurse() { setEffectiveParty(Kernel.getSystemParty()); - for (Iterator it = m_scripts.iterator(); it.hasNext();) { + for (Iterator it = m_scripts.iterator(); it.hasNext(); ) { Script script = (Script) it.next(); s_log.info("Running data loader " + script.getClass(). getName()); @@ -165,14 +238,54 @@ class Loader { txn.commitTxn(); } } + + /** + * Unloads the data. Takes the data-unload script from the ".load"-file and + * implements a new KernelExcursion's excurse-method, in which the + * Script.java's run-method is called. This run-method is implemented by + * the AbstractContentTypeUnloader.java, which implements himself an other + * new KernelExcursion's excurse-method, calling the sweepTypes-method, the + * heart of the unloadData functionality. + * + * @param ssn The session to the database + */ + public void unloadData(Session ssn) { + TransactionContext txn = ssn.getTransactionContext(); + txn.beginTxn(); + + final ScriptContext ctx = new ScriptContext(ssn, null); + new KernelExcursion() { + @Override + protected void excurse() { + setEffectiveParty(Kernel.getSystemParty()); + for (Iterator it = m_scripts.iterator(); it.hasNext(); ) { + Script script = (Script) it.next(); + s_log.info("Running data unloader " + script.getClass(). + getName()); + script.run(ctx); + } + } + }.run(); + if (txn.inTxn()) { + txn.commitTxn(); + } + } + + /** + * Loads the initializers, set in the ".load"-file. Creates a new data- + * object with an "oid" for every provided initializer and and add the + * required initializers to its requirements for data-associations. + * + * @param ssn The session + */ public void loadInits(final Session ssn) { final TransactionContext txn = ssn.getTransactionContext(); txn.beginTxn(); final List inits = m_info.getProvidedInitializers(); final List required = m_info.getRequiredInitializers(); - for (Iterator it = inits.iterator(); it.hasNext();) { + for (Iterator it = inits.iterator(); it.hasNext(); ) { String init = (String) it.next(); DataObject dataobject = ssn.create(new OID(INIT, init)); DataAssociation da = @@ -187,8 +300,46 @@ class Loader { txn.commitTxn(); } } + + /** + * Unloads the initializers, set in the ".load"-file". Removes for every + * provided initializer the required initializers form the requirements in + * the data-association and deletes the data object to that provided + * initializer. + * + * @param ssn The session + */ + public void unloadInits(final Session ssn) { + final TransactionContext txn = ssn.getTransactionContext(); + txn.beginTxn(); + + final List inits = m_info.getProvidedInitializers(); + final List required = m_info.getRequiredInitializers(); + for (Iterator it = inits.iterator(); it.hasNext(); ) { + String init = (String) it.next(); + OID oid = new OID(INIT, init); + DataObject dataObject = ssn.retrieve(oid); + DataAssociation da = + (DataAssociation) dataObject.get("requirements"); + for (Iterator reqIt = required.iterator(); reqIt.hasNext(); ) { + String reqInit = (String) reqIt.next(); + da.remove(ssn.retrieve(new OID(INIT, reqInit))); + } + ssn.delete(oid); + } + + if (txn.inTxn()) { + txn.commitTxn(); + } + } - Set getRequired() { + /** + * Returns all tables, initializers and packages required by this loader. + * Used in Load.sort + * + * @return A set of required things like tables, initializers and packages + */ + public Set getRequired() { Set result = new HashSet(); result.addAll(m_info.getRequiredTables()); result.addAll(m_info.getRequiredInitializers()); @@ -196,7 +347,13 @@ class Loader { return result; } - Set getProvided() { + /** + * Returns all tables and initializers provided by this loader. + * Used in Load.sort + * + * @return A set of provided things like tables and initializers. + */ + public Set getProvided() { Set result = new HashSet(); result.addAll(m_info.getProvidedTables()); result.addAll(m_info.getProvidedInitializers()); diff --git a/ccm-core/src/com/arsdigita/packaging/LoaderInfo.java b/ccm-core/src/com/arsdigita/packaging/LoaderInfo.java index fd1d0cd7c..94c3092eb 100755 --- a/ccm-core/src/com/arsdigita/packaging/LoaderInfo.java +++ b/ccm-core/src/com/arsdigita/packaging/LoaderInfo.java @@ -18,6 +18,7 @@ */ package com.arsdigita.packaging; +import com.arsdigita.packaging.LoadCenter.LoadType; import com.arsdigita.xml.XML; import java.io.InputStream; import java.util.ArrayList; @@ -42,6 +43,8 @@ class LoaderInfo { private List m_providedInitializers = new ArrayList(); private List m_schemaScripts = new ArrayList(); private List m_dataScripts = new ArrayList(); + private List m_dataLoadScripts = new ArrayList(); + private List m_dataUnloadScripts = new ArrayList(); private List m_requiredPackages = new ArrayList(); /** @@ -76,10 +79,19 @@ class LoaderInfo { return m_schemaScripts; } - public List getDataScripts() { + public List getDataScripts(LoadType scriptType) { + switch (scriptType) { + case LOAD: + m_dataScripts = m_dataLoadScripts; + break; + case UNLOAD: + m_dataScripts = m_dataUnloadScripts; + break; + } return m_dataScripts; } + //Strings correspond to the tag names in the ".load"-files private static final String PROVIDES = "provides"; private static final String REQUIRES = "requires"; private static final String TABLE = "table"; @@ -87,7 +99,8 @@ class LoaderInfo { private static final String PACKAGE = "package"; private static final String SCRIPTS = "scripts"; private static final String SCHEMA = "schema"; - private static final String DATA = "data"; + private static final String DATA_LOAD = "data"; + private static final String DATA_UNLOAD = "data-unload"; // attributes private static final String NAME = "name"; @@ -98,6 +111,7 @@ class LoaderInfo { private List m_context = new ArrayList(); + @Override public void startElement(String uri, String name, String qn, Attributes attrs) { if (name.equals(TABLE)) { @@ -165,7 +179,7 @@ class LoaderInfo { m_schemaScripts.add(dir); } - if (name.equals(DATA)) { + if (name.equals(DATA_LOAD)) { if (!m_context.contains(SCRIPTS)) { throw new IllegalStateException ("data element must appear inside scripts"); @@ -177,12 +191,28 @@ class LoaderInfo { ("data element requires class attribute"); } - m_dataScripts.add(klass); + m_dataLoadScripts.add(klass); } + + if (name.equals(DATA_UNLOAD)) { + if (!m_context.contains(SCRIPTS)) { + throw new IllegalStateException + ("data element must appear inside scripts"); + } + String klass = attrs.getValue(uri, CLASS); + if (klass == null) { + throw new IllegalStateException + ("data element requires class attribute"); + } + + m_dataUnloadScripts.add(klass); + } + m_context.add(name); } + @Override public void endElement(String uri, String name, String qn) { m_context.remove(m_context.lastIndexOf(name)); } diff --git a/ccm-core/src/com/arsdigita/packaging/MasterTool.java b/ccm-core/src/com/arsdigita/packaging/MasterTool.java index a68202a89..101f1b332 100755 --- a/ccm-core/src/com/arsdigita/packaging/MasterTool.java +++ b/ccm-core/src/com/arsdigita/packaging/MasterTool.java @@ -39,7 +39,7 @@ import java.util.Map; * provided script implementation is ccm, a shell script (sh and bat) backed * by some PERL scripts, located in the tools directory of CCM trunk. * - * @author Justin Ross <jross@redhat.com> + * @author Justin Ross <jross@redhat.com> tosmers; * @version $Id: MasterTool.java 2031 2009-12-10 03:34:04Z terry $ */ public class MasterTool { diff --git a/ccm-core/src/com/arsdigita/packaging/Set.java b/ccm-core/src/com/arsdigita/packaging/Set.java index b9e7d32c4..d7a2dd648 100755 --- a/ccm-core/src/com/arsdigita/packaging/Set.java +++ b/ccm-core/src/com/arsdigita/packaging/Set.java @@ -93,8 +93,14 @@ class Set extends Command { ConfigRegistry reg = new ConfigRegistry(); Config config = new Config(reg); config.load(System.err); - - Properties props = Load.props(line.getArgs()); + + Properties props; + try { + props = Load.argsToProperties(line.getArgs()); + } catch (IOException e) { + System.err.println(e.getMessage()); + return false; + } boolean valid = true; for (Iterator it = props.keySet().iterator(); it.hasNext(); ) { diff --git a/ccm-core/src/com/arsdigita/packaging/Unload.java b/ccm-core/src/com/arsdigita/packaging/Unload.java index 9a2b2279e..3dfc080cc 100755 --- a/ccm-core/src/com/arsdigita/packaging/Unload.java +++ b/ccm-core/src/com/arsdigita/packaging/Unload.java @@ -18,12 +18,17 @@ */ package com.arsdigita.packaging; -import com.arsdigita.runtime.CCMResourceManager; -import com.arsdigita.util.Files; - -import java.io.File; -import java.io.FileFilter; +import com.arsdigita.loader.PackageLoader; +import com.arsdigita.persistence.Session; +import com.arsdigita.persistence.SessionManager; +import com.arsdigita.runtime.RuntimeConfig; +import com.arsdigita.runtime.Runtime; +import com.arsdigita.util.jdbc.Connections; +import java.io.IOException; +import java.sql.Connection; +import java.sql.SQLException; import java.util.HashSet; +import java.util.List; import java.util.Set; import org.apache.commons.cli.CommandLine; @@ -35,7 +40,7 @@ import org.apache.commons.cli.PosixParser; import org.apache.log4j.Logger; /** - * PackageTool worker class, implements the "load" command. + * PackageTool worker class, implements the "unload" command. * * It is called by class MasterTols and unloads the database schema and initial * content. @@ -47,31 +52,78 @@ import org.apache.log4j.Logger; * PACKAGE-KEYS one or more space separated names of modules (package-key, e.g. * ccm-cms-types-event) which should be loaded into database and * configuration registry - * Options: [--config] Removes entries in the registry (configuration repo) - * if set prevents any of the three data load steps - * described before to be executed! + * Options: [-usage] Display a usage message for load command + * [-help|--help] Display a help message for load command + * [--packagekeys-file FILE] Reads list of packages to load from + * File (in addition to command line) + * [--schema] Loads just the schema for a package into the + * database, no data, no initializer + * [--data] Loads just data into the database, schema must + * exist already, initializers are not recorded + * [--init] Records the initializer and classes into database + * [--config] Removes entries in the registry (configuration + * repo) if set prevents any of the three data + * load steps described before to be executed! + * [--recursive] Recursively load required packages * - * @author Rafael H. Schloming <rhs@mit.edu> tosmers; - * @version $Revision: #7 $ $Date: 2015/03/29 $ - * @version $Id: Unload.java 736 2015-03-29 14:22:20Z tosmers $ + * @author Tobias Osmers + * @version $Revision: #11 $ $Date: 2015/04/27 $ */ -class Unload extends Command { - +class Unload extends Command implements LoadCenter { + + private static final LoadCenterDelegate delegate = new LoadCenterDelegate(); + private static final Logger logger = Logger.getLogger(Unload.class); - private static final Options OPTIONS = new Options(); + + //for OLD unloadConfig Method + private static final Set EXCLUDE = new HashSet(); + + //Initializes all option-flags. static { logger.debug("Static initalizer starting..."); + OPTIONS.addOption + (OptionBuilder + .hasArg() + .withLongOpt("packagekeys-file") + .withArgName("FILE") + .withDescription( + "Use PACKAGE_KEYS from FILE instead of command line") + .create()); OPTIONS.addOption (OptionBuilder .hasArg(false) .withLongOpt("config") .withDescription("Unload configuration") .create()); + OPTIONS.addOption + (OptionBuilder + .hasArg(false) + .withLongOpt("schema") + .withDescription("Unload schema") + .create()); + OPTIONS.addOption + (OptionBuilder + .hasArg(false) + .withLongOpt("data") + .withDescription("Unload data") + .create()); + OPTIONS.addOption + (OptionBuilder + .hasArg(false) + .withLongOpt("init") + .withDescription("Unload initializers") + .create()); +// OPTIONS.addOption +// (OptionBuilder +// .hasArg(false) +// .withLongOpt("recursive") +// .withDescription("Recursively load required packages") +// .create()); logger.debug("Static initalizer finished."); } - private static final Set EXCLUDE = new HashSet(); + //Initializes all excluded files. static { logger.debug("Static initalizer starting..."); EXCLUDE.add("resin.conf"); @@ -90,12 +142,16 @@ class Unload extends Command { } /** - * Invoked from the central tool "MasterTool" to execute the load process. + * Invoked from the central tool "MasterTool" to execute the unload process. * - * @param args - * @return + * @param args The parameters and option-flags + * @return true if successful, false otherwise */ + @Override public boolean run(String[] args) { + + //Takes the option-set and the arguments and parses + //them into a command-line CommandLine line; try { line = new PosixParser().parse(OPTIONS, args); @@ -104,27 +160,258 @@ class Unload extends Command { return false; } - String[] packages = line.getArgs(); - if (packages.length == 0) { - usage(OPTIONS, System.err); + //[--usage || --help] + //If set, prints an info-output and returns the function + //with true + if (line.hasOption("usage") || line.hasOption("help")) { + usage(OPTIONS, System.out, "PACKAGE-KEYS"); + return true; + } + + //Gets all packages which will be unloaded and assures + //that this list is not empty. + List packages; + try { + packages = getAllPackages(line); + if (packages.isEmpty()) { + usage(OPTIONS, System.err, "PACKAGE-KEYS"); + return false; + } + } catch (IOException e) { + System.err.println(e.getMessage()); return false; } - - if (line.hasOption("config")) { - // XXX: This just deletes everything. - File conf = CCMResourceManager.getConfigDirectory(); - File[] files = conf.listFiles(new FileFilter() { - public boolean accept(File file) { - return !EXCLUDE.contains(file.getName()); - } - }); - - for (int i = 0; i < files.length; i++) { - Files.delete(files[i]); - } + + //Gets all unloaders corresponding to the packages which + //will be unloaded. + Loader[] unloaders; + try { + unloaders = getAllLoaders(line, packages, LoadType.UNLOAD); + } catch (Error e) { + System.err.println(e.getMessage()); + return false; } - - return true; + + //Determines if all steps have to be performed. + final boolean all = hasAllOptions(line); + + //Checks for existence and accessibility of a database. + if (!checkDatabase()) { + return false; + } + + Connection conn = Connections.acquire( + RuntimeConfig.getConfig().getJDBCURL()); + Session ssn = null; + + //Unload + boolean result = true; + if (all || line.hasOption("init")) { + result &= unloadInits(conn, ssn, unloaders); + } + //TODO: Recursivly changing the reading of the ExternalLinkUnloader.java + if (all || line.hasOption("data")) { + result &= unloadData(ssn, unloaders); + } + //Finished + if (all || line.hasOption("schema")) { + result &= unloadSchema(conn, unloaders); + } + //Finished + if (all || line.hasOption("config")) { + //Removes the configurations for the packages to be unloaded. + result &= unloadConfig(packages); + //result &= unloadConfig(); --OLD + } + + return result; } -} + /** + * Gets all packages to be unloaded either from the command-line or a file, + * if the option flag [--packagekeys-file FILE] has been set and puts them + * in a list of packages (by their package-keys). + * + * @param line The command-line with all options and arguments + * @return The list of packages to be unloaded + * @throws IOException + */ + @Override + public List getAllPackages(CommandLine line) throws IOException { + return delegate.getAllPackages(line); + } + + /** + * Gets all loaders to the given package-list and sorts them before re- + * turning. Creates a map that assigns to every package-key an equivalent + * loader. This loader contains a bunch of informations (required, + * provided, scripts) provided by an ".load"-file. Then all loaders from + * the map (pkg-key -> loader) are composed into an array of loaders and + * sorted. + * + * @param line The command-line with all options and arguments + * @param packages The list of packages to be loaded + * @param loadType Weather packages are been loaded or unloaded + * @return A sorted list of loaders + * @throws Error + */ + @Override + public Loader[] getAllLoaders(CommandLine line, List packages, + LoadType loadType) throws Error { + return delegate.getAllLoaders(line, packages, loadType); + } + + /** + * Determines if all steps (config, schema, data, inits) have to be + * performed. + * + * @param line The command-line with all options and arguments + * @return True if all options need to be performed + */ + @Override + public boolean hasAllOptions(CommandLine line) { + return delegate.hasAllOptions(line); + } + + /** + * Checks existence and accessibility of the database and does a rollback + * if necessary. + * + * @return True on success, otherwise false + */ + @Override + public boolean checkDatabase() { + return delegate.checkDatabase(); + } + + /** + * Sets back the configuration to the original packages. Goes through + * all packages from the configuration-context and removes the ones + * contained in the list of packages which will be loaded. + * + * @param config The configuration + * @param packages The packages to be loaded + * @return True on success, otherwise false + */ + @Override + public boolean rollbackConfig(Config config, List packages) { + return delegate.rollbackConfig(config, packages); + } + + /** + * Checks the initializer dependencies set in the ".load"-file. + * + * @param loaders A list of loaders to the corresponding packages + * to-be-loaded + * @param sessionName Name of the session + * @return true on success, otherwise false + */ + @Override + public boolean checkInitializerDependencies(final Loader[] loaders, + String sessionName) { + return delegate.checkInitializerDependencies(loaders, sessionName); + } + + /** + * Unloads the initializers. needed? + * + * @param ssn The session for the db-connection + * @param unloaders The list of unloaders from the packages to be unloaded + * @return true on success, otherwise false + */ + private boolean unloadInits(Connection conn, Session ssn, Loader[] unloaders) { + if (ssn == null) { + new Runtime().startup(); + ssn = SessionManager.getSession(); + } + boolean passed = true; + if (PackageLoader.exists(conn, "inits")) { + passed &= checkInitializerDependencies(unloaders, "unloader"); + if (!passed) { + return false; + } + } + for (Loader unloader : unloaders) { + unloader.unloadInits(ssn); + } + return true; + } + + /** + * Unloads the data. + * + * @param ssn The session for the db-connection + * @param unloaders The list of unloaders from the packages to be unloaded + * @return true on success, otherwise false + */ + private boolean unloadData(Session ssn, Loader[] unloaders) { + if (ssn == null) { + new Runtime().startup(); + ssn = SessionManager.getSession(); + } + boolean passed = true; + for (Loader unloader : unloaders) { + passed &= unloader.checkData(ssn); + if (!passed) { + return false; + } + unloader.unloadData(ssn); + } + return true; + } + + /** + * Unloads the schema. + * + * @param conn The connection to the database + * @param unloaders The list of unloaders from the packages to be unloaded + * @return true on success, otherwise false + */ + private boolean unloadSchema(Connection conn, Loader[] unloaders) { + boolean passed = true; + for (Loader unloader : unloaders) { + passed &= unloader.checkSchema(); + if (!passed) { + return false; + } + unloader.unloadSchema(conn); + } + try { + conn.commit(); + } catch (SQLException e) { + System.err.println(e.getMessage()); + return false; + } + return true; + } + + /** + * Unloads the configuration by running a rollback. + * + * @param packages The packages to be loaded + * @return true on success, otherwise false + */ + private boolean unloadConfig(List packages) { + return rollbackConfig(null, packages); + } + + /** + * OLD VERSION + * Unloads the configuration. Useful??????? + * + * @return true on success, otherwise false + */ +// private boolean unloadConfig() { +// File conf = CCMResourceManager.getConfigDirectory(); +// File[] files = conf.listFiles(new FileFilter() { +// public boolean accept(File file) { +// return !EXCLUDE.contains(file.getName()); +// } +// }); +// +// for (int i = 0; i < files.length; i++) { +// Files.delete(files[i]); +// } +// return true; +// } +} \ No newline at end of file diff --git a/ccm-core/src/com/arsdigita/packaging/Upgrade.java b/ccm-core/src/com/arsdigita/packaging/Upgrade.java index 01fe4f053..77f1fe566 100755 --- a/ccm-core/src/com/arsdigita/packaging/Upgrade.java +++ b/ccm-core/src/com/arsdigita/packaging/Upgrade.java @@ -188,10 +188,10 @@ class Upgrade extends Command { String[] params = line.getOptionValues("parameters"); LinkedList ll = new LinkedList(); if (params != null) { - for (int i = 0; i < params.length; i++) { - String[] split = StringUtils.split(params[i], ','); - for (int j = 0; j < split.length; j++) { - ll.add(split[j]); + for (String param : params) { + String[] split = StringUtils.split(param, ','); + for (String split1 : split) { + ll.add(split1); } } } diff --git a/ccm-core/src/com/arsdigita/runtime/AbstractScript.java b/ccm-core/src/com/arsdigita/runtime/AbstractScript.java index 04e3c748e..fe7fcea27 100755 --- a/ccm-core/src/com/arsdigita/runtime/AbstractScript.java +++ b/ccm-core/src/com/arsdigita/runtime/AbstractScript.java @@ -49,6 +49,7 @@ public abstract class AbstractScript extends AbstractParameterContext * @param context the context in which to run the script */ + @Override public abstract void run(ScriptContext context); } diff --git a/tools-ng/ecdc/scriptlib/build-ccm.xml b/tools-ng/ecdc/scriptlib/build-ccm.xml index 7d1ad5b15..b6592c567 100644 --- a/tools-ng/ecdc/scriptlib/build-ccm.xml +++ b/tools-ng/ecdc/scriptlib/build-ccm.xml @@ -242,7 +242,64 @@ - + + + + Unloading bundle from ${this.bundle.folder} from ${ccmhome} + + + + + + + + + + + Unload ${applications} without further configuration specifications but using build in defaults. + + + + + + + + + + Unload initializers for ${applications} (--init) + + + + + + + + + Unload schema only for ${applications} (--schema) + + + + + + + + + Unregisters configuration from ${this.bundle.folder} for ${applications} from registry + + + + + +