001: /*
002: * <copyright>
003: *
004: * Copyright 2003-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.yp;
028:
029: import java.io.BufferedInputStream;
030: import java.io.BufferedOutputStream;
031: import java.io.ByteArrayInputStream;
032: import java.io.ByteArrayOutputStream;
033: import java.io.File;
034: import java.io.FileInputStream;
035: import java.io.FileOutputStream;
036: import java.io.IOException;
037: import java.io.ObjectInputStream;
038: import java.io.ObjectOutputStream;
039: import java.io.Serializable;
040: import java.util.zip.ZipEntry;
041: import java.util.zip.ZipInputStream;
042: import java.util.zip.ZipOutputStream;
043:
044: import org.cougaar.util.log.Logger;
045: import org.cougaar.util.log.Logging;
046:
047: /**
048: * A serialization wrapper around the disk files which make up the database.
049: * The key point here is that these objects are trivial to create (four data members,
050: * the non-trivial ones pointing to externally maintained objects), but expensive
051: * to serialize and read.
052: * <p>
053: * Essentially, serializing a DatabaseEnvelope means zipping up the contents of the
054: * database file directory while holding the database activity lock so that it cannot
055: * be changed. The zip archive is then copied to the serialization stream as a byte array.
056: * <p>
057: * Reading one of these objects keeps the byte array around for later dumping back to
058: * a directory.
059: *
060: */
061:
062: public class DatabaseEnvelope implements Serializable {
063: public static final Logger logger = Logging
064: .getLogger(DatabaseEnvelope.class);
065:
066: private File directory;
067: private long timestamp;
068:
069: private transient byte[] stuff = null; // if non-null, contains a persist snapshot
070: private transient Locker locker = null;
071:
072: DatabaseEnvelope(File directory, Locker locker) {
073: assert directory.isDirectory();
074: assert locker != null;
075: this .directory = directory;
076: this .locker = locker;
077: timestamp = System.currentTimeMillis();
078: if (logger.isInfoEnabled())
079: logger.info("Created " + this );
080: }
081:
082: public String toString() {
083: return "DatabaseEnvelope of " + directory + " @" + timestamp
084: + ((stuff == null) ? "" : " (pending)");
085: }
086:
087: private void writeObject(ObjectOutputStream out) throws IOException {
088: if (logger.isInfoEnabled())
089: logger.info("About to serialize " + this );
090: out.defaultWriteObject();
091: if (locker == null) { // if locker is null, then we've already rehydrated
092: assert stuff != null;
093: out.writeObject(stuff); // might as well support re-writing
094: if (logger.isInfoEnabled())
095: logger.info("Reserialized " + this );
096: } else {
097: ByteArrayOutputStream bos = new ByteArrayOutputStream();
098:
099: synchronized (locker) { // sync
100: try {
101: locker.stop();
102:
103: // when we are really called, we serialize the database into the stream (yech!)
104: ZipOutputStream zos = new ZipOutputStream(bos);
105: // we'll leave it the default for now since this stuff is easily compressed.
106: File[] files = directory.listFiles();
107: byte[] buf = new byte[1024];
108: for (int i = 0; i < files.length; i++) {
109: File f = files[i];
110: if (!f.isFile())
111: continue; // skip any directories
112:
113: ZipEntry ze = new ZipEntry(f.getName());
114: zos.putNextEntry(ze);
115:
116: // copy the file to the zip stream
117: BufferedInputStream fin = new BufferedInputStream(
118: new FileInputStream(f));
119: int len;
120: while ((len = fin.read(buf, 0, 1024)) > 0) {
121: zos.write(buf, 0, len);
122: }
123: fin.close();
124:
125: zos.closeEntry();
126: }
127: zos.finish();
128:
129: } finally {
130: locker.start();
131: }
132: }
133:
134: out.writeObject(bos.toByteArray());
135: if (logger.isInfoEnabled())
136: logger.info("Serialized " + this );
137: }
138: }
139:
140: private void readObject(ObjectInputStream in)
141: throws ClassNotFoundException, IOException {
142: in.defaultReadObject();
143: // when we read the object, we just suck the bits into our byte array - if we need to
144: // use the bits, we'll have to fool with the database at that point.
145: stuff = (byte[]) in.readObject();
146: if (logger.isInfoEnabled())
147: logger.info("Got rehydration snapshot (" + stuff.length
148: + " bytes) from " + this );
149: }
150:
151: public boolean hasPayload() {
152: return stuff != null;
153: }
154:
155: /** dump the stored bytes into the specified directory **/
156: public void dumpPayload(File parent) throws IOException {
157: assert hasPayload();
158:
159: if (logger.isInfoEnabled())
160: logger.info("Rehydrating from " + this );
161:
162: ByteArrayInputStream is = new ByteArrayInputStream(stuff);
163: ZipInputStream zis = new ZipInputStream(is);
164: byte[] buf = new byte[1024];
165:
166: ZipEntry ze;
167: while ((ze = zis.getNextEntry()) != null) {
168: String fname = ze.getName();
169: File f = new File(parent, fname);
170: if (f.exists())
171: f.delete();
172: BufferedOutputStream out = new BufferedOutputStream(
173: new FileOutputStream(f));
174: int len;
175: while ((len = zis.read(buf)) > 0) {
176: out.write(buf, 0, len);
177: }
178: out.close();
179: zis.closeEntry();
180: }
181: zis.close();
182:
183: // when done, should we null the stuff to prime the gc? No - we need to be able to
184: // re-write it until the database gets another snapshot taken.
185: //stuff = null;
186: }
187:
188: /** Database Lock abstraction - provides for both locking (to prevent database modifications
189: * during backups) and stop/start (to provide for cache flushing and database compression)
190: **/
191: public interface Locker {
192: /** call while synchronized to start the database instance **/
193: void stop();
194:
195: /** call while synchronized and stopped to restart the database instance **/
196: void start();
197: }
198:
199: }
|