0001: /*
0002: * <copyright>
0003: *
0004: * Copyright 2000-2004 BBNT Solutions, LLC
0005: * under sponsorship of the Defense Advanced Research Projects
0006: * Agency (DARPA).
0007: *
0008: * You can redistribute this software and/or modify it under the
0009: * terms of the Cougaar Open Source License as published on the
0010: * Cougaar Open Source Website (www.cougaar.org).
0011: *
0012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
0013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
0014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
0015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
0016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
0022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0023: *
0024: * </copyright>
0025: */
0026: package org.cougaar.tools.csmart.experiment;
0027:
0028: import org.cougaar.tools.csmart.core.cdata.ComponentData;
0029: import org.cougaar.tools.csmart.core.db.CMT;
0030: import org.cougaar.tools.csmart.core.db.CommunityDbUtils;
0031: import org.cougaar.tools.csmart.core.db.DBConflictHandler;
0032: import org.cougaar.tools.csmart.core.db.DBUtils;
0033: import org.cougaar.tools.csmart.core.db.ExperimentDB;
0034: import org.cougaar.tools.csmart.core.db.PopulateDb;
0035: import org.cougaar.tools.csmart.core.property.BaseComponent;
0036: import org.cougaar.tools.csmart.core.property.ModifiableComponent;
0037: import org.cougaar.tools.csmart.recipe.RecipeComponent;
0038: import org.cougaar.tools.csmart.society.AgentComponent;
0039: import org.cougaar.tools.csmart.society.SocietyComponent;
0040: import org.cougaar.tools.csmart.ui.console.CSMARTConsoleModel;
0041: import org.cougaar.tools.csmart.util.ReadOnlyProperties;
0042: import org.cougaar.util.Parameters;
0043:
0044: import javax.swing.*;
0045: import java.io.File;
0046: import java.io.IOException;
0047: import java.net.InetAddress;
0048: import java.net.UnknownHostException;
0049: import java.sql.SQLException;
0050: import java.util.ArrayList;
0051: import java.util.Calendar;
0052: import java.util.Collections;
0053: import java.util.Iterator;
0054: import java.util.List;
0055: import java.util.Properties;
0056:
0057: /**
0058: * A CSMART Experiment. Holds the components being run, and the configuration of host/node/agents.<br>
0059: * @property org.cougaar.tools.csmart.allowComplexRecipeQueries
0060: * (default false): If true, save changes after every recipe
0061: * application, to permit recipe target queries to see the effect
0062: * of the previous recipe.
0063: */
0064: public final class DBExperiment extends ExperimentBase {
0065:
0066: // Define some Node Args
0067: private static final String CONFIG_DATABASE = "org.cougaar.configuration.database";
0068: private static final String CONFIG_USER = "org.cougaar.configuration.user";
0069: private static final String CONFIG_PASSWD = "org.cougaar.configuration.password";
0070:
0071: private static final String DBINIT_PROP = "DB"; // Use the CSMART database
0072:
0073: private Trial theTrial; // Experiments currently have a single trial
0074:
0075: ////////////////////////////////////////////
0076: // Constructors
0077: ////////////////////////////////////////////
0078:
0079: // Organizer uses this to create a new experiment based on a society/recipe
0080: // or from the UI
0081: public DBExperiment(final String name,
0082: final SocietyComponent societyComponent,
0083: final RecipeComponent[] recipes) {
0084: this (name);
0085: init();
0086: setSocietyComponent(societyComponent);
0087: if (recipes != null)
0088: setRecipeComponents(recipes);
0089:
0090: // Give it a new empty Comm ASB:
0091: PopulateDb pdb = null;
0092: try {
0093: pdb = new PopulateDb(null, null);
0094: setCommAsbID(pdb.getNewCommAssembly());
0095: } catch (SQLException sqle) {
0096: log.error("constructor failed to get new comm ASB", sqle);
0097: } catch (IOException ie) {
0098: log.error("constructor failed to get new comm ASB", ie);
0099: } finally {
0100: try {
0101: if (pdb != null)
0102: pdb.close();
0103: } catch (SQLException se) {
0104: }
0105: }
0106: }
0107:
0108: // Used in copy when not in DB mode
0109: public DBExperiment(final String name) {
0110: super (name);
0111: init();
0112: }
0113:
0114: // Used in copy when in DB mode or on load from DB
0115: public DBExperiment(final String name, final String expID,
0116: final String trialID) {
0117: super (name, expID, trialID);
0118: init();
0119: }
0120:
0121: ////////////////////////////////////////////
0122: // Private Operations.
0123: ////////////////////////////////////////////
0124:
0125: private void init() {
0126: createLogger();
0127: setDefaultNodeArguments();
0128: if (getTrialID() != null)
0129: theTrial = new Trial(getTrialID());
0130: else
0131: theTrial = new Trial("Trial 1");
0132: theTrial.initProperties();
0133: }
0134:
0135: protected void setDefaultNodeArguments() {
0136: defaultNodeArguments = new ReadOnlyProperties(Collections
0137: .singleton(EXPERIMENT_ID));
0138: createObserver();
0139: defaultNodeArguments.put(PERSISTENCE_ENABLE, PERSISTENCE_DFLT);
0140: // By default we clear any existing persistence deltas when we
0141: // start a run.
0142: defaultNodeArguments.put(PERSIST_CLEAR, PERSIST_CLEAR_DFLT);
0143: defaultNodeArguments.put(TIMEZONE, TIMEZONE_DFLT);
0144: defaultNodeArguments.put(AGENT_STARTTIME, AGENT_STARTTIME_DFLT);
0145: defaultNodeArguments.put(COMPLAININGLP_LEVEL,
0146: COMPLAININGLP_LEVEL_DFLT);
0147: defaultNodeArguments.put(CONTROL_PORT, Integer
0148: .toString(APP_SERVER_DEFAULT_PORT));
0149:
0150: // By default, we tell the AppServer to ignore connection errors
0151: // if CSMART dies, so that the society does _not_ die.
0152: defaultNodeArguments.put(AS_SWALLOW_ERRORS,
0153: AS_SWALLOW_ERRORS_DFLT);
0154:
0155: // Class of Node to run. This is the first argument to the BOOTSTRAP_CLASS
0156: // below.
0157: defaultNodeArguments.put(CSMARTConsoleModel.COMMAND_ARGUMENTS,
0158: DEFAULT_NODE_CLASS);
0159:
0160: // Class of bootstrapper to use. The actual class being executed
0161: defaultNodeArguments.put(BOOTSTRAP_CLASS,
0162: DEFAULT_BOOTSTRAP_CLASS);
0163:
0164: if (DBUtils.dbMode) {
0165: defaultNodeArguments.put(CONFIG_DATABASE, Parameters
0166: .findParameter(DBUtils.DATABASE));
0167: defaultNodeArguments.put(CONFIG_USER, Parameters
0168: .findParameter(DBUtils.USER));
0169: defaultNodeArguments.put(CONFIG_PASSWD, Parameters
0170: .findParameter(DBUtils.PASSWORD));
0171: // Initializer components from the CSMART database
0172: defaultNodeArguments.put(INITIALIZER_PROP, DBINIT_PROP);
0173: if (getTrialID() != null)
0174: defaultNodeArguments.setReadOnlyProperty(EXPERIMENT_ID,
0175: getTrialID());
0176: }
0177: try {
0178: defaultNodeArguments.put(ENV_DISPLAY, InetAddress
0179: .getLocalHost().getHostName()
0180: + ":0.0");
0181: } catch (UnknownHostException uhe) {
0182: if (log.isErrorEnabled()) {
0183: log.error("UnknownHost Exception", uhe);
0184: }
0185: }
0186: }
0187:
0188: /**
0189: * Return the trial defined for this experiment.
0190: */
0191: private Trial getTrial() {
0192: return theTrial;
0193: }
0194:
0195: ////////////////////////////////////////////
0196: // Basic Operations.
0197: ////////////////////////////////////////////
0198:
0199: /**
0200: * Sets the Name of this Component.
0201: *
0202: * @param newName - New Component Name
0203: */
0204: public void setName(final String newName) {
0205: // If name is same, don't fire modification
0206: if (getExperimentName().equals(newName) || newName == null
0207: || newName.equals(""))
0208: return;
0209:
0210: if (log.isDebugEnabled()) {
0211: log.debug("Change expt name from " + getExperimentName()
0212: + " to " + newName);
0213: }
0214: super .setName(newName);
0215:
0216: // If the experiment isnt otherwise modified, and we can save
0217: // to the DB, then do so, and dont mark the experiment
0218: // as modified.
0219: if (!modified && getExperimentID() != null) {
0220: try {
0221: PopulateDb.changeExptName(getExperimentID(), newName);
0222: } catch (Exception e) {
0223: if (log.isErrorEnabled()) {
0224: log.error("setName: error setting name in DB", e);
0225: }
0226: }
0227: return;
0228: }
0229:
0230: fireModification();
0231: }
0232:
0233: /**
0234: * Return a deep copy of the experiment.
0235: * Called when an experiment is selected in the organizer.
0236: * Add experiments and society to workspace.
0237: * @return the copy of the experiment.
0238: */
0239: public ModifiableComponent copy(final String uniqueName) {
0240: if (log.isDebugEnabled()) {
0241: log.debug("Experiment copying " + getExperimentName()
0242: + " into new name " + uniqueName);
0243: }
0244: DBExperiment experimentCopy;
0245: if (DBUtils.dbMode)
0246: experimentCopy = new DBExperiment(uniqueName,
0247: getExperimentID(), getTrialID());
0248: else
0249: experimentCopy = new DBExperiment(uniqueName);
0250:
0251: // FIXME: Unset the experiment and trial IDs? I think so...
0252: experimentCopy.setExperimentID(null);
0253: experimentCopy.setTrialID(null);
0254:
0255: final Properties newProps = experimentCopy
0256: .getDefaultNodeArguments();
0257: newProps.clear();
0258: newProps.putAll(getDefaultNodeArguments());
0259:
0260: // // Copy the society component
0261: // ModifiableComponent society = getSocietyComponent();
0262: // if (society != null) {
0263: // ModifiableComponent copiedSocietyComponent = null;
0264:
0265: // // FIXME: USe a name based on the original society!!
0266: // // copiedSocietyComponent = society.copy("Society for " + uniqueName);
0267: // copiedSocietyComponent = society.copy(society.getShortName() + " copy");
0268: // if (copiedSocietyComponent != null) {
0269: // // FIXME: Must save the copied society to the DB? I think the
0270: // // society does it for me...
0271: // experimentCopy.addComponent(copiedSocietyComponent);
0272: // }
0273: // }
0274:
0275: // Don't copy the society.
0276: experimentCopy.addComponent(getSocietyComponent());
0277:
0278: // Copy the recipe components
0279: for (int i = 0; i < getRecipeComponentCount(); i++) {
0280: final ModifiableComponent mc = getRecipeComponent(i);
0281: // ModifiableComponent copiedComponent = null;
0282: // // FIXME: USe a name based on the original recipe!!
0283: // // copiedComponent = mc.copy("Recipe for " + uniqueName);
0284: // copiedComponent = mc.copy(mc.getShortName() + " copy");
0285: // if (copiedComponent != null) {
0286: // experimentCopy.addComponent(copiedComponent);
0287: // }
0288:
0289: // Don't copy recipes either
0290: experimentCopy.addComponent(mc);
0291: }
0292:
0293: // copy hosts
0294: // Use special version of it that doesnt do the
0295: // Node/Agent reconciliation, since we dont
0296: // care for this purpose
0297: final HostComponent[] hosts = getHostComponentsNoReconcile();
0298: final HostComponent[] nhosts = new HostComponent[hosts.length];
0299: for (int i = 0; i < hosts.length; i++) {
0300: // FIXME: instead to nhosts[i] = hosts[i].copy();
0301: // and then don't need the following 2 set calls hopefully
0302: nhosts[i] = experimentCopy.addHost(hosts[i].getShortName()
0303: .toString());
0304: nhosts[i].setServerPort(hosts[i].getServerPort());
0305: nhosts[i].setMonitoringPort(hosts[i].getMonitoringPort());
0306: // hosts[i].copy(nhosts[i]);
0307: // FIXME: What about other Host fields? OS, etc?
0308: // -- answer - see above fixme
0309: }
0310:
0311: // copy nodes
0312: // Note that this will update the NameServer host, and do exactly
0313: // one Node/Agent reconciliation
0314: final NodeComponent[] nodes = getNodeComponents();
0315: final NodeComponent[] nnodes = new NodeComponent[nodes.length];
0316: for (int i = 0; i < nodes.length; i++) {
0317: nnodes[i] = ((ExperimentNode) nodes[i])
0318: .copy(experimentCopy);
0319: experimentCopy.addNodeComponent((ExperimentNode) nnodes[i]);
0320:
0321: // FIXME: What about other Node fields?
0322: // answer -- see comment in ExperimentNode.copy
0323: }
0324:
0325: // reconcile hosts-nodes-agents
0326: final AgentComponent[] nagents = experimentCopy.getAgents();
0327: NodeComponent nnode = null;
0328: final ArrayList nNodesCopied = new ArrayList();
0329: for (int i = 0; i < hosts.length; i++) {
0330: final NodeComponent[] onodes = hosts[i].getNodes();
0331: // for each of the old nodes, find the corresponding new node
0332: for (int j = 0; j < onodes.length; j++) {
0333: for (int k = 0; k < nnodes.length; k++) {
0334: if (nnodes[k].getShortName().equals(
0335: onodes[j].getShortName())) {
0336: nnode = nnodes[k];
0337: // add new node to new host
0338: nhosts[i].addNode(nnode);
0339: break;
0340: }
0341: }
0342: // add new agents to new node
0343: final AgentComponent[] oagents = onodes[j].getAgents();
0344: for (int x = 0; x < oagents.length; x++)
0345: for (int y = 0; y < nagents.length; y++)
0346: if (nagents[y].getShortName().equals(
0347: oagents[x].getShortName()))
0348: if (nagents[y] != null) {
0349: nnode.addAgent(nagents[y]);
0350: }
0351:
0352: // Record which Nodes we've (potentially) copied the Agents for
0353: nNodesCopied.add(nnode);
0354: } // loop over old old nodes on original Host
0355: } // loop over original hosts
0356:
0357: // FIXME: What about if orig Experiment had Agents assigned to a Node
0358: // that was not assigned to a host - is that mapping lost?
0359: // I think so. So we need to maybe keep track of which Nodes we updated just now
0360: for (int i = 0; i < nnodes.length; i++) {
0361: if (!nNodesCopied.contains(nnodes[i])) {
0362: // We may need to copy the old node-agent mapping onto this new node
0363: final AgentComponent[] oagents = nodes[i].getAgents();
0364: // if (log.isDebugEnabled() && oagents.length > 0) {
0365: // log.debug("copy: Found node whose Agent mapping may not have been copied: " + nnodes[i] + " and old Node " + nodes[i]);
0366: // }
0367: for (int j = 0; j < oagents.length; j++) {
0368: // if (log.isDebugEnabled()) {
0369: // log.debug("Looking at Agent " + oagents[j].getShortName() + " on old Node");
0370: // }
0371: for (int k = 0; k < nagents.length; k++) {
0372: if (nagents[k].getShortName().equals(
0373: oagents[j].getShortName())) {
0374: if (log.isDebugEnabled()) {
0375: log.debug("copy: Putting new Agent "
0376: + nagents[k].getShortName()
0377: + " on new Node "
0378: + nnodes[i].getShortName());
0379: }
0380: nnodes[i].addAgent(nagents[k]);
0381: } // Found Agent match
0382: } // loop over new agents
0383: } // loop over old agents on old Node
0384: } // block to deal with uncopied Node
0385: } // loop over new nodes
0386: // copy the results directory; results from the copied experiment
0387: // will be stored in this directory under the copied experiment name
0388: experimentCopy.setResultDirectory(getResultDirectory());
0389:
0390: // FIXME: Maybe copy now needs a Node/Agent reconciliation?
0391: //experimentCopy.getNodeComponents();
0392:
0393: // Fixme: Grab comm info from orig expt & resave under new asbID
0394: // and attach that new ID to this Expt
0395: PopulateDb pdb = null;
0396: String newAsbid = null;
0397: try {
0398: pdb = new PopulateDb(getExperimentID(), getTrialID());
0399: newAsbid = pdb.getNewCommAsbFromExpt(getExperimentID(),
0400: getTrialID());
0401: } catch (SQLException sqle) {
0402: log.error("Expt Copy error copying community info", sqle);
0403: } catch (IOException ioe) {
0404: log.error("Expt Copy error copying community info", ioe);
0405: } finally {
0406: try {
0407: if (pdb != null)
0408: pdb.close();
0409: } catch (SQLException se) {
0410: }
0411: }
0412: if (log.isDebugEnabled()) {
0413: log.debug("copy: new Expt gets Comm ASB " + newAsbid);
0414: }
0415: experimentCopy.setCommAsbID(newAsbid);
0416:
0417: // FIXME: Must duplicate the expt_trial_thread information
0418: // Else must recognize & fix problem on load in Organizer / CMTDialog
0419:
0420: // FIXME: Only do this sometimes maybe?
0421: experimentCopy.storeSelectedThreads(getSelectedThreads());
0422:
0423: return experimentCopy;
0424: }
0425:
0426: // Retrieve from the DB the threads selected in this experiment.
0427: // Do not store on the local experiment, but instead return a list
0428: // which will be stored on the copy
0429: private List getSelectedThreads() {
0430: if (getTrialID() == null || getSocietyComponent() == null)
0431: return null;
0432: final List threads = new ArrayList();
0433:
0434: for (int i = 0; i < CMT.ULDBThreads.length; i++) {
0435: if (ExperimentDB.isULThreadSelected(getTrialID(),
0436: CMT.ULDBThreads[i]))
0437: threads.add(CMT.ULDBThreads[i]);
0438: }
0439: if (threads.isEmpty())
0440: return null;
0441: else
0442: return threads;
0443: }
0444:
0445: // List of the CMT style threads selected for this Experiment
0446: // This should be non-null only when we copied the experiment
0447: // and have not yet saved it.
0448: private List selThreads = null;
0449:
0450: // Stash away the list of CMT style threads seleced in this experiment
0451: private void storeSelectedThreads(final List threads) {
0452: this .selThreads = threads;
0453: }
0454:
0455: // If there are any locally storead Threads, save them in DB
0456: // if we can.
0457: private void saveSelectedThreads() {
0458: if (selThreads == null || selThreads.isEmpty()
0459: || getExperimentID() == null
0460: || getExperimentID().equals("") || getTrialID() == null
0461: || getTrialID().equals(""))
0462: return;
0463:
0464: for (int i = 0; i < CMT.ULDBThreads.length; i++)
0465: ExperimentDB.setULThreadSelected(getTrialID(),
0466: CMT.ULDBThreads[i], selThreads
0467: .contains(CMT.ULDBThreads[i]));
0468: selThreads = null;
0469: }
0470:
0471: /**
0472: * Get component data for the society in the experiment.
0473: * If the experiment is not in the database, then it
0474: * constructs the society component data here.
0475: * @return ComponentData the component data for the society
0476: */
0477: public ComponentData getSocietyComponentData() {
0478: if (modified)
0479: saveToDb(); // if modified, update component data and save to database
0480:
0481: // If the exp/trialID is null and we have recipes, we must do a save
0482: // Otherwise recipes wont correctly add their data
0483: // do this within generateCompleteSociety
0484:
0485: if (completeSociety == null)
0486: generateCompleteSociety();
0487:
0488: return completeSociety;
0489: }
0490:
0491: // Private Methods
0492:
0493: ////////////////////////////////////////////
0494: // Host Component Operations.
0495: ////////////////////////////////////////////
0496:
0497: // Public Methods
0498:
0499: // Private Methods
0500:
0501: ////////////////////////////////////////////
0502: // Node Component Operations.
0503: ////////////////////////////////////////////
0504:
0505: // Public
0506:
0507: ////////////////////////////////////////////
0508: // Agent Component Operations.
0509: ////////////////////////////////////////////
0510:
0511: // Public
0512:
0513: // Private
0514:
0515: ////////////////////////////////////////////
0516: // Database Specific Operations
0517: ////////////////////////////////////////////
0518:
0519: /**
0520: * Save the experiment to the database, regardless of
0521: * whether or not it's been modified.
0522: * To avoid useless saves, the caller should check the modified
0523: * flag with isModified. This method specifically does not check
0524: * the modified flag to allow the user to force saving the experiment.
0525: * If the experiment has been modified, save it to the database.
0526: * This creates the full ComponentData tree and then uses
0527: * <code>PopulateDb</code> to save it to the database
0528: *
0529: * @param ch a <code>DBConflictHandler</code> to graphically handle conflicts
0530: * @see org.cougaar.tools.csmart.core.db.PopulateDb
0531: */
0532: public void save(final DBConflictHandler ch) {
0533: PopulateDb pdb = null;
0534:
0535: // To debug slow saves, bug 2002.
0536: // Added per bug 2015
0537: final long saveStartTime = Calendar.getInstance()
0538: .getTimeInMillis();
0539: if (log.isShoutEnabled())
0540: log.shout("save starting save");
0541:
0542: // Make sure we have the Community Assembly stored locally
0543: if (getCommAsbID() == null) {
0544: if (log.isDebugEnabled()) {
0545: log.debug("save had no CommAsb for expt "
0546: + getExperimentID());
0547: }
0548: try {
0549: // retrieve from db
0550: pdb = new PopulateDb(getExperimentID(), getTrialID());
0551: // set it locally
0552: setCommAsbID(pdb.getCommAsbForExpt(getExperimentID(),
0553: getTrialID()));
0554: if (getCommAsbID() == null) {
0555: if (log.isDebugEnabled()) {
0556: log
0557: .debug("save found no comm asb in DB either");
0558: }
0559: setCommAsbID(pdb.getNewCommAssembly());
0560: }
0561: } catch (SQLException sqle) {
0562: log.error("save error getting Comm assembly", sqle);
0563: } catch (IOException ioe) {
0564: log.error("save error getting Comm assembly", ioe);
0565: } finally {
0566: try {
0567: if (pdb != null)
0568: pdb.close();
0569: } catch (SQLException se) {
0570: }
0571: pdb = null;
0572: }
0573: }
0574:
0575: if (log.isDebugEnabled()) {
0576: log.debug("save has commAsb " + getCommAsbID()
0577: + " for expt " + getExperimentID());
0578: }
0579:
0580: try {
0581: if (log.isInfoEnabled()) {
0582: log.info("Saving experiment " + getExperimentName()
0583: + " to database");
0584: }
0585:
0586: // Don't do this here -- it gets done by generateHNACData
0587: // updateNameServerHostName(); // Be sure this is up-to-date
0588: final List components = getComponents();
0589:
0590: boolean componentWasRemoved = false;
0591:
0592: final SocietyComponent sc = getSocietyComponent();
0593: if (sc == null) {
0594: if (log.isErrorEnabled())
0595: log
0596: .error("Experiment has no society; cannot save it to database.");
0597: return;
0598: }
0599:
0600: if (sc.getAssemblyId() == null) {
0601: // This should never happen!
0602: if (log.isErrorEnabled()) {
0603: log
0604: .error(
0605: "Saving experiment "
0606: + getExperimentName()
0607: + " with exptID "
0608: + getExperimentID()
0609: + " containing society "
0610: + sc.getSocietyName()
0611: + " but society has now Assembly! Caller trace: ",
0612: new Throwable());
0613: }
0614:
0615: // Try to save the society that somehow
0616: // was not previously saved
0617: if (!sc.saveToDatabase()) {
0618: if (log.isErrorEnabled()) {
0619: log
0620: .error("Failed to save society "
0621: + sc.getSocietyName()
0622: + " so going to give up saving the experiment.");
0623: }
0624: return;
0625: }
0626: } else if (sc.isModified()) {
0627: // Save the society before trying to save the Experiment
0628: if (!sc.saveToDatabase()) {
0629: if (log.isErrorEnabled()) {
0630: log
0631: .error("Failed to save society "
0632: + sc.getSocietyName()
0633: + " so going to give up saving the experiment.");
0634: }
0635: return;
0636: }
0637: }
0638:
0639: // Check all recipe components to see if modified, if so, save them
0640: // Note that below the recipes are added to the experiment's
0641: // table
0642: // and this must happen after start the expt save
0643: final RecipeComponent[] rComponents = getRecipeComponents();
0644: for (int i = 0; i < rComponents.length; i++) {
0645: final RecipeComponent rc = rComponents[i];
0646: if (rc.isModified()) {
0647: rc.saveToDatabase();
0648: }
0649: }
0650:
0651: ///////////
0652: // OK: Now go ahead and save the experiment.
0653: pdb = new PopulateDb("CMT", "CSHNA", "CSMI",
0654: getExperimentName(), getExperimentID(),
0655: getTrialID(), ch, sc.getAssemblyId());
0656:
0657: setExperimentID(pdb.getExperimentId());
0658: setTrialID(pdb.getTrialId()); // sets trial id and -D argument
0659:
0660: // Save back to DB the selected threads, if necc
0661: saveSelectedThreads();
0662:
0663: // store in the DB the community assembly ID being used
0664: pdb.setCommAssemblyId(getCommAsbID());
0665:
0666: // Some components will want access to the complete set of Nodes
0667: // in the society, etc. To get that, they must get back to the
0668: // root soc object, and do a getOwner and go from there. Ugly.
0669:
0670: // So far we've just created the pdb (which
0671: // sets up the Experimet & Trial IDs, and puts the CMT assembly
0672: // in the right place if necc, and clears out the other
0673: // assemblies (CSMI, CSHNA)
0674:
0675: // Gather the HostNodeAgent stuff
0676: // This will call updateNameServerHostName,
0677: // and take care of reconciling the Node/Agent mapping
0678: generateHNACDATA();
0679: if (log.isErrorEnabled() && completeSociety == null) {
0680: log.error("save: Society Data is null!");
0681: // FIXME: Throw an exception to pop out at this point?
0682: }
0683:
0684: // Problem: Societies from files store _tons_ of properties
0685: // on the Agents. This is most of the society definition, in fact.
0686: // It appears that these end up changing (somehow),
0687: // between saves. So this can throw that IllegalArgumentException
0688: // FIXME!!!!
0689: // Save what we have so far in the CSHNA assembly
0690: if (log.isDebugEnabled()) {
0691: log.debug("About to save HNA data");
0692: }
0693: pdb.populateHNA(completeSociety);
0694:
0695: // Now let components add in their pieces
0696: componentWasRemoved = askComponentsToAddCDATA();
0697:
0698: // Recipes do their thing in modifyComponentData
0699: // Assuming that is the case (big assumption),
0700: // we clear the modified flag here, so that _only_ the
0701: // recipe deltas will show up as modifications
0702: // when we go to save the CSMI assembly later
0703: // This breaks AgentInsertionRecipes and CompleteAgentRecipes,
0704: // and perhaps all Complex recipes!!
0705: // So instead, add resetModified() call to SocietyBase.addComponentData...
0706: //completeSociety.resetModified();
0707:
0708: // If any component removed something, we need
0709: // to recreate the CMT assembly (under the original ID)
0710: // So this, currently destroys the orig CMT
0711: // Note also it does not effect Agent asset data
0712: // or relationships or OPLAN stuff
0713: if (componentWasRemoved) {
0714: if (log.isDebugEnabled()) {
0715: log
0716: .debug("save: After adding CDATA, got a removed.");
0717: }
0718: pdb.populateCSA(completeSociety);
0719: } else {
0720: if (log.isDebugEnabled()) {
0721: log
0722: .debug("save: Done adding CDATA, ready to modify.");
0723: }
0724: }
0725:
0726: // Are we allowing complex Recipe target queries that depend on the previous
0727: // actions of earlier recipes on Plugins, Binders, etc?
0728: final boolean doComplex = (System
0729: .getProperty(
0730: "org.cougaar.tools.csmart.allowComplexRecipeQueries",
0731: "false").equalsIgnoreCase("true"));
0732:
0733: if (!doComplex) {
0734: // Should this be turned down to INFO level? Or perhaps only after people have gotten used
0735: // to it?
0736: if (log.isWarnEnabled())
0737: log
0738: .warn("save: Not allowing complex recipe target queries. Some queries using Agent contents or relationships may not work. Queries based on Agent, Node, or Host names and assignments, and Community information will work. If your recipe fails to apply correctly, try un-commenting the appropriate line in the CSMART startup script.");
0739: }
0740:
0741: // then give everyone a chance to modify what they've collectively produced
0742: for (int i = 0, n = components.size(); i < n; i++) {
0743: final BaseComponent soc = (BaseComponent) components
0744: .get(i);
0745:
0746: if (log.isDebugEnabled())
0747: log.debug("save: About to apply mods by "
0748: + soc.getShortName());
0749: soc.modifyComponentData(completeSociety, pdb);
0750:
0751: if (soc.componentWasRemoved()) {
0752: if (log.isDebugEnabled()) {
0753: log
0754: .debug("save: After modifying CDATA by comp "
0755: + soc.getShortName()
0756: + ", got a removed.");
0757: }
0758: pdb.populateCSA(completeSociety);
0759: }
0760:
0761: // If we are allowing complex recipe target queries, then
0762: // we must save each recipes changes to the DB in turn, so that
0763: // later recipes can look at these changes to decide what to do.
0764: // If not though, and we're only looking at HNA data and perhaps
0765: // CMT data, plus COMM data, then these extra saves are not necessary
0766: if (doComplex) {
0767: try {
0768: // Incrementally save
0769: // so that later recipes have the benefit
0770: // of the earlier modifications
0771: if (log.isDebugEnabled()) {
0772: log
0773: .debug("About to save CSMI data, having just done mod in "
0774: + soc.getShortName());
0775: }
0776: pdb.populateCSMI(completeSociety);
0777: } catch (IllegalArgumentException iae) {
0778: if (log.isInfoEnabled()) {
0779: log
0780: .info("Caught iae "
0781: + iae.toString()
0782: + " while saving modifications. Must redo as a CSA");
0783: }
0784: pdb.populateCSA(completeSociety);
0785: }
0786: }
0787: } // end of loop over components to do mod
0788:
0789: // If we did not iteratively save above, must do a save here
0790: if (!doComplex) {
0791: if (log.isInfoEnabled())
0792: log
0793: .info("save: Now saving all modifications at once.");
0794: try {
0795: // Incrementally save
0796: // so that later recipes have the benefit
0797: // of the earlier modifications
0798: if (log.isDebugEnabled()) {
0799: log
0800: .debug("About to save CSMI data, having done all mods");
0801: }
0802: pdb.populateCSMI(completeSociety);
0803: } catch (IllegalArgumentException iae) {
0804: if (log.isInfoEnabled()) {
0805: log
0806: .info("Caught iae "
0807: + iae.toString()
0808: + " while saving modifications. Must redo as a CSA");
0809: }
0810: pdb.populateCSA(completeSociety);
0811: }
0812: }
0813:
0814: // Save the inclusion of these recipes in the experiment
0815: // without re-saving the recipes
0816: pdb.setModRecipes(recipes);
0817:
0818: // Now make sure the runtime/ config time assemblies
0819: // are all correct
0820: if (log.isDebugEnabled()) {
0821: log.debug("About to do fix assemblies");
0822: }
0823: if (!pdb.fixAssemblies()) {
0824: // Some sort of error trying to ensure the assemblies are all correct.
0825: if (log.isErrorEnabled()) {
0826: log
0827: .error("Failed to ensure correct assemblies saved.");
0828: }
0829: }
0830:
0831: resetModified();
0832: } catch (Exception sqle) {
0833: if (log.isErrorEnabled())
0834: log
0835: .error("Error saving experiment to database: ",
0836: sqle);
0837: return;
0838: } finally {
0839: final long saveStopTime = Calendar.getInstance()
0840: .getTimeInMillis();
0841: // To debug slow saves, bug 2002.
0842: // Added per bug 2015
0843: if (log.isShoutEnabled())
0844: log
0845: .shout("save done. Experiment Save Time in Seconds: "
0846: + (saveStopTime - saveStartTime)
0847: / 1000l);
0848:
0849: if (pdb != null) {
0850: try {
0851: pdb.close();
0852: } catch (SQLException e) {
0853: }
0854: }
0855: }
0856: }
0857:
0858: private void saveToDb() {
0859: save(new DBConflictHandler() {
0860: public int handleConflict(final Object msg,
0861: final Object[] choices, final Object defaultChoice) {
0862: return JOptionPane.showOptionDialog(null, msg,
0863: "Database Conflict",
0864: JOptionPane.WARNING_MESSAGE,
0865: JOptionPane.DEFAULT_OPTION, null, choices,
0866: defaultChoice);
0867: }
0868: });
0869: }
0870:
0871: /**
0872: * Generate a complete ComponentData tree for the experiment
0873: *
0874: * @return a <code>boolean</code>, true if any component was removed
0875: */
0876: public boolean generateCompleteSociety() {
0877: // If the exp/trialID is null and we have recipes, we must do a save
0878: // Otherwise the pdb recipes get when doing modifyComponentData
0879: // wont let them do the DB queries they want to do
0880: if ((getTrialID() == null || getExperimentID() == null)
0881: && getRecipeComponentCount() > 0)
0882: saveToDb();
0883:
0884: if (!modified && completeSociety != null)
0885: return false;
0886: generateHNACDATA();
0887:
0888: // if (log.isDebugEnabled())
0889: // log.debug("generatecompleteSoc: after genHNA have: " + completeSociety);
0890:
0891: boolean mods = askComponentsToAddCDATA();
0892:
0893: // if (log.isDebugEnabled())
0894: // log.debug("generatecompleteSoc: after askToAdd have: " + completeSociety);
0895:
0896: return mods |= allowModifyCData();
0897: }
0898:
0899: private boolean allowModifyCData() {
0900: // then give everyone a chance to modify what they've collectively produced
0901: boolean componentModified = false;
0902: final BaseComponent theSoc = getSocietyComponent();
0903: PopulateDb pdb = null;
0904: try {
0905: pdb = new PopulateDb(getExperimentID(), getTrialID());
0906: if (log.isDebugEnabled()) {
0907: log.debug("allowModify letting comps modify. comp: "
0908: + theSoc.getShortName());
0909: }
0910: // Recipes typically need to do DB queries
0911: // in order to do these insertions correctly,
0912: // so need the version of modify that takes a pdb called.
0913: theSoc.modifyComponentData(completeSociety, pdb);
0914: componentModified |= theSoc.componentWasRemoved();
0915:
0916: for (int i = 0, n = recipes.size(); i < n; i++) {
0917: final BaseComponent soc = (BaseComponent) recipes
0918: .get(i);
0919: if (log.isDebugEnabled()) {
0920: log
0921: .debug("allowModify letting comps modify. comp: "
0922: + soc.getShortName());
0923: }
0924: // Recipes typically need to do DB queries
0925: // in order to do these insertions correctly,
0926: // so need the version of modify that takes a pdb called.
0927: soc.modifyComponentData(completeSociety, pdb);
0928: componentModified |= soc.componentWasRemoved();
0929: }
0930: } catch (Exception e) {
0931: if (log.isErrorEnabled()) {
0932: log.error("allowModifyCData error with pdb", e);
0933: }
0934: } finally {
0935: if (pdb != null) {
0936: try {
0937: pdb.close();
0938: } catch (SQLException e) {
0939: }
0940: }
0941: }
0942:
0943: // if (log.isDebugEnabled())
0944: // log.debug("end of allowModify: complete is now: " + completeSociety);
0945:
0946: return componentModified;
0947: }
0948:
0949: ////////////////////////////////////////////
0950: // Configuration Writer Specific Operations
0951: ////////////////////////////////////////////
0952:
0953: /**
0954: * Dump out the INI files for the first trial to
0955: * the local results directory for that trial.
0956: * This saves the experiment to the database.
0957: * The caller is responsible for not unnecessarily
0958: * saving the experiment to the database.
0959: */
0960: public void dumpINIFiles() {
0961: // FIXME:
0962: // If this Exp's society's assembly has entries in the oplan
0963: // tables, then must save out what the assembly ID is such
0964: // that on load of the INI files we can preserve the OPLAN INFO
0965:
0966: // Generate the complete Society
0967: generateCompleteSociety();
0968:
0969: final ExperimentINIWriter cw = new ExperimentINIWriter(
0970: completeSociety);
0971: cw.setTrialID(getTrialID());
0972:
0973: File resultDir = getResultDirectory();
0974: // if user didn't specify results directory, save in local directory
0975: if (resultDir == null) {
0976: resultDir = new File(".");
0977: }
0978: final Trial trial = getTrial();
0979: final String dirname = resultDir.getAbsolutePath()
0980: + File.separatorChar + getExperimentName()
0981: + File.separatorChar + trial.getShortName()
0982: + File.separatorChar + "INIFiles-dump";
0983: File f = null;
0984: try {
0985: f = new File(dirname);
0986: // guarantee that directories exist
0987: if (!f.exists() && !f.mkdirs() && !f.exists())
0988: f = new File(".");
0989: } catch (Exception e) {
0990: JOptionPane
0991: .showMessageDialog(null,
0992: "Couldn't create results directory: " + e,
0993: "Can't create directory",
0994: JOptionPane.ERROR_MESSAGE);
0995: if (log.isErrorEnabled()) {
0996: log.error("Couldn't create results directory: ", e);
0997: }
0998: }
0999: try {
1000: JOptionPane.showMessageDialog(null, "Writing ini file to "
1001: + f.getAbsolutePath() + "...");
1002: cw.writeConfigFiles(f);
1003: // Also, write out the community XML file
1004: if (!CommunityDbUtils.dumpCommunityXML(f.getAbsolutePath()
1005: + File.separatorChar + "communities.xml",
1006: getCommAsbID()))
1007: log.error("Couldn't write communities.xml file");
1008: else if (log.isDebugEnabled())
1009: log.debug("got true from commDbUtils.dump with path "
1010: + f.getAbsolutePath() + File.separatorChar
1011: + "communities.xml, and asbID "
1012: + getCommAsbID());
1013:
1014: } catch (Exception e) {
1015: if (log.isErrorEnabled()) {
1016: log.error("Couldn't write ini files: ", e);
1017: }
1018: }
1019: }
1020:
1021: public Iterator getConfigFiles(final NodeComponent[] nodes) {
1022: if (DBUtils.dbMode) {
1023: // Send a config writer that only writes LeafComponentData
1024: try {
1025: createConfigWriter();
1026: return configWriter.getFileNames();
1027: } catch (Exception e) {
1028: if (log.isErrorEnabled()) {
1029: log.error("Exception, creating config writer", e);
1030: }
1031: return null;
1032: }
1033: } else {
1034: // return new ExperimentINIWriter(getComponents(), nodes, this);
1035: return null;
1036: }
1037: }
1038:
1039: // Private Methods
1040:
1041: /**
1042: * Dumps a Host / Node / Agent mapping to an XML file.
1043: * The stored file can then be imported at a later date
1044: * to re-create all mappings in the current experiment.
1045: *
1046: */
1047: public void dumpHNA() {
1048: generateCompleteSociety();
1049:
1050: final ExperimentXML xmlWriter = new ExperimentXML();
1051:
1052: File resultDir = getResultDirectory();
1053: // if user didn't specify results directory, save in defult results directory
1054: // FIXME: This should use the CSMART global setting
1055: // CSMART.getResultDir() would do it if the experiment had access
1056: // to this...
1057: if (resultDir == null) {
1058: String resultDirName;
1059: try {
1060: resultDirName = System
1061: .getProperty("org.cougaar.install.path");
1062: } catch (RuntimeException e) {
1063: // just use current directory
1064: resultDirName = ".";
1065: }
1066: resultDir = new File(resultDirName + File.separatorChar
1067: + "results");
1068: }
1069:
1070: final Trial trial = getTrial();
1071: final String dirname = resultDir.getAbsolutePath()
1072: + File.separatorChar + getExperimentName()
1073: + File.separatorChar + trial.getShortName()
1074: + File.separatorChar + "HNA-XML";
1075: File f = null;
1076: try {
1077: f = new File(dirname);
1078: // guarantee that directories exist
1079: if (!f.exists() && !f.mkdirs() && !f.exists())
1080: f = new File(".");
1081: } catch (Exception e) {
1082: JOptionPane
1083: .showMessageDialog(null,
1084: "Couldn't create results directory: " + e,
1085: "Can't create directory",
1086: JOptionPane.ERROR_MESSAGE);
1087: if (log.isErrorEnabled()) {
1088: log.error("Couldn't create results directory: ", e);
1089: }
1090: }
1091: if (f != null) {
1092: try {
1093: JOptionPane.showMessageDialog(null,
1094: "Writing xml file to " + f.getAbsolutePath()
1095: + "...");
1096:
1097: xmlWriter.createExperimentFile(completeSociety, f);
1098: } catch (Exception e) {
1099: if (log.isErrorEnabled()) {
1100: log.error("dumpHNA: Couldn't write XML file: ", e);
1101: }
1102: }
1103: }
1104: }
1105:
1106: /**
1107: * If the experiment has still unbound properties,
1108: * then we can't run it yet. <br>
1109: * For each property in the experiment, if it is not set,
1110: * and we don't have a set of experimental values, return true;
1111: *
1112: * @return a <code>boolean</code> value
1113: */
1114: public boolean hasUnboundProperties() {
1115: // Dont use this!!!!
1116: return false;
1117: }
1118: } // end of Experiment.java
|