001: /**
002: * <copyright>
003: *
004: * Copyright 2002-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */package org.cougaar.tools.csmart.society;
026:
027: import org.cougaar.tools.csmart.core.cdata.ComponentData;
028: import org.cougaar.tools.csmart.core.db.PopulateDb;
029: import org.cougaar.tools.csmart.core.property.*;
030: import org.cougaar.tools.csmart.core.property.name.CompositeName;
031: import org.cougaar.tools.csmart.society.cdata.SocietyCDataComponent;
032: import org.cougaar.tools.csmart.ui.viewer.CSMART;
033: import org.cougaar.tools.csmart.ui.viewer.GUIUtils;
034: import org.cougaar.util.log.Logger;
035:
036: import java.io.FileFilter;
037: import java.io.IOException;
038: import java.io.ObjectInputStream;
039: import java.net.URL;
040: import java.sql.SQLException;
041: import java.util.ArrayList;
042: import java.util.Collection;
043: import java.util.Iterator;
044:
045: /**
046: * Implements generic methods required by all societies.
047: *
048: */
049: public abstract class SocietyBase extends
050: ModifiableConfigurableComponent implements SocietyComponent,
051: PropertiesListener {
052:
053: protected static final String DESCRIPTION_RESOURCE_NAME = "/org/cougaar/tools/csmart/society/society-base-description.html";
054: protected static final String BACKUP_DESCRIPTION = "A Society: Agents, Binders, Plugins, etc.";
055:
056: protected transient boolean isRunning = false;
057: protected boolean isSelfTerminating = false;
058:
059: protected transient Logger log;
060:
061: protected String assemblyId;
062: public String oldAssemblyId;
063: protected boolean modified = true;
064:
065: // modification event
066: public static final int SOCIETY_SAVED = 2;
067:
068: // used to block modify notifications while saving
069: private transient boolean saveInProgress = false;
070:
071: /**
072: * Constructs a <code>SocietyBase</code> object
073: * with the given name.
074: * @param name Name for this component
075: */
076: public SocietyBase(String name) {
077: super (name);
078: createLogger();
079: installListeners();
080: }
081:
082: private void createLogger() {
083: log = CSMART
084: .createLogger("org.cougaar.tools.csmart.society.SocietyBase");
085: }
086:
087: /**
088: * Returns the name of this Society
089: *
090: * @return Society Name
091: */
092: public String getSocietyName() {
093: return getShortName();
094: }
095:
096: public void setName(String newName) {
097: if (newName == null || newName.equals("")
098: || newName.equals(getSocietyName()))
099: return;
100:
101: boolean temp = modified;
102: // String oldname = getSocietyName();
103: super .setName(newName);
104: if (getAssemblyId() != null) {
105: // do the DB save that is necessary
106: try {
107: PopulateDb.changeSocietyName(getAssemblyId(), newName);
108: } catch (Exception e) {
109: if (log.isErrorEnabled()) {
110: log.error("setName exception changing name from "
111: + getSocietyName() + " to " + newName
112: + " for assembly " + getAssemblyId(), e);
113: }
114: // On error, mark the society as modified
115: temp = true;
116: }
117: } else {
118: // Couldnt do the save ourselves, so mark the society as modified
119: temp = true;
120: }
121: modified = temp;
122: }
123:
124: /**
125: * Returns the agents in this Society
126: * @return an array of <code>AgentComponent</code> objects
127: */
128: public AgentComponent[] getAgents() {
129: ArrayList agents = new ArrayList(
130: getDescendentsOfClass(AgentComponent.class));
131: return (AgentComponent[]) agents
132: .toArray(new AgentComponent[agents.size()]);
133: }
134:
135: /**
136: * Get the assembly id for this Society.
137: * @return a <code>String</code> which is the assembly id for this Society
138: */
139: public String getAssemblyId() {
140: return this .assemblyId;
141: }
142:
143: /**
144: * Set by the experiment controller to indicate that the
145: * society is running.
146: * The society is running from the moment that any node
147: * is successfully created
148: * (via the app-server's "create" method)
149: * until all nodes are terminated (aborted, self terminated, or
150: * manually terminated).
151: * @param isRunning flag indicating whether or not the society is running
152: */
153: public void setRunning(boolean isRunning) {
154: this .isRunning = isRunning;
155: }
156:
157: /**
158: * Returns whether or not the society is running,
159: * i.e. can be dynamically monitored.
160: * Running societies are not editable, but they can be copied,
161: * and the copy can be edited.
162: * @return true if society is running and false otherwise
163: */
164: public boolean isRunning() {
165: return isRunning;
166: }
167:
168: /**
169: * Return a file filter which can be used to fetch
170: * the metrics files for this experiment.
171: * @return <code>FileFilter</code> to get metrics files for this experiment
172: */
173: public FileFilter getResultFileFilter() {
174: return null;
175: }
176:
177: /**
178: * Return a file filter which can be used to delete
179: * the files generated by this experiment.
180: * @return <code>FileFilter</code> for cleanup
181: */
182: public FileFilter getCleanupFileFilter() {
183: return null;
184: }
185:
186: /**
187: * Returns whether the society is self terminating or must
188: * be manually terminated.
189: * Self terminating nodes cause the app-server to send back
190: * a "process-destroyed" message when the node terminates.
191: * @return true if society is self terminating
192: */
193: public boolean isSelfTerminating() {
194: return this .isSelfTerminating;
195: }
196:
197: /**
198: * Sets if the society is self terminating or not.
199: * Self terminating nodes cause the app-server to send back
200: * a "process-destroyed" message when the node terminates.
201: *
202: * @param isSelfTerminating true if society is self terminating
203: */
204: protected void setSelfTerminating(boolean isSelfTerminating) {
205: this .isSelfTerminating = isSelfTerminating;
206: }
207:
208: /**
209: * Returns the description of this society
210: *
211: * @return an <code>URL</code> value
212: */
213: public URL getDescription() {
214: return getClass().getResource(DESCRIPTION_RESOURCE_NAME);
215:
216: }
217:
218: /**
219: * Modifies any part of the ComponentData Structure.
220: *
221: * @param data Completed ComponentData structure for the society
222: * @return a <code>ComponentData</code> value
223: */
224: public ComponentData modifyComponentData(ComponentData data) {
225: return data;
226: }
227:
228: /**
229: * Adds any relevent <code>ComponentData</code> for this component.
230: * This method does not modify any existing <code>ComponentData</code>
231: *
232: * @see ComponentData
233: * @param data Pointer to the global <code>ComponentData</code>
234: * @return an updated <code>ComponentData</code> object
235: */
236: public ComponentData addComponentData(ComponentData data) {
237: ComponentData[] children = data.getChildren();
238: for (int i = 0; i < children.length; i++) {
239: ComponentData child = children[i];
240: // for each child component data, if it's an agent's component data
241: if (child.getType() == ComponentData.AGENT) {
242: // get all my agent components
243: Iterator iter = ((Collection) getDescendentsOfClass(AgentComponent.class))
244: .iterator();
245: while (iter.hasNext()) {
246: AgentComponent agent = (AgentComponent) iter.next();
247: // if the component data name matches the agent name
248: if (child.getName().equals(
249: agent.getShortName().toString())) {
250: // then set me as the owner of the component data
251: child.setOwner(this );
252: // and add the component data
253: agent.addComponentData(child);
254: // child.resetModified();
255: // Dont do above cause it leaves the HNA open, but it
256: // would at least ensure that if other recipe snuck in earlier,
257: // this worked OK.
258: }
259: }
260: } else {
261: // FIXME!! Will we support other top-level components?
262: // Process children of component data
263: addComponentData(child);
264: }
265: }
266:
267: // Reset the modified flag so nothing needed in Experiment.java,
268: // and only recipe changes will appear in the tree
269: // see comment in Experiment.save
270: data.resetModified();
271: // Note that this resets everything. So the HNA must be in the DB.
272: // And all recipe addCDatas must come after the society addCData
273: return data;
274: }
275:
276: private void readObject(ObjectInputStream ois) throws IOException,
277: ClassNotFoundException {
278: ois.defaultReadObject();
279: createLogger();
280: installListeners();
281: isRunning = false;
282: saveInProgress = false;
283: }
284:
285: /**
286: * Save this society to the database. Only to be used
287: * after creating a new society. Not to be used from DB societies
288: * which are already in the database.
289: *
290: * @return a <code>boolean</code>, false on error
291: */
292: public boolean saveToDatabase() {
293: saveInProgress = true;
294: if (log.isInfoEnabled()) {
295: log.info("saveToDatabase society (" + getSocietyName()
296: + ") with asb: " + getAssemblyId()
297: + " and old Assembly: " + oldAssemblyId);
298: }
299:
300: // TODO:
301: // Should I notice when I need to save and only save then?
302: // Should I resist creating a new assembly ID, to avoid
303: // breaking other experiments? Or always create one?
304:
305: String oldCMTAsbid = oldAssemblyId;
306: String currAssID = getAssemblyId();
307: String name = getSocietyName();
308:
309: if (currAssID != null && currAssID.startsWith("CMT")) {
310: if (log.isDebugEnabled()) {
311: log
312: .debug("saveToDB not saving CMT society ("
313: + getSocietyName()
314: + ") with id "
315: + currAssID
316: + " and old ID "
317: + oldCMTAsbid
318: + " into same ID. Will create new one and new name -- like a copy, except done in place");
319: }
320: oldCMTAsbid = currAssID;
321: currAssID = null;
322: name = name + " edited";
323: }
324:
325: // And what is my current assemblyID?
326: // How do I know if it is different?
327: // FIXME: Maybe I need a new
328:
329: // But probably only want to pass it in if it was in fact a CMT assembly, no?
330: // Or does it hurt to pass it in?
331: PopulateDb pdb = null;
332: boolean ret = true;
333: try {
334: // FIXME: Is there a non-gui conflict handler I should use?
335: pdb = new PopulateDb(oldCMTAsbid, name, currAssID, GUIUtils
336: .createSaveToDbConflictHandler(null), false);
337: pdb.populateCSA(SocietyComponentCreator
338: .getComponentData(this ));
339: // Set the new CSA assembly ID on the society - get it from the PDB
340: // setAssemblyId(pdb.getCMTAssemblyId());
341: assemblyId = pdb.getCMTAssemblyId();
342: // What about fixAssemblies?
343: // is it really populateCSA?
344: pdb.close();
345: } catch (Exception sqle) {
346: if (log.isErrorEnabled()) {
347: log.error("Error saving society to database: ", sqle);
348: }
349: ret = false;
350: } finally {
351: if (pdb != null) {
352: try {
353: pdb.close();
354: } catch (SQLException e) {
355: }
356: }
357: }
358: modified = false;
359: saveInProgress = false;
360: // tell listeners society is now saved
361: fireModification(new ModificationEvent(this , SOCIETY_SAVED));
362: return ret;
363: }
364:
365: // Save the copy in the database before returning
366: /**
367: * Copy the given society, including the modified status.
368: * The copy will store the original AssemblyId in the oldAssemblyId slot.
369: *
370: * @param name a <code>String</code> new society name
371: * @return a <code>ModifiableComponent</code> society copy
372: */
373: public ModifiableComponent copy(String name) {
374: if (log.isDebugEnabled()) {
375: log.debug("Copying society " + this .getSocietyName()
376: + " with assembly " + getAssemblyId()
377: + " into new name " + name);
378: }
379: // ModifiableComponent component = super.copy(name);
380: ComponentData cdata = SocietyComponentCreator
381: .getComponentData(this );
382: cdata.setName(name);
383: SocietyComponent component = new SocietyCDataComponent(cdata,
384: null);
385: component.initProperties();
386:
387: // Let the new society be marked as saved? That would force
388: // a save on exit though...
389: ((SocietyBase) component).modified = this .modified;
390:
391: // copy the assembly ID - the one under which this societies'
392: // data is currently in the DB, but must be copied
393: ((SocietyBase) component).oldAssemblyId = getAssemblyId();
394:
395: return component;
396: }
397:
398: /**
399: * Copy this Society and save the copy to the database, under
400: * the given new name. If the save fails, the new society
401: * will be marked modified when this method returns
402: *
403: * @param name a <code>String</code> new society name
404: * @return a <code>ModifiableComponent</code> new society
405: */
406: public ModifiableComponent copyAndSave(String name) {
407: ModifiableComponent component = copy(name);
408: if (!((SocietyBase) component).saveToDatabase()) {
409: ((SocietyBase) component).modified = true;
410: if (log.isWarnEnabled()) {
411: log.warn("Error saving copy of society "
412: + getSocietyName());
413: }
414: }
415: return component;
416: }
417:
418: /**
419: * Has this society been modified, such that a save would do something.
420: *
421: * @return a <code>boolean</code>, false if no save necessary
422: */
423: public boolean isModified() {
424: return modified;
425: }
426:
427: public void fireModification() {
428: modified = true;
429: super .fireModification();
430: }
431:
432: // only listen on local properties
433: // modifications of properties of agents, plugins, binders, etc.
434: // should be propagated, such that this society knows it's been modified
435:
436: private void installListeners() {
437: addPropertiesListener(this );
438: for (Iterator i = getLocalPropertyNames(); i.hasNext();) {
439: Property p = getProperty((CompositeName) i.next());
440: p.addPropertyListener(myPropertyListener);
441: }
442: }
443:
444: public void propertyAdded(PropertyEvent e) {
445: Property addedProperty = e.getProperty();
446: Property myProperty = getProperty(addedProperty.getName()
447: .last().toString());
448: if (myProperty != null) {
449: setPropertyVisible(addedProperty, true);
450: addedProperty.addPropertyListener(myPropertyListener);
451: fireModification();
452: }
453: }
454:
455: public void propertyRemoved(PropertyEvent e) {
456: e.getProperty().removePropertyListener(myPropertyListener);
457: fireModification();
458: }
459:
460: PropertyListener myPropertyListener = new PropertyListener() {
461: public void propertyValueChanged(PropertyEvent e) {
462: if (e == null || e.getProperty() == null)
463: return;
464: if (e.getProperty().getValue() == null) {
465: if (e.getPreviousValue() == null)
466: return;
467: else
468: fireModification();
469: } else if (e.getPreviousValue() == null) {
470: fireModification();
471: } else if (!e.getProperty().getValue().toString().trim()
472: .equals(e.getPreviousValue().toString()))
473: fireModification();
474: }
475:
476: public void propertyOtherChanged(PropertyEvent e) {
477: fireModification();
478: }
479: };
480:
481: ModificationListener myModificationListener = new MyModificationListener();
482:
483: public int addChild(ComposableComponent c) {
484: ((ModifiableConfigurableComponent) c)
485: .addModificationListener(myModificationListener);
486: fireModification();
487: return super .addChild(c);
488: }
489:
490: public void removeChild(ComposableComponent c) {
491: ((ModifiableConfigurableComponent) c)
492: .removeModificationListener(myModificationListener);
493: fireModification();
494: super .removeChild(c);
495: }
496:
497: class MyModificationListener implements ModificationListener,
498: ConfigurableComponentListener {
499: public void modified(ModificationEvent e) {
500: // don't propagate modifications when we're saving
501: if (!saveInProgress)
502: fireModification();
503: }
504: }
505:
506: }// SocietyBase
|