libreccm-legacy/ccm-core/src/com/arsdigita/packaging/Load.java

708 lines
24 KiB
Java
Executable File

/*
* 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;
// pboy (Jan.09): deprecated classes and methods removed,
// comments should be deleted after extensiv testing
// deprecated, no replacement specified by author,
// created InteractiveParameterReader analogous to CompoundParameterReader
//import com.arsdigita.runtime.InteractiveParameterLoader;
import com.arsdigita.runtime.InteractiveParameterReader;
import com.arsdigita.runtime.ConfigRegistry;
import com.arsdigita.runtime.RegistryConfig;
import com.arsdigita.runtime.RuntimeConfig;
import com.arsdigita.runtime.Startup;
import com.arsdigita.util.UncheckedWrapperException;
// deprecated, use c.ad.util.JavaPropertyReader instead
// import com.arsdigita.util.config.JavaPropertyLoader;
import com.arsdigita.util.JavaPropertyReader;
import com.arsdigita.util.jdbc.Connections;
// deprecated:
// import com.arsdigita.util.parameter.CompoundParameterLoader;
import com.arsdigita.util.parameter.CompoundParameterReader;
import com.arsdigita.util.parameter.Parameter;
import com.arsdigita.util.parameter.ParameterContext;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
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.Set;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
/**
* PackageTool worker class, implements the "load" command.
*
* Loads the database schema and initial content.
*
* Called by PackageTool
*
* @author Rafael H. Schloming <rhs@mit.edu>
* @version $Revision: #29 $ $Date: 2004/08/16 $
**/
class Load extends Command {
public final static String versionId =
"$Id: Load.java 736 2005-09-01 10:46:05Z sskracic $" +
" by $Author: sskracic $, " +
"$DateTime: 2004/08/16 18:10:38 $";
private static final Options OPTIONS = getOptions();
static {
OPTIONS.addOption
(OptionBuilder
.hasArg(false)
.withLongOpt("config")
.withDescription("Load configuration")
.create());
OPTIONS.addOption
(OptionBuilder
.hasArg(false)
.withLongOpt("schema")
.withDescription("Load schema")
.create());
OPTIONS.addOption
(OptionBuilder
.hasArg(false)
.withLongOpt("data")
.withDescription("Load data")
.create());
OPTIONS.addOption
(OptionBuilder
.hasArg(false)
.withLongOpt("init")
.withDescription("Load initializers")
.create());
OPTIONS.addOption
(OptionBuilder
.hasArg()
.withLongOpt("parameter-file")
.withArgName("FILE")
.withDescription("Use key-value pairs from FILE")
.create());
OPTIONS.addOption
(OptionBuilder
.hasArg(false)
.withLongOpt("interactive")
.withDescription("Prompt for required parameter values")
.create());
OPTIONS.addOption
(OptionBuilder
.hasArgs()
.withLongOpt("parameters")
.withArgName("KEY=VALUE ...")
.withDescription("Use key-value pairs from command line")
.create());
OPTIONS.addOption
(OptionBuilder
.hasArg(false)
.withLongOpt("recursive")
.withDescription("Recursively load required packages")
.create());
/*OPTIONS.addOption
(OptionBuilder
.hasArg()
.withLongOpt("log")
.withArgName("FILE")
.withDescription("Log parameter values as key-value " +
"pairs in FILE")
.create());*/
}
public Load() {
super("load", "Load a CCM package");
}
public boolean run(String[] args) {
CommandLine line;
try {
line = new PosixParser().parse(OPTIONS, args);
} catch (ParseException e) {
System.err.println(e.getMessage());
return false;
}
List packages = line.getArgList();
if (packages.isEmpty()) {
usage(OPTIONS, System.err, "PACKAGE-KEYS");
return false;
}
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 rc = new RegistryConfig();
rc.load();
List loaded = Arrays.asList(rc.getPackages());
Map loaders = new HashMap();
List keys = new ArrayList();
keys.addAll(packages);
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) {
return false;
}
}
ParameterMap contexts = new ParameterMap();
Properties parameters = new Properties();
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));
cpr.add(new JavaPropertyReader(parameters));
}
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);
}
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);
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)) {
return false;
}
if (line.hasOption("interactive")) {
ParameterEditor editor =
new ParameterEditor(contexts, System.in, System.out);
if (!editor.edit()) { return true; }
} else if (!contexts.validate(System.err)) {
return false;
}
if (!saveConfig(config)) {
return false;
}
Session ssn = null;
if (all || line.hasOption("schema") || line.hasOption("data")) {
Check checkdb = new CheckDB();
checkdb.run(null);
if (checkdb.getStatus() == null
|| checkdb.getStatus().equals(Check.FAIL)) {
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: " + conflicts);
}
if (conflicts.size() > 0 || missing.size() > 0) {
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 Startup().run();
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);
}
}
}
if (all || line.hasOption("init")) {
if (ssn == null) {
new Startup().run();
ssn = SessionManager.getSession();
}
for (int i = 0; i < sorted.length; i++) {
sorted[i].loadInits(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;
}
private boolean saveConfig(Config config) {
if (config != null) {
try {
config.save();
} catch (IOException e) {
System.err.println(e.getMessage());
return false;
}
}
return true;
}
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]);
}
}
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);
}
}
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;
}
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;
}
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);
}
}
return missing;
}
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;
}
private static String[] concat(String[] a, String[] b) {
if (a == null) { return b; }
if (b == null) { return a; }
String[] result = new String[a.length + b.length];
System.arraycopy(a, 0, result, 0, a.length);
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;
}
}
return false;
}
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");
}
w.flush();
Properties props = new Properties();
props.load(new ByteArrayInputStream(baos.toByteArray()));
return props;
} catch (IOException e) {
throw new UncheckedWrapperException(e);
}
}
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();
}
}
} 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();
}
}
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);
}
}
}
}