001: /*
002: * <copyright>
003: *
004: * Copyright 1997-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: */
026:
027: package org.cougaar.core.persist;
028:
029: import java.io.IOException;
030: import java.io.ObjectOutputStream;
031: import java.util.ArrayList;
032: import java.util.List;
033:
034: import org.cougaar.core.mts.MessageAddress;
035: import org.cougaar.util.LinkedByteOutputStream;
036: import org.cougaar.util.log.Logger;
037:
038: /**
039: * Write persistable objects to a stream.
040: * @see PersistenceInputStream
041: */
042: public class PersistenceOutputStream extends ObjectOutputStream
043: implements PersistenceStream {
044:
045: private static final int DEFAULT_INITIAL_SIZE = 10000;
046:
047: private Logger logger;
048: private boolean detail;
049:
050: public MessageAddress getOriginator() {
051: return null;
052: }
053:
054: public MessageAddress getTarget() {
055: return null;
056: }
057:
058: /**
059: * Keeps track of the (identity of) objects written to the stream.
060: */
061: private List writeIndex = null;
062:
063: /**
064: * The stream to which the objects are written
065: */
066: private LinkedByteOutputStream byteStream;
067:
068: /**
069: * Public constructor
070: */
071: public PersistenceOutputStream(Logger logger) throws IOException {
072: this (new LinkedByteOutputStream(DEFAULT_INITIAL_SIZE), logger);
073: }
074:
075: /**
076: * Private constructor needed to capture the byte buffer
077: * we are using.
078: * @param stream the byte buffer into which we store everything.
079: */
080: private PersistenceOutputStream(LinkedByteOutputStream stream,
081: Logger logger) throws IOException {
082: super (stream);
083: byteStream = stream;
084: enableReplaceObject(true);
085: this .logger = logger;
086: this .detail = logger.isDetailEnabled();
087: }
088:
089: // /**
090: // * Get the array of bytes encoding everything we stored.
091: // * @return the array of bytes encoding everything we stored.
092: // */
093: // public byte[] getBytes() throws IOException {
094: // flush();
095: // if (history != null) {
096: // history.close();
097: // history = null;
098: // }
099: // return byteStream.toByteArray();
100: // }
101:
102: /**
103: * Write the bytes encoding everything we stored preceded by the
104: * byte count.
105: */
106: public void writeBytes(ObjectOutputStream oos) throws IOException {
107: oos.writeInt(byteStream.size());
108: byteStream.writeTo(oos);
109: }
110:
111: public byte[] getBytes() {
112: return byteStream.toByteArray();
113: }
114:
115: public int size() {
116: return byteStream.size();
117: }
118:
119: /**
120: * Write a plan object from a PersistenceAssociation.
121: * @param pAssoc the PersistenceAssocation having the object to save.
122: * @return an array of the PersistenceReferences of the objects that
123: * were actually saved. This typically includes the objects that are
124: * referenced by the object being saved (and the objects they
125: * reference, etc.).
126: */
127: public PersistenceReference[] writeAssociation(
128: PersistenceAssociation pAssoc) throws IOException {
129: Object obj = pAssoc.getObject();
130: if (obj == null) {
131: logger.error("pAssoc has null object: " + pAssoc);
132: }
133: writeIndex = new ArrayList();
134: writeInt(pAssoc.getActive());
135: writeObject(pAssoc.getClientId());
136: try {
137: writeObject(obj);
138: } catch (java.io.NotSerializableException e) {
139: logger.error(e + " for: " + obj.getClass().getName() + ": "
140: + obj);
141: } catch (IllegalArgumentException e) {
142: logger.error(e + " for: " + obj.getClass().getName());
143: } catch (Exception e) {
144: logger.error(e + " for: " + obj.getClass().getName() + ": "
145: + obj);
146: if (e.getClass().getName().equals(
147: java.io.NotSerializableException.class.getName())) {
148: logger.error("Catch failed", e);
149: } else {
150: IOException ioe = new IOException();
151: ioe.initCause(e);
152: throw ioe;
153: }
154: }
155: PersistenceReference[] result = new PersistenceReference[writeIndex
156: .size()];
157: result = (PersistenceReference[]) writeIndex.toArray(result);
158: writeIndex = null;
159: return result;
160: }
161:
162: /**
163: * Replace objects that are in the identityTable with reference
164: * objects. This operation is suppressed for objects that have
165: * changed and actually need to be written.
166: * @param o the object to consider for replacement.
167: * @return the replacement object.
168: */
169: protected Object replaceObject(Object o) {
170: if (o instanceof PersistenceReference) {
171: if (detail)
172: print("Writing Ref ", o);
173: return o;
174: }
175: if (o.getClass().isArray()) {
176: if (detail)
177: print("Writing array "
178: + java.lang.reflect.Array.getLength(o));
179: return o;
180: }
181: if (o instanceof String) {
182: if (detail)
183: print("Writing String ", o);
184: return o;
185: }
186: PersistenceAssociation pAssoc = identityTable.find(o);
187: String ix = null;
188: if (detail) {
189: if (writeIndex != null) {
190: ix = writeIndex.size() + " ";
191: } else {
192: ix = "";
193: }
194: }
195: if (pAssoc == null) {
196: // This is (probably) _not_ something we care about
197: if ((o instanceof ActivePersistenceObject)
198: && ((ActivePersistenceObject) o)
199: .skipUnpublishedPersist(logger)) {
200: if (detail)
201: print("Skipping " + ix + getShortClassName(o) + " "
202: + o);
203: return null; // Not published yet
204: }
205: if (writeIndex != null) {
206: writeIndex.add(null); // No identityTable fixup needed
207: }
208: if (detail)
209: print("Writing " + ix + getShortClassName(o) + " " + o);
210: return o;
211: }
212: if (pAssoc.isMarked()) {
213: if (writeIndex != null) {
214: // Remember that we wrote it here
215: writeIndex.add(pAssoc.getReferenceId());
216: }
217: if (detail)
218: print("Writing " + ix, pAssoc, " as ", o);
219: return o;
220: }
221: if (detail)
222: print("Subst ", pAssoc, " for ", o);
223: return pAssoc.getReferenceId();
224: }
225:
226: private String getShortClassName(Object o) {
227: String cn = o.getClass().getName();
228: int dot = cn.lastIndexOf('.');
229: if (dot >= 0)
230: return cn.substring(dot + 1);
231: return cn;
232: }
233:
234: private void print(String intro, PersistenceAssociation pAssoc,
235: String prep, Object o) {
236: print(intro + pAssoc.getReferenceId()
237: + (pAssoc.isActive() ? " active" : " inactive") + prep
238: + o);
239: }
240:
241: private void print(String intro, Object o) {
242: print(intro + o);
243: }
244:
245: private void print(String message) {
246: logger.detail(message);
247: }
248:
249: /**
250: * Object identity table. This is supplied by the creator of this stream.
251: */
252: private IdentityTable identityTable;
253:
254: /**
255: * Get the IdentityTable being used by this stream. This is not
256: * normally used since the IdentityTable is usually maintained by
257: * the creator of this stream.
258: * @return the IdentityTable being used by this stream.
259: */
260: public IdentityTable getIdentityTable() {
261: return identityTable;
262: }
263:
264: /**
265: * Set the IdentityTable to be used by this stream. The
266: * IdentityTable contains assocations of objects to earlier
267: * persistence deltas. References to these earlier objects are
268: * replaced with reference objects to save space.
269: * @param identityTable the new IdentityTable to use.
270: */
271: public void setIdentityTable(IdentityTable identityTable) {
272: this.identityTable = identityTable;
273: }
274: }
|