0001: /*
0002:
0003: This software is OSI Certified Open Source Software.
0004: OSI Certified is a certification mark of the Open Source Initiative.
0005:
0006: The license (Mozilla version 1.0) can be read at the MMBase site.
0007: See http://www.MMBase.org/license
0008:
0009: */
0010: package org.mmbase.module.tools;
0011:
0012: import java.util.*;
0013: import java.util.Map.Entry;
0014:
0015: import org.mmbase.bridge.Field;
0016: import org.mmbase.cache.NodeCache;
0017: import org.mmbase.core.CoreField;
0018: import org.mmbase.module.builders.Versions;
0019: import org.mmbase.module.core.*;
0020: import org.mmbase.module.corebuilders.*;
0021: import org.mmbase.storage.search.*;
0022: import org.mmbase.storage.search.implementation.*;
0023: import org.mmbase.util.*;
0024: import org.mmbase.util.logging.Logger;
0025: import org.mmbase.util.logging.Logging;
0026: import org.mmbase.util.xml.ApplicationReader;
0027: import org.xml.sax.InputSource;
0028:
0029: /**
0030: * Application installations functionality of MMAdmin.
0031: *
0032: * @author Nico Klasens
0033: * @since MMBase-1.8
0034: * @version $Id: ApplicationInstaller.java,v 1.14 2007/10/02 12:15:14 michiel Exp $
0035: */
0036: class ApplicationInstaller {
0037:
0038: private static final Logger log = Logging
0039: .getLoggerInstance(ApplicationInstaller.class);
0040:
0041: private final MMBase mmb;
0042: private final MMAdmin admin;
0043:
0044: public ApplicationInstaller(MMBase mmb, MMAdmin admin) {
0045: this .mmb = mmb;
0046: this .admin = admin;
0047: }
0048:
0049: public void installApplications() throws SearchQueryException {
0050: ResourceLoader applicationLoader = ResourceLoader
0051: .getConfigurationRoot().getChildResourceLoader(
0052: "applications");
0053: for (String appResource : applicationLoader.getResourcePaths(
0054: ResourceLoader.XML_PATTERN, false)) {
0055: ApplicationResult result = new ApplicationResult();
0056: if (!installApplication(appResource.substring(0,
0057: appResource.length() - 4), -1, null, result,
0058: new HashSet<String>(), true)) {
0059: log.error("Problem installing application : "
0060: + appResource + ", cause: "
0061: + result.getMessage());
0062: }
0063: }
0064: }
0065:
0066: /**
0067: * Installs the application
0068: * @param applicationName Name of the application file, without the xml extension
0069: * This is also assumed to be the name of teh application itself
0070: * (if not, a warning will be issued)
0071: * @param result the result object, containing error messages when the installation fails,
0072: * or the installnotice if succesfull or already installed
0073: * @param installationSet set of installations that are currently being installed.
0074: * used to check if there are circular dependencies
0075: * @param autoDeploy if true, the installation is only installed if the application is set to autodeploy
0076: * @return true if succesfull, false otherwise
0077: */
0078: public boolean installApplication(String applicationName,
0079: int requiredVersion, String requiredMaintainer,
0080: ApplicationResult result, Set<String> installationSet,
0081: boolean autoDeploy) throws SearchQueryException {
0082:
0083: if (installationSet.contains(applicationName)) {
0084: return result
0085: .error("Circular reference to application with name "
0086: + applicationName);
0087: }
0088:
0089: ApplicationReader reader = getApplicationReader(applicationName);
0090: Versions ver = (Versions) mmb.getBuilder("versions");
0091: if (reader != null) {
0092: // test autodeploy
0093: if (autoDeploy) {
0094: if (!reader.hasAutoDeploy()) {
0095: return true;
0096: } else {
0097: if (admin.getIgnoredAutodeployApplications()
0098: .contains(applicationName)) {
0099: log
0100: .info("Ignoring auto-deploy '"
0101: + applicationName
0102: + "' because specified as ignore-auto-deploy parameter of mmadmin");
0103: return true;
0104: }
0105: }
0106: }
0107: String name = reader.getName();
0108: String maintainer = reader.getMaintainer();
0109: if (requiredMaintainer != null
0110: && !maintainer.equals(requiredMaintainer)) {
0111: return result
0112: .error("Install error: " + name
0113: + " requires maintainer '"
0114: + requiredMaintainer
0115: + "' but found maintainer '"
0116: + maintainer + "'");
0117: }
0118: int version = reader.getVersion();
0119: if (requiredVersion != -1 && version != requiredVersion) {
0120: return result.error("Install error: " + name
0121: + " requires version '" + requiredVersion
0122: + "' but found version '" + version + "'");
0123: }
0124: int installedVersion = ver.getInstalledVersion(name,
0125: "application");
0126: if (installedVersion == -1 || version > installedVersion) {
0127: if (!name.equals(applicationName)) {
0128: result
0129: .warn("Application name "
0130: + name
0131: + " not the same as the base filename "
0132: + applicationName
0133: + ".\n"
0134: + "This may cause problems when referring to this application.");
0135: }
0136: // We should possibly check whether the maintainer is valid here (see sample code below).
0137: // There is currently no way to do this, though, unless we use awful queries.
0138: // what we need is a getInstalledMaintainer() method on the Versions builder
0139: /* sample code
0140: String installedMaintainer=ver.getInstalledMaintainer(name,"application");
0141: if (!maintainer.equals(installedAppMaintainer)) {
0142: return result.error("Install error: "+name+" is of maintainer '"+maintainer+"' but installed application is of maintainer '"+installedMaintainer+"'");
0143: }
0144: */
0145: // should be installed - add to installation set
0146: installationSet.add(applicationName);
0147: List<Map<String, String>> requires = reader
0148: .getRequirements();
0149: for (Map<String, String> reqapp : requires) {
0150: String reqType = reqapp.get("type");
0151: if (reqType == null
0152: || reqType.equals("application")) {
0153: String appName = reqapp.get("name");
0154: int installedAppVersion = ver
0155: .getInstalledVersion(appName,
0156: "application");
0157: String appMaintainer = reqapp.get("maintainer");
0158: int appVersion = -1;
0159: try {
0160: String appVersionAttr = reqapp
0161: .get("version");
0162: if (appVersionAttr != null)
0163: appVersion = Integer
0164: .parseInt(appVersionAttr);
0165: } catch (Exception e) {
0166: }
0167: if (installedAppVersion == -1
0168: || appVersion > installedAppVersion) {
0169: log.service("Application '"
0170: + applicationName + "' requires : "
0171: + appName);
0172: if (!installApplication(appName,
0173: appVersion, appMaintainer, result,
0174: installationSet, false)) {
0175: return false;
0176: }
0177: } else if (appMaintainer != null) {
0178: // we should possibly check whether the maintainer is valid here (see sample code below).
0179: // There is currently no way to do this, though, unless we use awful queries.
0180: // what we need is a getInstalledMaintainer() method on the Versions builder
0181: /* sample code
0182: String installedAppMaintainer=ver.getInstalledMaintainer(name,"application");
0183: if (!appMaintainer.equals(installedAppMaintainer)) {
0184: return result.error("Install error: "+name+" requires maintainer '"+appMaintainer+"' but found maintainer '"+installedAppMaintainer+"'");
0185: }
0186: */
0187: }
0188: }
0189: }
0190: // note: currently name and application file name should be the same
0191: if (installedVersion == -1) {
0192: log.info("Installing application : " + name);
0193: } else {
0194: log.info("installing application : " + name
0195: + " new version from " + installedVersion
0196: + " to " + version);
0197: }
0198: if (installBuilders(reader.getNeededBuilders(),
0199: "applications/" + applicationName, result)
0200: && installRelDefs(reader.getNeededRelDefs(),
0201: result)
0202: && installAllowedRelations(reader
0203: .getAllowedRelations(), result)
0204: && installDataSources(reader.getDataSources(),
0205: applicationName, result)
0206: && installRelationSources(reader
0207: .getRelationSources(), applicationName,
0208: result)) {
0209:
0210: if (installedVersion == -1) {
0211: ver.setInstalledVersion(name, "application",
0212: maintainer, version);
0213: } else {
0214: ver.updateInstalledVersion(name, "application",
0215: maintainer, version);
0216: }
0217: log.info("Application '" + name
0218: + "' deployed succesfully.");
0219: result
0220: .success("Application loaded oke\n\n"
0221: + "The application has the following install notice for you : \n\n"
0222: + reader.getInstallNotice());
0223: }
0224: // installed or failed - remove from installation set
0225: installationSet.remove(applicationName);
0226: } else {
0227: // only return this message if the application is the main (first) application
0228: // and if it was not auto-deployed (as in that case messages would not be deemed very useful)
0229: if (installationSet.size() == 1) {
0230: result
0231: .success("Application was allready loaded (or a higher version)\n\n"
0232: + "To remind you here is the install notice for you again : \n\n"
0233: + reader.getInstallNotice());
0234: }
0235: }
0236: } else {
0237: result
0238: .error("Install error: can't find xml file: applications/"
0239: + applicationName + ".xml");
0240: }
0241: return result.isSuccess();
0242: }
0243:
0244: /**
0245: * @javadoc
0246: * @since MMBase-1.7
0247: */
0248: protected boolean installDataSources(
0249: List<Map<String, String>> dataSources, String appName,
0250: ApplicationResult result) {
0251: MMObjectBuilder syncbul = mmb.getBuilder("syncnodes");
0252:
0253: List<MMObjectNode> nodeFieldNodes = new ArrayList<MMObjectNode>(); // a temporary list with all nodes that have NODE fields, which should be synced, later.
0254: if (syncbul != null) {
0255: for (Map<String, String> bh : dataSources) {
0256: XMLNodeReader nodeReader = getNodeReader(bh, appName);
0257: if (nodeReader == null) {
0258: continue;
0259: } else {
0260: installDatasource(syncbul, nodeReader,
0261: nodeFieldNodes, result);
0262: }
0263: }
0264:
0265: treatNodeFields(nodeFieldNodes, syncbul);
0266:
0267: return result.isSuccess();
0268: } else {
0269: return result
0270: .error("Application installer : can't reach syncnodes builder"); //
0271: }
0272: }
0273:
0274: private void installDatasource(MMObjectBuilder syncbul,
0275: XMLNodeReader nodeReader,
0276: List<MMObjectNode> nodeFieldNodes, ApplicationResult result) {
0277: String exportsource = nodeReader.getExportSource();
0278: int timestamp = nodeReader.getTimeStamp();
0279:
0280: nodeReader.setLoadBinaries(false);
0281:
0282: // loop all nodes , and add to syncnodes.
0283: for (Iterator<MMObjectNode> n = nodeReader.getNodes(mmb)
0284: .iterator(); n.hasNext();) {
0285: try {
0286: MMObjectNode newNode = n.next();
0287: nodeReader.loadBinairyFields(newNode);
0288:
0289: int exportnumber = newNode.getIntValue("number");
0290: if (existsSyncnode(syncbul, exportsource, exportnumber)) {
0291: // XXX To do : we may want to load the node and check/change the fields
0292: log.debug("node allready installed : "
0293: + exportnumber);
0294: } else {
0295: newNode.setValue("number", -1);
0296: int localnumber = doKeyMergeNode(syncbul, newNode,
0297: exportsource, result);
0298: if (localnumber != -1) { // this node was not yet imported earlier
0299: createSyncnode(syncbul, exportsource,
0300: timestamp, exportnumber, localnumber);
0301: if (localnumber == newNode.getNumber()) {
0302: findFieldsOfTypeNode(nodeFieldNodes,
0303: exportsource, newNode);
0304: }
0305: }
0306: NodeCache.getCache().remove(localnumber);
0307: }
0308: n.remove();
0309: } catch (SearchQueryException sqe) {
0310: log.error(sqe);
0311: }
0312: }
0313: }
0314:
0315: private void findFieldsOfTypeNode(
0316: List<MMObjectNode> nodeFieldNodes, String exportsource,
0317: MMObjectNode newNode) {
0318: // determine if there were NODE fields, which need special treatment later.
0319: Collection<CoreField> fields = newNode.getBuilder().getFields();
0320: Iterator<CoreField> i = fields.iterator();
0321: while (i.hasNext()) {
0322: CoreField field = i.next();
0323:
0324: // Fields with type NODE and notnull=true will be handled
0325: // by the doKeyMergeNode() method.
0326: if (field.getType() == Field.TYPE_NODE
0327: && !field.getName().equals("number")
0328: && !field.isRequired()) {
0329:
0330: newNode.storeValue("__exportsource", exportsource);
0331: nodeFieldNodes.add(newNode);
0332: break;
0333: }
0334: }
0335: }
0336:
0337: private void treatNodeFields(List<MMObjectNode> nodeFieldNodes,
0338: MMObjectBuilder syncbul) {
0339: Iterator<MMObjectNode> i = nodeFieldNodes.iterator();
0340: while (i.hasNext()) {
0341: MMObjectNode importedNode = i.next();
0342: String exportsource = (String) importedNode.getValues()
0343: .get("__exportsource");
0344: // clean it up
0345: importedNode.storeValue("__exportsource", null); // hack to remove it.
0346:
0347: Collection<CoreField> fields = importedNode.getBuilder()
0348: .getFields();
0349: Iterator<CoreField> j = fields.iterator();
0350: while (j.hasNext()) {
0351: CoreField def = j.next();
0352: String fieldName = def.getName();
0353: if (def.getType() == Field.TYPE_NODE
0354: && !fieldName.equals("number")
0355: && !fieldName.equals("snumber")
0356: && !fieldName.equals("dnumber")
0357: && !fieldName.equals("rnumber")) {
0358:
0359: updateFieldWithTypeNode(syncbul, importedNode,
0360: exportsource, fieldName);
0361: }
0362: }
0363: if (importedNode.isChanged()) {
0364: importedNode.commit();
0365: }
0366: }
0367: }
0368:
0369: /**
0370: * @javadoc !!!
0371: */
0372: private int doKeyMergeNode(MMObjectBuilder syncbul,
0373: MMObjectNode newNode, String exportsource,
0374: ApplicationResult result) {
0375: MMObjectBuilder bul = newNode.getBuilder();
0376: if (bul != null) {
0377: Collection<CoreField> vec = bul.getFields();
0378: Constraint constraint = null;
0379: NodeSearchQuery query = null;
0380: for (CoreField def : vec) {
0381: // check for notnull fields with type NODE.
0382: if (def.getType() == Field.TYPE_NODE
0383: && !def.getName().equals("number")
0384: && !def.getName().equals("otype")
0385: && def.isRequired()) {
0386:
0387: // Dangerous territory here.
0388: // The node contains a reference to another node.
0389: // The referenced node has to exist when this node is inserted.
0390: // trying to update the node.
0391: updateFieldWithTypeNode(syncbul, newNode,
0392: exportsource, def.getName());
0393: if (newNode.getIntValue(def.getName()) == -1) {
0394: // guess that failed
0395: result
0396: .error("Insert of node "
0397: + newNode
0398: + " failed. Field '"
0399: + def.getName()
0400: + "' with type NODE is not allowed to have a null value. "
0401: + "The referenced node is not found. Try to reorder the nodes so the referenced node is imported before this one.");
0402: return -1;
0403: }
0404: }
0405:
0406: // generation of key constraint to check if there is a node already present.
0407: // if a node is present then we can't insert this one.
0408: if (def.isUnique()) {
0409: int type = def.getType();
0410: String name = def.getName();
0411: if (type == Field.TYPE_STRING) {
0412: String value = newNode.getStringValue(name);
0413: if (query == null) {
0414: query = new NodeSearchQuery(bul);
0415: }
0416: StepField field = query.getField(def);
0417: Constraint newConstraint = new BasicFieldValueConstraint(
0418: field, value);
0419: if (constraint == null) {
0420: constraint = newConstraint;
0421: } else {
0422: BasicCompositeConstraint compConstraint = new BasicCompositeConstraint(
0423: CompositeConstraint.LOGICAL_AND);
0424: compConstraint.addChild(constraint);
0425: compConstraint.addChild(newConstraint);
0426: constraint = compConstraint;
0427: }
0428: }
0429: }
0430: }
0431: if (query != null && constraint != null) {
0432: query.setConstraint(constraint);
0433: try {
0434: List<MMObjectNode> nodes = bul.getNodes(query);
0435: if (nodes.size() > 0) {
0436: MMObjectNode oldNode = nodes.get(0);
0437: return oldNode.getIntValue("number");
0438: }
0439: } catch (SearchQueryException sqe) {
0440: result
0441: .error("Application installer can't search builder storage ("
0442: + sqe.getMessage() + ")");
0443: return -1;
0444: }
0445: }
0446:
0447: int localnumber = newNode.insert("import");
0448: if (localnumber == -1) {
0449: result.error("Insert of node " + newNode + " failed.");
0450: }
0451: return localnumber;
0452:
0453: } else {
0454: result
0455: .error("Application installer can't find builder for : "
0456: + newNode);
0457: return -1;
0458: }
0459: }
0460:
0461: /** update the field with the real node number of the referenced node
0462: *
0463: * @param syncbul syncnode builder
0464: * @param importedNode Node to update
0465: * @param exportsource export source of the node to update
0466: * @param fieldname name of the field
0467: */
0468: private void updateFieldWithTypeNode(MMObjectBuilder syncbul,
0469: MMObjectNode importedNode, String exportsource,
0470: String fieldname) {
0471:
0472: int exportnumber;
0473: try {
0474: exportnumber = Integer.parseInt((String) importedNode
0475: .getValues().get("__" + fieldname));
0476: } catch (Exception e) {
0477: exportnumber = -1;
0478: }
0479:
0480: // clean it up (don't know if this is necessary, but don't risk anything!)
0481: importedNode.storeValue("__" + fieldname, null);
0482:
0483: int localNumber = -1;
0484:
0485: List<MMObjectNode> syncnodes = null;
0486: try {
0487: syncnodes = getSyncnodes(syncbul, exportsource,
0488: exportnumber);
0489: } catch (SearchQueryException e) {
0490: log.warn("Search for exportnumber " + exportnumber
0491: + " exportsource " + exportsource + "failed", e);
0492: }
0493: if (syncnodes != null && !syncnodes.isEmpty()) {
0494: MMObjectNode n2 = syncnodes.get(0);
0495: localNumber = n2.getIntValue("localnumber");
0496: }
0497: if (localNumber != -1) { // leave it unset in that case, because foreign keys whine otherwise (so, if you have foreign keys (e.g. hsql), the field _must not_ be required).
0498: importedNode.setValue(fieldname, localNumber);
0499: }
0500: }
0501:
0502: /**
0503: * @javadoc
0504: */
0505: boolean installRelationSources(List<Map<String, String>> ds,
0506: String appname, ApplicationResult result) {
0507: MMObjectBuilder syncbul = mmb.getBuilder("syncnodes");
0508: InsRel insRel = mmb.getInsRel();
0509: if (syncbul != null) {
0510: List<MMObjectNode> nodeFieldNodes = new ArrayList<MMObjectNode>(); // a temporary list with all nodes that have NODE fields, which should be synced, later.
0511: for (Map<String, String> bh : ds) {
0512: XMLRelationNodeReader nodereader = getRelationNodeReader(
0513: appname, bh);
0514: if (nodereader == null) {
0515: continue;
0516: } else {
0517: installRelationSource(syncbul, insRel, nodereader,
0518: nodeFieldNodes, result);
0519: }
0520: }
0521: treatNodeFields(nodeFieldNodes, syncbul);
0522: } else {
0523: result
0524: .error("Application installer : can't reach syncnodes builder");
0525: }
0526: return result.isSuccess();
0527: }
0528:
0529: private void installRelationSource(MMObjectBuilder syncbul,
0530: InsRel insRel, XMLRelationNodeReader nodereader,
0531: List<MMObjectNode> nodeFieldNodes, ApplicationResult result) {
0532: String exportsource = nodereader.getExportSource();
0533: int timestamp = nodereader.getTimeStamp();
0534:
0535: for (Iterator<MMObjectNode> n = (nodereader.getNodes(mmb))
0536: .iterator(); n.hasNext();) {
0537: try {
0538: MMObjectNode newNode = n.next();
0539: int exportnumber = newNode.getIntValue("number");
0540:
0541: if (existsSyncnode(syncbul, exportsource, exportnumber)) {
0542: // XXX To do : we may want to load the relation node and check/change the fields
0543: log.debug("node allready installed : "
0544: + exportnumber);
0545: } else {
0546: newNode.setValue("number", -1);
0547: // The following code determines the 'actual' (synced) numbers for the destination and source nodes
0548: // This will normally work well, however:
0549: // It is _theoretically_ possible that one or both nodes are _themselves_ relation nodes.
0550: // (since relations are nodes).
0551: // Due to the order in which syncing takles place, it is possible that such structures will fail
0552: // to get imported.
0553: // ye be warned.
0554:
0555: // find snumber
0556: int snumber = newNode.getIntValue("snumber");
0557: List<MMObjectNode> snumberNodes = getSyncnodes(
0558: syncbul, exportsource, snumber);
0559: if (!snumberNodes.isEmpty()) {
0560: MMObjectNode n2 = snumberNodes.get(0);
0561: snumber = n2.getIntValue("localnumber");
0562: } else {
0563: snumber = -1;
0564: }
0565: newNode.setValue("snumber", snumber);
0566:
0567: // find dnumber
0568: int dnumber = newNode.getIntValue("dnumber");
0569: List<MMObjectNode> dnumberNodes = getSyncnodes(
0570: syncbul, exportsource, dnumber);
0571: if (!dnumberNodes.isEmpty()) {
0572: MMObjectNode n2 = dnumberNodes.get(0);
0573: dnumber = n2.getIntValue("localnumber");
0574: } else {
0575: dnumber = -1;
0576: }
0577: newNode.setValue("dnumber", dnumber);
0578:
0579: int localnumber = -1;
0580: if (snumber != -1 && dnumber != -1) {
0581: // test whether a relation with the proposed snumber/dnumber/rnumber already exists
0582: // if so, skip this relation when the same
0583: if (relationAlreadyExists(insRel, newNode,
0584: snumber, dnumber)) {
0585: log
0586: .warn("Application tries to add relation which already exists. "
0587: + "Skipping relation with exportnumber "
0588: + exportnumber);
0589: } else {
0590: localnumber = newNode.insert("import");
0591: if (localnumber != -1) {
0592: createSyncnode(syncbul, exportsource,
0593: timestamp, exportnumber,
0594: localnumber);
0595: if (localnumber == newNode.getNumber()) {
0596: findFieldsOfTypeNode(
0597: nodeFieldNodes,
0598: exportsource, newNode);
0599: }
0600: }
0601: }
0602: } else {
0603: result
0604: .error("Cannot sync relation (exportnumber=="
0605: + exportnumber
0606: + ", snumber:"
0607: + snumber
0608: + ", dnumber:"
0609: + dnumber + ")");
0610: }
0611: }
0612: } catch (SearchQueryException sqe) {
0613: log.error(sqe);
0614: }
0615: }
0616: }
0617:
0618: private boolean existsSyncnode(MMObjectBuilder syncbul,
0619: String exportsource, int exportnumber)
0620: throws SearchQueryException {
0621: List<MMObjectNode> nodes = getSyncnodes(syncbul, exportsource,
0622: exportnumber);
0623: return !nodes.isEmpty();
0624: }
0625:
0626: private List<MMObjectNode> getSyncnodes(MMObjectBuilder syncbul,
0627: String exportsource, int exportnumber)
0628: throws SearchQueryException {
0629: NodeSearchQuery existQuery = new NodeSearchQuery(syncbul);
0630: BasicFieldValueConstraint constraint1 = new BasicFieldValueConstraint(
0631: existQuery.getField(syncbul.getField("exportnumber")),
0632: exportnumber);
0633: BasicFieldValueConstraint constraint2 = new BasicFieldValueConstraint(
0634: existQuery.getField(syncbul.getField("exportsource")),
0635: exportsource);
0636: BasicCompositeConstraint constraint = new BasicCompositeConstraint(
0637: CompositeConstraint.LOGICAL_AND);
0638: constraint.addChild(constraint1);
0639: constraint.addChild(constraint2);
0640: existQuery.setConstraint(constraint);
0641: List<MMObjectNode> nodes = syncbul.getNodes(existQuery);
0642: if (nodes == null) {
0643: // could this happen?
0644: nodes = new ArrayList<MMObjectNode>();
0645: }
0646: return nodes;
0647: }
0648:
0649: private void createSyncnode(MMObjectBuilder syncbul,
0650: String exportsource, int timestamp, int exportnumber,
0651: int localnumber) {
0652: MMObjectNode syncnode = syncbul.getNewNode("import");
0653: syncnode.setValue("exportsource", exportsource);
0654: syncnode.setValue("exportnumber", exportnumber);
0655: syncnode.setValue("timestamp", timestamp);
0656: syncnode.setValue("localnumber", localnumber);
0657: syncnode.insert("import");
0658: }
0659:
0660: /**
0661: * This method uses the {@link ResourceLoader} to fetch an application by name. for this purpose
0662: * it requests the resource by adding <code>applications/</code> to the start of the appName and appends <code>.xml</core> to the end
0663: * @param appName the name of the application to be read.
0664: * @return the ApplicationReader for the application, or null is the application wat not found or an exception occured. In the later a message is logged
0665: */
0666: private ApplicationReader getApplicationReader(String appName) {
0667: String resourceName = appName + ".xml";
0668: try {
0669: ResourceLoader applicationLoader = ResourceLoader
0670: .getConfigurationRoot().getChildResourceLoader(
0671: "applications");
0672: InputSource is = applicationLoader
0673: .getInputSource(resourceName);
0674: if (is == null) {
0675: return null;
0676: }
0677: return new ApplicationReader(is);
0678: } catch (Exception e) {
0679: log.error("error while reading application from resource "
0680: + resourceName + " : " + e.getMessage(), e);
0681: return null;
0682: }
0683: }
0684:
0685: private XMLNodeReader getNodeReader(Map<String, String> bh,
0686: String appName) {
0687: XMLNodeReader nodeReader = null;
0688:
0689: String path = bh.get("path");
0690: ResourceLoader applicationLoader = ResourceLoader
0691: .getConfigurationRoot().getChildResourceLoader(
0692: "applications");
0693: InputSource is = null;
0694: try {
0695: is = applicationLoader.getInputSource(path);
0696: } catch (Exception e) {
0697: log.info("No datasource resource " + path);
0698: }
0699: if (is != null) {
0700: nodeReader = new XMLNodeReader(is, applicationLoader
0701: .getChildResourceLoader(appName));
0702: }
0703: return nodeReader;
0704: }
0705:
0706: private XMLRelationNodeReader getRelationNodeReader(String appname,
0707: Map<String, String> bh) {
0708: XMLRelationNodeReader nodereader = null;
0709:
0710: String path = bh.get("path");
0711: ResourceLoader applicationLoader = ResourceLoader
0712: .getConfigurationRoot().getChildResourceLoader(
0713: "applications");
0714: InputSource is = null;
0715: try {
0716: is = applicationLoader.getInputSource(path);
0717: } catch (Exception e) {
0718: log.info("No relationsource resource " + path);
0719: }
0720: if (is != null) {
0721: nodereader = new XMLRelationNodeReader(is,
0722: applicationLoader.getChildResourceLoader(appname));
0723: }
0724: return nodereader;
0725: }
0726:
0727: private boolean relationAlreadyExists(InsRel insRel,
0728: MMObjectNode newNode, int snumber, int dnumber) {
0729: boolean relationAlreadyExists = false;
0730: MMObjectNode testNode = insRel.getRelation(snumber, dnumber,
0731: newNode.getIntValue("rnumber"));
0732: if (testNode != null) {
0733: relationAlreadyExists = true;
0734: Map<String, Object> values = newNode.getValues();
0735: for (Entry<String, Object> entry : values.entrySet()) {
0736: String newFieldName = entry.getKey();
0737: if (!insRel.hasField(newFieldName)) {
0738: Object newValue = entry.getValue();
0739: Object testValue = testNode.getValue(newFieldName);
0740: if (!newValue.equals(testValue)) {
0741: relationAlreadyExists = false;
0742: }
0743: }
0744: }
0745: }
0746: return relationAlreadyExists;
0747: }
0748:
0749: /**
0750: * Checks and if required installs needed relation definitions.
0751: * Retrieves, for each reldef entry, the attributes, and passes these on to {@link #installRelDef}
0752: * @param reldefs a list of hashtables. Each hashtable represents a reldef entry, and contains a list of name-value
0753: * pairs (the reldef attributes).
0754: * @return Always <code>true</code> (?)
0755: */
0756: private boolean installRelDefs(List<Map<String, String>> reldefs,
0757: ApplicationResult result) {
0758: for (Map<String, String> bh : reldefs) {
0759: String source = bh.get("source");
0760: String target = bh.get("target");
0761: String direction = bh.get("direction");
0762: String guisourcename = bh.get("guisourcename");
0763: String guitargetname = bh.get("guitargetname");
0764: // retrieve builder info
0765: int builder = -1;
0766: if (RelDef.usesbuilder) {
0767: String buildername = bh.get("builder");
0768: // if no 'builder' attribute is present (old format), use source name as builder name
0769: if (buildername == null) {
0770: buildername = bh.get("source");
0771: }
0772: builder = mmb.getTypeDef().getIntValue(buildername);
0773: }
0774: // is not explicitly set to unidirectional, direction is assumed to be bidirectional
0775: if ("unidirectional".equals(direction)) {
0776: if (!installRelDef(source, target, 1, guisourcename,
0777: guitargetname, builder, result))
0778: return false;
0779: } else {
0780: if (!installRelDef(source, target, 2, guisourcename,
0781: guitargetname, builder, result))
0782: return false;
0783: }
0784: }
0785: return true;
0786: }
0787:
0788: /**
0789: * Checks and if required installs needed allowed type relations.
0790: * Retrieves, for each allowed relation entry, the attributes, and passes these on to {@link #installTypeRel}
0791: * @param relations a list of hashtables. Each hashtable represents a allowedrelation entry, and contains a list of name-value
0792: * pairs (the allowed relation attributes).
0793: * @return <code>true</code> if succesfull, <code>false</code> if an error occurred
0794: */
0795: private boolean installAllowedRelations(
0796: List<Map<String, String>> relations,
0797: ApplicationResult result) {
0798: for (Map<String, String> bh : relations) {
0799: String from = bh.get("from");
0800: String to = bh.get("to");
0801: String type = bh.get("type");
0802: if (!installTypeRel(from, to, type, -1, result)) {
0803: return false;
0804: }
0805: }
0806: return true;
0807: }
0808:
0809: /**
0810: * Lists the required builders for this application, and makes attempts to install any builders that are
0811: * not present.
0812: * If there is a failure, the function returns false.
0813: * Failure messages are stored in the lastmsg member.
0814: * @param neededbuilders a list with builder data that need be installed on teh system for this application to work
0815: * each element in teh list is a Map containing builder properties (in particular, 'name').
0816: * @param applicationRoot the rootpath where the application's configuration files are located
0817: * @return true if the builders were succesfully installed, false if the installation failed
0818: */
0819: private boolean installBuilders(
0820: List<Map<String, String>> neededbuilders,
0821: String applicationRoot, ApplicationResult result) {
0822: for (Map<String, String> builderdata : neededbuilders) {
0823: String name = builderdata.get("name");
0824: MMObjectBuilder bul = mmb.getBuilder(name);
0825: // if builder not loaded
0826: if (bul == null) {
0827: // if 'inactive' in the config/builder path, fail
0828: String path = mmb.getBuilderPath(name, "");
0829: if (path != null) {
0830: result
0831: .error("The builder '"
0832: + name
0833: + "' was already on our system, but inactive."
0834: + "To install this application, make the builder '"
0835: + path + name + ".xml ' active");
0836: continue;
0837: }
0838: ResourceLoader appLoader = ResourceLoader
0839: .getConfigurationRoot().getChildResourceLoader(
0840: ResourceLoader
0841: .getDirectory(applicationRoot));
0842: ResourceLoader this AppLoader = appLoader
0843: .getChildResourceLoader(ResourceLoader
0844: .getName(applicationRoot));
0845: ResourceLoader builderLoader = this AppLoader
0846: .getChildResourceLoader("builders");
0847:
0848: // attempt to open the builder file.
0849: org.w3c.dom.Document config;
0850: try {
0851: config = builderLoader.getDocument(name + ".xml");
0852: } catch (org.xml.sax.SAXException se) {
0853: String msg = "builder '" + name + "':\n"
0854: + se.toString();
0855: log.error(msg, se);
0856: result.error("A XML parsing error occurred ("
0857: + se.toString()
0858: + "). Check the log for details.");
0859: continue;
0860: } catch (java.io.IOException ioe) {
0861: String msg = "builder '" + name + "':\n"
0862: + ioe.toString();
0863: log.error(msg, ioe);
0864: result.error("A file I/O error occurred ("
0865: + ioe.toString()
0866: + "). Check the log for details.");
0867: continue;
0868: } catch (Throwable t) {
0869: String msg = "builder '" + name + "': "
0870: + t.getMessage();
0871: log.error(msg, t);
0872: result.error("An error occured " + t.getClass()
0873: + " " + msg);
0874: continue;
0875: }
0876:
0877: if (config == null) {
0878: result.error("Could not find the builderfile : '"
0879: + builderLoader.getResource(name + ".xml")
0880: + "' (builder '" + name + "')");
0881: continue;
0882: }
0883:
0884: // check the presence of typedef (if not present, fail)
0885: MMObjectBuilder typeDef = mmb.getTypeDef();
0886: if (typeDef == null) {
0887: return result
0888: .error("Could not find the typedef builder.");
0889: }
0890: try {
0891: // try to add a node to typedef, same as adding a builder...
0892: MMObjectNode typeNode = typeDef
0893: .getNewNode("system");
0894: // fill the name....
0895: typeNode.setValue("name", name);
0896: typeNode.setValue("config", config);
0897: // insert into mmbase
0898: typeNode.insert("system");
0899: } catch (Exception e) {
0900: result.error(e.getMessage());
0901: continue;
0902: }
0903: // we now made the builder active.. look for other builders...
0904: }
0905: }
0906: return result.isSuccess();
0907: }
0908:
0909: /**
0910: * Checks whether a given relation definition exists, and if not, creates that definition.
0911: * @param sname source name of the relation definition
0912: * @param dname destination name of the relation definition
0913: * @param dir directionality (uni or bi)
0914: * @param sguiname source GUI name of the relation definition
0915: * @param dguiname destination GUI name of the relation definition
0916: * @param builder references the builder to use (only in new format)
0917: * @return <code>true</code> if succesfull, <code>false</code> if an error occurred
0918: */
0919: private boolean installRelDef(String sname, String dname, int dir,
0920: String sguiname, String dguiname, int builder,
0921: ApplicationResult result) {
0922:
0923: RelDef reldef = mmb.getRelDef();
0924: if (reldef != null) {
0925: if (reldef.getNumberByName(sname + "/" + dname) == -1) {
0926: MMObjectNode node = reldef.getNewNode("system");
0927: node.setValue("sname", sname);
0928: node.setValue("dname", dname);
0929: node.setValue("dir", dir);
0930: node.setValue("sguiname", sguiname);
0931: node.setValue("dguiname", dguiname);
0932: if (RelDef.usesbuilder) {
0933: // if builder is unknown (falsely specified), use the InsRel builder
0934: if (builder <= 0) {
0935: builder = mmb.getInsRel().getNumber();
0936: }
0937: node.setValue("builder", builder);
0938: }
0939: int id = reldef.insert("system", node);
0940: if (id != -1) {
0941: log.debug("RefDef (" + sname + "," + dname
0942: + ") installed");
0943: } else {
0944: return result.error("RelDef (" + sname + ","
0945: + dname + ") could not be installed");
0946: }
0947: }
0948: } else {
0949: return result.error("Can't get reldef builder");
0950: }
0951: return true;
0952: }
0953:
0954: /**
0955: * Checks and if required installs an allowed type relation (typerel object).
0956: * @param sname source type name of the type relation
0957: * @param dname destination type name of the type relation
0958: * @param rname role name of the type relation
0959: * @param count cardinality of the type relation
0960: * @return <code>true</code> if succesfull, <code>false</code> if an error occurred
0961: */
0962: private boolean installTypeRel(String sname, String dname,
0963: String rname, int count, ApplicationResult result) {
0964: TypeRel typerel = mmb.getTypeRel();
0965: if (typerel != null) {
0966: TypeDef typedef = mmb.getTypeDef();
0967: if (typedef == null) {
0968: return result.error("Can't get typedef builder");
0969: }
0970: RelDef reldef = mmb.getRelDef();
0971: if (reldef == null) {
0972: return result.error("Can't get reldef builder");
0973: }
0974:
0975: // figure out rnumber
0976: int rnumber = reldef.getNumberByName(rname);
0977: if (rnumber == -1) {
0978: return result.error("No reldef with role '" + rname
0979: + "' defined");
0980: }
0981:
0982: // figure out snumber
0983: int snumber = typedef.getIntValue(sname);
0984: if (snumber == -1) {
0985: return result.error("No builder with name '" + sname
0986: + "' defined");
0987: }
0988:
0989: // figure out dnumber
0990: int dnumber = typedef.getIntValue(dname);
0991: if (dnumber == -1) {
0992: return result.error("No builder with name '" + dname
0993: + "' defined");
0994: }
0995:
0996: if (!typerel.contains(snumber, dnumber, rnumber,
0997: TypeRel.STRICT)) {
0998: MMObjectNode node = typerel.getNewNode("system");
0999: node.setValue("snumber", snumber);
1000: node.setValue("dnumber", dnumber);
1001: node.setValue("rnumber", rnumber);
1002: node.setValue("max", count);
1003: int id = typerel.insert("system", node);
1004: if (id != -1) {
1005: log.debug("TypeRel (" + sname + "," + dname + ","
1006: + rname + ") installed");
1007: } else {
1008: return result.error("TypeRel (" + sname + ","
1009: + dname + "," + rname
1010: + ") could not be installed");
1011: }
1012: }
1013: return true;
1014: } else {
1015: return result.error("Can't get typerel builder");
1016: }
1017: }
1018:
1019: }
|