0001: /*
0002: * $RCSfile: SerializableRenderedImage.java,v $
0003: *
0004: * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * Use is subject to license terms.
0007: *
0008: * $Revision: 1.1 $
0009: * $Date: 2005/02/11 04:57:53 $
0010: * $State: Exp $
0011: */
0012: package javax.media.jai.remote;
0013:
0014: /*
0015: XXX: RFE (from Bob):
0016: If the SM can't be serialized perhaps a different SM know to be serializable
0017: could be created and the data copied.
0018: */
0019:
0020: import java.awt.Image;
0021: import java.awt.Point;
0022: import java.awt.Rectangle;
0023: import java.awt.image.ColorModel;
0024: import java.awt.image.DataBuffer;
0025: import java.awt.image.Raster;
0026: import java.awt.image.RenderedImage;
0027: import java.awt.image.SampleModel;
0028: import java.awt.image.WritableRaster;
0029: import java.io.ByteArrayInputStream;
0030: import java.io.ByteArrayOutputStream;
0031: import java.io.InputStream;
0032: import java.io.InterruptedIOException;
0033: import java.io.IOException;
0034: import java.io.NotSerializableException;
0035: import java.io.OutputStream;
0036: import java.io.ObjectInputStream;
0037: import java.io.ObjectOutputStream;
0038: import java.io.Serializable;
0039: import java.net.InetAddress;
0040: import java.net.Socket;
0041: import java.net.SocketException;
0042: import java.net.ServerSocket;
0043: import java.net.UnknownHostException;
0044: import java.util.Enumeration;
0045: import java.util.Hashtable;
0046: import java.util.Vector;
0047: import javax.media.jai.JAI;
0048: import javax.media.jai.OperationRegistry;
0049: import javax.media.jai.PlanarImage;
0050: import javax.media.jai.ParameterListDescriptor;
0051: import javax.media.jai.RasterAccessor;
0052: import javax.media.jai.RasterFormatTag;
0053: import javax.media.jai.RemoteImage;
0054: import javax.media.jai.TileCache;
0055: import javax.media.jai.remote.SerializableState;
0056: import javax.media.jai.remote.SerializerFactory;
0057: import javax.media.jai.tilecodec.TileCodecDescriptor;
0058: import javax.media.jai.tilecodec.TileCodecParameterList;
0059: import javax.media.jai.tilecodec.TileDecoder;
0060: import javax.media.jai.tilecodec.TileDecoderFactory;
0061: import javax.media.jai.tilecodec.TileEncoder;
0062: import javax.media.jai.tilecodec.TileEncoderFactory;
0063: import javax.media.jai.util.CaselessStringKey;
0064: import javax.media.jai.util.ImagingException;
0065: import javax.media.jai.util.ImagingListener;
0066: import com.sun.media.jai.util.ImageUtil;
0067:
0068: /**
0069: * A serializable wrapper class for classes which implement the
0070: * <code>RenderedImage</code> interface.
0071: *
0072: * <p> A <code>SerializableRenderedImage</code> provides a means to serialize
0073: * a <code>RenderedImage</code>. Transient fields are handled using
0074: * <code>Serializer</code>s registered with <code>SerializerFactory</code>.
0075: * Two means are available for providing the wrapped
0076: * <code>RenderedImage</code> data to a remote version of a
0077: * <code>SerializableRenderedImage</code> object: either via deep copy or by
0078: * "on-demand" copying. If a deep copy is requested, the entire image
0079: * <code>Raster</code> is copied during object serialization and tiles are
0080: * extracted from it as needed using the <code>Raster.createChild()</code>
0081: * method. If a deep copy is not used, the image data are transmitted
0082: * "on-demand" using socket communications. If the request is made on the
0083: * local host, the image data are provided in both cases merely by forwarding
0084: * the request to the wrapped <code>RenderedImage</code>. Note that a single
0085: * <code>SerializableRenderedImage</code> object should be able to service
0086: * multiple remote hosts.
0087: *
0088: * <p> The properties associated with the <code>RenderedImage</code> being
0089: * wrapped are serialized and accessible to a remote version of a
0090: * <code>SerializableRenderedImage</code> object. However it should be noted
0091: * that only those properties which are serializable are available to the
0092: * <code>SerializableRenderedImage</code> object.
0093: *
0094: * <p> This class makes no guarantee as to the stability of the data of the
0095: * wrapped image, at least in the case where a deep copy is not made.
0096: * Consequently if the data of a <code>RenderedImage</code> change but
0097: * affected tiles have already been transmitted then the modifications will
0098: * not be visible remotely. For example, this implies that a
0099: * <code>SerializableRenderedImage</code> should not be used to wrap a
0100: * <code>RenderedOp</code> the data of which are subject to change if the
0101: * chain in which the node is present is edited. Instead the
0102: * <code>SerializableRenderedImage</code> should be used to wrap the image
0103: * returned by invoking either <code>getRendering()</code> or
0104: * <code>createInstance()</code> on the <code>RenderedOp</code>. A similar
0105: * situation will obtain if the wrapped image is a
0106: * <code>WritableRenderedImage</code>. If in this case the wrapped image
0107: * is also a <code>PlanarImage</code>, then the image returned by
0108: * <code>createSnapshot()</code> should be wrapped instead.
0109: *
0110: * <p> An example of the usage of this class is as follows:
0111: *
0112: * <pre>
0113: * import java.io.IOException;
0114: * import java.io.ObjectInputStream;
0115: * import java.io.ObjectOutputStream;
0116: * import java.io.Serializable;
0117: *
0118: * public class SomeSerializableClass implements Serializable {
0119: * protected transient RenderedImage image;
0120: *
0121: * // Fields omitted.
0122: *
0123: * public SomeSerializableClass(RenderedImage image) {
0124: * this.image = image;
0125: * }
0126: *
0127: * // Methods omitted.
0128: *
0129: * // Serialization method.
0130: * private void writeObject(ObjectOutputStream out) throws IOException {
0131: * out.defaultWriteObject();
0132: * out.writeObject(new SerializableRenderedImage(image));
0133: * }
0134: *
0135: * // Deserialization method.
0136: * private void readObject(ObjectInputStream in)
0137: * throws IOException, ClassNotFoundException {
0138: * in.defaultReadObject();
0139: * image = (RenderedImage)in.readObject();
0140: * }
0141: * }
0142: * </pre>
0143: *
0144: * @see java.awt.image.RenderedImage
0145: * @see java.awt.image.WritableRenderedImage
0146: * @see javax.media.jai.PlanarImage
0147: * @see javax.media.jai.RenderedOp
0148: *
0149: *
0150: * @since JAI 1.1
0151: */
0152: // NB: This class was added in EA3 to com.sun.media.jai.rmi and made
0153: // public only in JAI 1.1.
0154: public final class SerializableRenderedImage implements RenderedImage,
0155: Serializable {
0156: /** Value to indicate the server socket timeout period (milliseconds). */
0157: private static final int SERVER_TIMEOUT = 60000; // XXX 1 minute?
0158:
0159: /** Message indicating that a client will not connect again. */
0160: private static final String CLOSE_MESSAGE = "CLOSE";
0161:
0162: /** Message indicating that the server read the client's close message. */
0163: private static final String CLOSE_ACK = "CLOSE_ACK";
0164:
0165: /** The unique ID of this image. */
0166: private Object UID;
0167:
0168: /** Flag indicating whether this is a data server. */
0169: private transient boolean isServer;
0170:
0171: /** Flag indicating whether the source image is a RemoteImage. */
0172: private boolean isSourceRemote;
0173:
0174: /** The RenderedImage source of this object (server only). */
0175: private transient RenderedImage source;
0176:
0177: /** The X coordinate of the image's upper-left pixel. */
0178: private int minX;
0179:
0180: /** The Y coordinate of the image's upper-left pixel. */
0181: private int minY;
0182:
0183: /** The image's width in pixels. */
0184: private int width;
0185:
0186: /** The image's height in pixels. */
0187: private int height;
0188:
0189: /** The horizontal index of the leftmost column of tiles. */
0190: private int minTileX;
0191:
0192: /** The vertical index of the uppermost row of tiles. */
0193: private int minTileY;
0194:
0195: /** The number of tiles along the tile grid in the horizontal direction. */
0196: private int numXTiles;
0197:
0198: /** The number of tiles along the tile grid in the vertical direction. */
0199: private int numYTiles;
0200:
0201: /** The width of a tile. */
0202: private int tileWidth;
0203:
0204: /** The height of a tile. */
0205: private int tileHeight;
0206:
0207: /** The X coordinate of the upper-left pixel of tile (0, 0). */
0208: private int tileGridXOffset;
0209:
0210: /** The Y coordinate of the upper-left pixel of tile (0, 0). */
0211: private int tileGridYOffset;
0212:
0213: /** The image's SampleModel. */
0214: private transient SampleModel sampleModel = null;
0215:
0216: /** The image's ColorModel. */
0217: private transient ColorModel colorModel = null;
0218:
0219: /** The image's sources, stored in a Vector. */
0220: private transient Vector sources = null;
0221:
0222: /** A Hashtable containing the image properties. */
0223: private transient Hashtable properties = null;
0224:
0225: /** Flag indicating whether to use a deep copy of the source image. */
0226: private boolean useDeepCopy;
0227:
0228: /** A Rectangle indicating the entire image bounds. */
0229: private Rectangle imageBounds;
0230:
0231: /** The entire image Raster (client only). */
0232: private transient Raster imageRaster;
0233:
0234: /** The Internet Protocol (IP) address of the instantiating host. */
0235: private InetAddress host;
0236:
0237: /** The port on which the data server is listening. */
0238: private int port;
0239:
0240: /** Flag indicating that the server is available for connections. */
0241: private transient boolean serverOpen = false;
0242:
0243: /** The server socket for image data transfer (server only). */
0244: private transient ServerSocket serverSocket = null;
0245:
0246: /** The thread in which the data server is running (server only). */
0247: private transient Thread serverThread;
0248:
0249: /** The tile codec format name is TileCodec is used */
0250: private String formatName;
0251:
0252: /** The specified <code>OperationRegistry</code> when TileCodec is used */
0253: private transient OperationRegistry registry;
0254:
0255: /**
0256: * A table of counts of remote references to instances of this class
0257: * (server only).
0258: *
0259: * <p> This table consists of entries with the keys being instances of
0260: * <code>SerializableRenderedImage</code> and the values being
0261: * <code>Integer</code>s the int value of which represents the number
0262: * of remote <code>SerializableRenderedImage</code> objects which could
0263: * potentially request a socket connection with the associated key. This
0264: * table is necessary to prevent the garbage collector of the interpreter
0265: * in which the server <code>SerializableRenderedImage</code> object is
0266: * instantiated from finalizing the object - and thereby closing its
0267: * server socket - when that object could still receive socket connection
0268: * requests from its remote clients. The reference to the object in the
0269: * static class variable ensures that the object will not be prematurely
0270: * finalized.
0271: */
0272: private static transient Hashtable remoteReferenceCount;
0273:
0274: /** Indicate that tilecodec is used in the transfering or not */
0275: private boolean useTileCodec = false;
0276:
0277: /** Cache the encoder factory */
0278: private transient TileDecoderFactory tileDecoderFactory = null;
0279:
0280: /** Cache the decoder factory */
0281: private transient TileEncoderFactory tileEncoderFactory = null;
0282:
0283: /** Cache the encoding/decoding parameters */
0284: private TileCodecParameterList encodingParam = null;
0285: private TileCodecParameterList decodingParam = null;
0286:
0287: /**
0288: * Increment the remote reference count of the argument.
0289: *
0290: * <p> If the argument is not already in the remote reference table,
0291: * add it to the table with a count value of unity. If it exists in
0292: * table, increment its count value.
0293: *
0294: * @parameter o The object the count value of which is to be incremented.
0295: */
0296: private static synchronized void incrementRemoteReferenceCount(
0297: Object o) {
0298: if (remoteReferenceCount == null) {
0299: remoteReferenceCount = new Hashtable();
0300: remoteReferenceCount.put(o, new Integer(1));
0301: } else {
0302: Integer count = (Integer) remoteReferenceCount.get(o);
0303: if (count == null) {
0304: remoteReferenceCount.put(o, new Integer(1));
0305: } else {
0306: remoteReferenceCount.put(o, new Integer(count
0307: .intValue() + 1));
0308: }
0309: }
0310: }
0311:
0312: /**
0313: * Decrement the remote reference count of the argument.
0314: *
0315: * <p> If the count value of the argument exists in the table its count
0316: * value is decremented unless the count value is unity in which case the
0317: * entry is removed from the table.
0318: *
0319: * @parameter o The object the count value of which is to be decremented.
0320: */
0321: private static synchronized void decrementRemoteReferenceCount(
0322: Object o) {
0323: if (remoteReferenceCount != null) {
0324: Integer count = (Integer) remoteReferenceCount.get(o);
0325: if (count != null) {
0326: if (count.intValue() == 1) {
0327: remoteReferenceCount.remove(o);
0328: } else {
0329: remoteReferenceCount.put(o, new Integer(count
0330: .intValue() - 1));
0331: }
0332: }
0333: }
0334: }
0335:
0336: /**
0337: * The default constructor.
0338: */
0339: SerializableRenderedImage() {
0340: }
0341:
0342: /**
0343: * Constructs a <code>SerializableRenderedImage</code> wrapper for a
0344: * <code>RenderedImage</code> source. Image data may be serialized
0345: * tile-by-tile or via a single deep copy. Tile encoding and
0346: * decoding may be effected via a <code>TileEncoder</code> and
0347: * <code>TileDecoder</code> specified by format name.
0348: *
0349: * <p> It may be noted that if the <code>TileCodec</code> utilizes
0350: * <code>Serializer</code>s for encoding the image data, and none
0351: * is available for the <code>DataBuffer</code> of the supplied
0352: * image, an error/exception may be encountered.
0353: *
0354: * @param source The <code>RenderedImage</code> source.
0355: * @param useDeepCopy Whether a deep copy of the entire image Raster
0356: * will be made during object serialization.
0357: * @param registry The <code>OperationRegistry</code> to use in
0358: * creating the <code>TileEncoder</code>. The
0359: * <code>TileDecoder</code> will of necessity be
0360: * created using the default <code>OperationRegistry</code>
0361: * as the specified <code>OperationRegistry</code> is not
0362: * serialized. If <code>null</code> the default registry
0363: * will be used.
0364: * @param formatName The name of the format used to encode the data.
0365: * If <code>null</code> simple tile serialization will
0366: * be performed either directly or by use of a "raw"
0367: * <code>TileCodec</code>.
0368: * @param encodingParam The parameters to be used for data encoding. If
0369: * <code>null</code> the default encoding
0370: * <code>TileCodecParameterList</code> for this
0371: * format will be used. Ignored if
0372: * <code>formatName</code> is <code>null</code>.
0373: * @param decodingParam The parameters to be used for data decoding. If
0374: * <code>null</code> a complementary
0375: * <code>TileCodecParameterList</code> will be
0376: * derived from <code>encodingParam</code>. Ignored
0377: * if <code>formatName</code> is <code>null</code>.
0378: *
0379: * @exception IllegalArgumentException if <code>source</code>
0380: * is <code>null</code>.
0381: * @exception IllegalArgumentException if no <code>Serializer</code>s
0382: * are available for the types of
0383: * <code>SampleModel</code>, and <code>ColorModel</code>
0384: * contained in the specified image.
0385: */
0386: public SerializableRenderedImage(RenderedImage source,
0387: boolean useDeepCopy, OperationRegistry registry,
0388: String formatName, TileCodecParameterList encodingParam,
0389: TileCodecParameterList decodingParam)
0390: throws NotSerializableException {
0391: this (source, useDeepCopy, false);
0392:
0393: // When the provided format name is null, return to directly serialize
0394: // this image
0395: if (formatName == null)
0396: return;
0397:
0398: this .formatName = formatName;
0399:
0400: // When the provided registry is null, use the default one
0401: if (registry == null)
0402: registry = JAI.getDefaultInstance().getOperationRegistry();
0403: this .registry = registry;
0404:
0405: // Fix 4640094: When the provided encodingParam is null, use the default one
0406: if (encodingParam == null) {
0407: TileCodecDescriptor tcd = getTileCodecDescriptor(
0408: "tileEncoder", formatName);
0409: encodingParam = tcd.getDefaultParameters("tileEncoder");
0410: } else if (!formatName.equals(encodingParam.getFormatName())) {
0411: throw new IllegalArgumentException(JaiI18N
0412: .getString("UseTileCodec0"));
0413: }
0414:
0415: // Fix 4640094: When the provided decodingParam is null, use the default one
0416: if (decodingParam == null) {
0417: TileCodecDescriptor tcd = getTileCodecDescriptor(
0418: "tileDecoder", formatName);
0419: decodingParam = tcd.getDefaultParameters("tileDecoder");
0420: } else if (!formatName.equals(decodingParam.getFormatName())) {
0421: throw new IllegalArgumentException(JaiI18N
0422: .getString("UseTileCodec1"));
0423: }
0424:
0425: tileEncoderFactory = (TileEncoderFactory) registry.getFactory(
0426: "tileEncoder", formatName);
0427: tileDecoderFactory = (TileDecoderFactory) registry.getFactory(
0428: "tileDecoder", formatName);
0429: if (tileEncoderFactory == null || tileDecoderFactory == null)
0430: throw new RuntimeException(JaiI18N
0431: .getString("UseTileCodec2"));
0432:
0433: this .encodingParam = encodingParam;
0434: this .decodingParam = decodingParam;
0435: useTileCodec = true;
0436: }
0437:
0438: /**
0439: * Constructs a <code>SerializableRenderedImage</code> wrapper for a
0440: * <code>RenderedImage</code> source. Image data may be serialized
0441: * tile-by-tile or via a single deep copy. No <code>TileCodec</code>
0442: * will be used, i.e., data will be transmitted using the serialization
0443: * protocol for <code>Raster</code>s.
0444: *
0445: * @param source The <code>RenderedImage</code> source.
0446: * @param useDeepCopy Whether a deep copy of the entire image Raster
0447: * will be made during object serialization.
0448: *
0449: * @exception IllegalArgumentException if <code>source</code>
0450: * is <code>null</code>.
0451: * @exception IllegalArgumentException if no <code>Serializer</code>s
0452: * are available for the types of <code>DataBuffer</code>,
0453: * <code>SampleModel</code>, and <code>ColorModel</code>
0454: * contained in the specified image.
0455: */
0456: public SerializableRenderedImage(RenderedImage source,
0457: boolean useDeepCopy) {
0458: this (source, useDeepCopy, true);
0459: }
0460:
0461: /**
0462: * Constructs a <code>SerializableRenderedImage</code> wrapper for a
0463: * <code>RenderedImage</code> source. Image data will be serialized
0464: * tile-by-tile if possible. No <code>TileCodec</code>
0465: * will be used, i.e., data will be transmitted using the serialization
0466: * protocol for <code>Raster</code>s.
0467: *
0468: * @param source The <code>RenderedImage</code> source.
0469: * @exception IllegalArgumentException if <code>source</code>
0470: * is <code>null</code>.
0471: * @exception IllegalArgumentException if no <code>Serializer</code>s
0472: * are available for the types of <code>DataBuffer</code>,
0473: * <code>SampleModel</code>, and <code>ColorModel</code>
0474: * contained in the specified image.
0475: */
0476: public SerializableRenderedImage(RenderedImage source) {
0477: this (source, false, true);
0478: }
0479:
0480: /**
0481: * Constructs a <code>SerializableRenderedImage</code> wrapper for a
0482: * <code>RenderedImage</code> source.
0483: *
0484: * @param source The <code>RenderedImage</code> source.
0485: * @param useDeepCopy Whether a deep copy of the entire image Raster
0486: * will be made during object serialization.
0487: * @param checkDataBuffer Whether checking serializable for DataBuffer
0488: * or not. If no <code>TileCodec</code> will be used, set it to true.
0489: * If <code>TileCodec</code> will be used, it is set to false.
0490: */
0491:
0492: private SerializableRenderedImage(RenderedImage source,
0493: boolean useDeepCopy, boolean checkDataBuffer) {
0494:
0495: UID = ImageUtil.generateID(this );
0496:
0497: if (source == null) {
0498: throw new IllegalArgumentException(JaiI18N
0499: .getString("SerializableRenderedImage0"));
0500: }
0501:
0502: SampleModel sm = source.getSampleModel();
0503: if (sm != null
0504: && SerializerFactory.getSerializer(sm.getClass()) == null) {
0505: throw new IllegalArgumentException(JaiI18N
0506: .getString("SerializableRenderedImage2"));
0507: }
0508:
0509: ColorModel cm = source.getColorModel();
0510: if (cm != null
0511: && SerializerFactory.getSerializer(cm.getClass()) == null) {
0512: throw new IllegalArgumentException(JaiI18N
0513: .getString("SerializableRenderedImage3"));
0514: }
0515:
0516: if (checkDataBuffer) {
0517: Raster ras = source.getTile(source.getMinTileX(), source
0518: .getMinTileY());
0519: if (ras != null) {
0520: DataBuffer db = ras.getDataBuffer();
0521: if (db != null
0522: && SerializerFactory.getSerializer(db
0523: .getClass()) == null)
0524: throw new IllegalArgumentException(JaiI18N
0525: .getString("SerializableRenderedImage4"));
0526: }
0527: }
0528:
0529: // Set server flag.
0530: isServer = true;
0531:
0532: // Cache the deep copy flag.
0533: this .useDeepCopy = useDeepCopy;
0534:
0535: // Cache the parameter.
0536: this .source = source;
0537:
0538: // Set remote source flag.
0539: this .isSourceRemote = source instanceof RemoteImage;
0540:
0541: // Initialize RenderedImage fields.
0542: minX = source.getMinX();
0543: minY = source.getMinY();
0544: width = source.getWidth();
0545: height = source.getHeight();
0546: minTileX = source.getMinTileX();
0547: minTileY = source.getMinTileY();
0548: numXTiles = source.getNumXTiles();
0549: numYTiles = source.getNumYTiles();
0550: tileWidth = source.getTileWidth();
0551: tileHeight = source.getTileHeight();
0552: tileGridXOffset = source.getTileGridXOffset();
0553: tileGridYOffset = source.getTileGridYOffset();
0554: sampleModel = source.getSampleModel();
0555: colorModel = source.getColorModel();
0556: sources = new Vector();
0557: sources.add(source);
0558: properties = new Hashtable();
0559: // XXX Property names should use CaselessStringKey for the
0560: // keys so that case is preserved.
0561: String[] propertyNames = source.getPropertyNames();
0562: if (propertyNames != null) {
0563: for (int i = 0; i < propertyNames.length; i++) {
0564: properties.put(propertyNames[i], source
0565: .getProperty(propertyNames[i]));
0566: }
0567: }
0568:
0569: // Initialize the image bounds.
0570: imageBounds = new Rectangle(minX, minY, width, height);
0571:
0572: // Initialize the host field.
0573: try {
0574: host = InetAddress.getLocalHost();
0575: } catch (UnknownHostException e) {
0576: throw new RuntimeException(e.getMessage());
0577: }
0578:
0579: // Unset the server availability flag.
0580: serverOpen = false;
0581: }
0582:
0583: /**
0584: * Private implementation of tile server.
0585: */
0586: private class TileServer implements Runnable {
0587: /**
0588: * Provide Rasters to clients on request.
0589: *
0590: * <p> This method is called by the data server thread when a deep copy
0591: * of the source image Raster is not being used. A socket connection is
0592: * set up at a well known address to which clients may connect. After a
0593: * client connects it transmits a Rectangle object which is read by
0594: * this method. The Raster corresponding to this Rectangle is then
0595: * retrieved from the source image and transmitted back over the
0596: * socket connection.
0597: *
0598: * <p> The server loop will continue until this object is garbage
0599: * collected.
0600: */
0601: public void run() {
0602: // Loop while the server availability flag is set.
0603: while (serverOpen) {
0604: // Wait for a client connection request.
0605: Socket socket = null;
0606: try {
0607: socket = serverSocket.accept();
0608: socket.setSoLinger(true, 1);
0609: } catch (InterruptedIOException e) {
0610: // accept() timeout: restart loop to check
0611: // availability flag.
0612: continue;
0613: } catch (SocketException e) {
0614: sendExceptionToListener(
0615: JaiI18N
0616: .getString("SerializableRenderedImage5"),
0617: new ImagingException(
0618: JaiI18N
0619: .getString("SerializableRenderedImage5"),
0620: e));
0621: // throw new RuntimeException(e.getMessage());
0622: } catch (IOException e) {
0623: sendExceptionToListener(
0624: JaiI18N
0625: .getString("SerializableRenderedImage6"),
0626: new ImagingException(
0627: JaiI18N
0628: .getString("SerializableRenderedImage6"),
0629: e));
0630: }
0631:
0632: // Get the socket input and output streams and wrap object
0633: // input and output streams around them, respectively.
0634: InputStream in = null;
0635: OutputStream out = null;
0636: ObjectInputStream objectIn = null;
0637: ObjectOutputStream objectOut = null;
0638: try {
0639: in = socket.getInputStream();
0640: out = socket.getOutputStream();
0641: objectIn = new ObjectInputStream(in);
0642: objectOut = new ObjectOutputStream(out);
0643: } catch (IOException e) {
0644: sendExceptionToListener(
0645: JaiI18N
0646: .getString("SerializableRenderedImage7"),
0647: new ImagingException(
0648: JaiI18N
0649: .getString("SerializableRenderedImage7"),
0650: e));
0651: // throw new RuntimeException(e.getMessage());
0652: }
0653:
0654: // Read the Object from the object stream.
0655: Object obj = null;
0656: try {
0657: obj = objectIn.readObject();
0658: } catch (IOException e) {
0659: sendExceptionToListener(
0660: JaiI18N
0661: .getString("SerializableRenderedImage8"),
0662: new ImagingException(
0663: JaiI18N
0664: .getString("SerializableRenderedImage8"),
0665: e));
0666: // throw new RuntimeException(e.getMessage());
0667: } catch (ClassNotFoundException e) {
0668: sendExceptionToListener(
0669: JaiI18N
0670: .getString("SerializableRenderedImage9"),
0671: new ImagingException(
0672: JaiI18N
0673: .getString("SerializableRenderedImage9"),
0674: e));
0675: }
0676:
0677: // Switch according to object class; ignore unsupported types.
0678: if (obj instanceof String
0679: && ((String) obj).equals(CLOSE_MESSAGE)) {
0680:
0681: try {
0682: objectOut.writeObject(CLOSE_ACK);
0683: } catch (IOException e) {
0684: sendExceptionToListener(
0685: JaiI18N
0686: .getString("SerializableRenderedImage17"),
0687: new ImagingException(
0688: JaiI18N
0689: .getString("SerializableRenderedImage17"),
0690: e));
0691: // throw new RuntimeException(e.getMessage());
0692: }
0693:
0694: // Decrement the remote reference count.
0695: decrementRemoteReferenceCount(this );
0696: } else if (obj instanceof Rectangle) {
0697:
0698: // Retrieve the Raster of data from the source image.
0699: Raster raster = source.getData((Rectangle) obj);
0700: // Write the serializable Raster to the
0701: // object output stream.
0702:
0703: if (useTileCodec) {
0704: byte[] buf = encodeRasterToByteArray(raster);
0705: try {
0706: objectOut.writeObject(buf);
0707: } catch (IOException e) {
0708: sendExceptionToListener(
0709: JaiI18N
0710: .getString("SerializableRenderedImage10"),
0711: new ImagingException(
0712: JaiI18N
0713: .getString("SerializableRenderedImage10"),
0714: e));
0715: // throw new RuntimeException(e.getMessage());
0716: }
0717: } else {
0718: try {
0719: objectOut.writeObject(SerializerFactory
0720: .getState(raster, null));
0721: } catch (IOException e) {
0722: sendExceptionToListener(
0723: JaiI18N
0724: .getString("SerializableRenderedImage10"),
0725: new ImagingException(
0726: JaiI18N
0727: .getString("SerializableRenderedImage10"),
0728: e));
0729: // throw new RuntimeException(e.getMessage());
0730: }
0731: }
0732: }
0733:
0734: // XXX Concerning serialization of properties, perhaps the
0735: // best approach would be to serialize all the properties up
0736: // front if a deep copy were being made but otherwise to wait
0737: // until the first property request was received before
0738: // transmitting any property values. When the first request
0739: // was made, all property values would be transmitted and then
0740: // cached. Up front serialization might in both cases include
0741: // transmitting all names. If property serialization were
0742: // deferred, then a new message branch would be added here
0743: // to retrieve the properties which could be obtained as
0744: // a PropertySourceImpl. If properties are also served up
0745: // then this inner class should be renamed "DataServer".
0746:
0747: // Close the various streams and the socket itself.
0748: try {
0749: objectOut.flush();
0750: socket.shutdownOutput();
0751: socket.shutdownInput();
0752: objectOut.close();
0753: objectIn.close();
0754: out.close();
0755: in.close();
0756: socket.close();
0757: } catch (IOException e) {
0758: sendExceptionToListener(
0759: JaiI18N
0760: .getString("SerializableRenderedImage10"),
0761: new ImagingException(
0762: JaiI18N
0763: .getString("SerializableRenderedImage10"),
0764: e));
0765: // throw new RuntimeException(e.getMessage());
0766: }
0767: }
0768: }
0769: }
0770:
0771: // --- Begin implementation of java.awt.image.RenderedImage. ---
0772:
0773: public WritableRaster copyData(WritableRaster dest) {
0774: if (isServer || isSourceRemote) {
0775: return source.copyData(dest);
0776: }
0777:
0778: Rectangle region;
0779: if (dest == null) {
0780: region = imageBounds;
0781: SampleModel destSM = getSampleModel()
0782: .createCompatibleSampleModel(region.width,
0783: region.height);
0784: dest = Raster.createWritableRaster(destSM, new Point(
0785: region.x, region.y));
0786: } else {
0787: region = dest.getBounds().intersection(imageBounds);
0788: }
0789:
0790: if (!region.isEmpty()) {
0791: int startTileX = PlanarImage.XToTileX(region.x,
0792: tileGridXOffset, tileWidth);
0793: int startTileY = PlanarImage.YToTileY(region.y,
0794: tileGridYOffset, tileHeight);
0795: int endTileX = PlanarImage.XToTileX(region.x + region.width
0796: - 1, tileGridXOffset, tileWidth);
0797: int endTileY = PlanarImage.YToTileY(region.y
0798: + region.height - 1, tileGridYOffset, tileHeight);
0799:
0800: SampleModel[] sampleModels = { getSampleModel() };
0801: int tagID = RasterAccessor.findCompatibleTag(sampleModels,
0802: dest.getSampleModel());
0803:
0804: RasterFormatTag srcTag = new RasterFormatTag(
0805: getSampleModel(), tagID);
0806: RasterFormatTag dstTag = new RasterFormatTag(dest
0807: .getSampleModel(), tagID);
0808:
0809: for (int ty = startTileY; ty <= endTileY; ty++) {
0810: for (int tx = startTileX; tx <= endTileX; tx++) {
0811: Raster tile = getTile(tx, ty);
0812: Rectangle subRegion = region.intersection(tile
0813: .getBounds());
0814:
0815: RasterAccessor s = new RasterAccessor(tile,
0816: subRegion, srcTag, getColorModel());
0817: RasterAccessor d = new RasterAccessor(dest,
0818: subRegion, dstTag, null);
0819: ImageUtil.copyRaster(s, d);
0820: }
0821: }
0822: }
0823:
0824: return dest;
0825: }
0826:
0827: public ColorModel getColorModel() {
0828: return colorModel;
0829: }
0830:
0831: public Raster getData() {
0832: if (isServer || isSourceRemote) {
0833: return source.getData();
0834: }
0835:
0836: return getData(imageBounds);
0837: }
0838:
0839: public Raster getData(Rectangle rect) {
0840: Raster raster = null;
0841:
0842: // Branch according to whether the object is a data server or, if not,
0843: // according to whether it is a data client and using a deep copy of
0844: // the source data or pulling the data as needed over a socket.
0845: if (isServer || isSourceRemote) {
0846: raster = source.getData(rect);
0847: } else if (useDeepCopy) {
0848: raster = imageRaster.createChild(rect.x, rect.y,
0849: rect.width, rect.height, rect.x, rect.y, null);
0850: } else {
0851: // TODO: Use a Hashtable to store Rasters as they are pulled over
0852: // the network and look them up here using "rect" as key?
0853:
0854: // Connect to the data server.
0855: Socket socket = connectToServer();
0856:
0857: // Get the socket input and output streams and wrap object
0858: // input and output streams around them, respectively.
0859: OutputStream out = null;
0860: ObjectOutputStream objectOut = null;
0861: InputStream in = null;
0862: ObjectInputStream objectIn = null;
0863: try {
0864: out = socket.getOutputStream();
0865: objectOut = new ObjectOutputStream(out);
0866: in = socket.getInputStream();
0867: objectIn = new ObjectInputStream(in);
0868: } catch (IOException e) {
0869: sendExceptionToListener(
0870: JaiI18N.getString("SerializableRenderedImage7"),
0871: new ImagingException(
0872: JaiI18N
0873: .getString("SerializableRenderedImage7"),
0874: e));
0875: // throw new RuntimeException(e.getMessage());
0876: }
0877:
0878: // Write the Rectangle to the object output stream.
0879: try {
0880: objectOut.writeObject(rect);
0881: } catch (IOException e) {
0882: sendExceptionToListener(
0883: JaiI18N
0884: .getString("SerializableRenderedImage10"),
0885: new ImagingException(
0886: JaiI18N
0887: .getString("SerializableRenderedImage10"),
0888: e));
0889: // throw new RuntimeException(e.getMessage());
0890: }
0891:
0892: // Read serialized form of the Raster from object output stream.
0893: Object object = null;
0894: try {
0895: object = objectIn.readObject();
0896: } catch (IOException e) {
0897: sendExceptionToListener(
0898: JaiI18N.getString("SerializableRenderedImage8"),
0899: new ImagingException(
0900: JaiI18N
0901: .getString("SerializableRenderedImage8"),
0902: e));
0903: // throw new RuntimeException(e.getMessage());
0904: } catch (ClassNotFoundException e) {
0905: sendExceptionToListener(
0906: JaiI18N.getString("SerializableRenderedImage9"),
0907: new ImagingException(
0908: JaiI18N
0909: .getString("SerializableRenderedImage9"),
0910: e));
0911: }
0912:
0913: if (useTileCodec) {
0914: byte[] buf = (byte[]) object;
0915: raster = decodeRasterFromByteArray(buf);
0916: } else {
0917: if (!(object instanceof SerializableState))
0918: raster = null;
0919: // Reconstruct the Raster from the serialized form.
0920: SerializableState ss = (SerializableState) object;
0921: Class c = ss.getObjectClass();
0922: if (Raster.class.isAssignableFrom(c)) {
0923: raster = (Raster) ss.getObject();
0924: } else
0925: raster = null;
0926: }
0927:
0928: // Close the various streams and the socket.
0929: try {
0930: objectOut.flush();
0931: socket.shutdownOutput();
0932: socket.shutdownInput();
0933: objectOut.close();
0934: out.close();
0935: objectIn.close();
0936: in.close();
0937: socket.close();
0938: } catch (IOException e) {
0939: String message = JaiI18N
0940: .getString("SerializableRenderedImage11");
0941: sendExceptionToListener(message, new ImagingException(
0942: message, e));
0943: // throw new RuntimeException(e.getMessage());
0944: }
0945:
0946: // If the rectangle equals the image bounds, cache the Raster,
0947: // switch to "deep copy" mode, and notify the data server.
0948: if (imageBounds.equals(rect)) {
0949:
0950: closeClient();
0951:
0952: imageRaster = raster;
0953: useDeepCopy = true;
0954: }
0955: }
0956:
0957: return raster;
0958: }
0959:
0960: public int getHeight() {
0961: return height;
0962: }
0963:
0964: public int getMinTileX() {
0965: return minTileX;
0966: }
0967:
0968: public int getMinTileY() {
0969: return minTileY;
0970: }
0971:
0972: public int getMinX() {
0973: return minX;
0974: }
0975:
0976: public int getMinY() {
0977: return minY;
0978: }
0979:
0980: public int getNumXTiles() {
0981: return numXTiles;
0982: }
0983:
0984: public int getNumYTiles() {
0985: return numYTiles;
0986: }
0987:
0988: // XXX Should getProperty() request property values over a socket
0989: // connection also?
0990: public Object getProperty(String name) {
0991: // XXX Use CaselessStringKeys for the property name.
0992: Object property = properties.get(name);
0993: return property == null ? Image.UndefinedProperty : property;
0994: }
0995:
0996: public String[] getPropertyNames() {
0997: String[] names = null;
0998: if (!properties.isEmpty()) {
0999: names = new String[properties.size()];
1000: Enumeration keys = properties.keys();
1001: int index = 0;
1002: while (keys.hasMoreElements()) {
1003: // XXX If CaselessStringKey keys are used then
1004: // getName() would have to be called here to get the
1005: // prop name from the key.
1006: names[index++] = (String) keys.nextElement();
1007: }
1008: }
1009: return names;
1010: }
1011:
1012: public SampleModel getSampleModel() {
1013: return sampleModel;
1014: }
1015:
1016: /**
1017: * If this <code>SerializableRenderedImage</code> has not been
1018: * serialized, this method returns a <code>Vector</code> containing
1019: * only the <code>RenderedImage</code> passed to the constructor; if
1020: * this image has been deserialized, it returns <code>null</code>.
1021: */
1022: public Vector getSources() {
1023: return sources;
1024: }
1025:
1026: public Raster getTile(int tileX, int tileY) {
1027: if (isServer || isSourceRemote) {
1028: return source.getTile(tileX, tileY);
1029: }
1030:
1031: TileCache cache = JAI.getDefaultInstance().getTileCache();
1032: if (cache != null) {
1033: Raster tile = cache.getTile(this , tileX, tileY);
1034: if (tile != null)
1035: return tile;
1036: }
1037:
1038: // Determine the active area; tile intersects with image's bounds.
1039: Rectangle imageBounds = new Rectangle(getMinX(), getMinY(),
1040: getWidth(), getHeight());
1041: Rectangle destRect = imageBounds.intersection(new Rectangle(
1042: tileXToX(tileX), tileYToY(tileY), getTileWidth(),
1043: getTileHeight()));
1044:
1045: Raster tile = getData(destRect);
1046:
1047: if (cache != null) {
1048: cache.add(this , tileX, tileY, tile);
1049: }
1050:
1051: return tile;
1052: }
1053:
1054: /**
1055: * Returns a unique identifier (UID) for this <code>RenderedImage</code>.
1056: * This UID may be used when the potential redundancy of the value
1057: * returned by the <code>hashCode()</code> method is unacceptable.
1058: * An example of this is in generating a key for storing image tiles
1059: * in a cache.
1060: */
1061: public Object getImageID() {
1062: return UID;
1063: }
1064:
1065: /**
1066: * Converts a horizontal tile index into the X coordinate of its
1067: * upper left pixel. No attempt is made to detect out-of-range
1068: * indices.
1069: *
1070: * <p> This method is implemented in terms of the <code>PlanarImage</code>
1071: * static method <code>tileXToX()</code> applied to the values returned
1072: * by primitive layout accessors.
1073: *
1074: * @param tx the horizontal index of a tile.
1075: * @return the X coordinate of the tile's upper left pixel.
1076: */
1077: private int tileXToX(int tx) {
1078: return PlanarImage.tileXToX(tx, getTileGridXOffset(),
1079: getTileWidth());
1080: }
1081:
1082: /**
1083: * Converts a vertical tile index into the Y coordinate of its
1084: * upper left pixel. No attempt is made to detect out-of-range
1085: * indices.
1086: *
1087: * <p> This method is implemented in terms of the
1088: * <code>PlanarImage</code> static method <code>tileYToY()</code>
1089: * applied to the values returned by primitive layout accessors.
1090: *
1091: * @param ty the vertical index of a tile.
1092: * @return the Y coordinate of the tile's upper left pixel.
1093: */
1094: private int tileYToY(int ty) {
1095: return PlanarImage.tileYToY(ty, getTileGridYOffset(),
1096: getTileHeight());
1097: }
1098:
1099: public int getTileGridXOffset() {
1100: return tileGridXOffset;
1101: }
1102:
1103: public int getTileGridYOffset() {
1104: return tileGridYOffset;
1105: }
1106:
1107: public int getTileHeight() {
1108: return tileHeight;
1109: }
1110:
1111: public int getTileWidth() {
1112: return tileWidth;
1113: }
1114:
1115: public int getWidth() {
1116: return width;
1117: }
1118:
1119: // --- End implementation of java.awt.image.RenderedImage. ---
1120:
1121: /**
1122: * Create a server socket and start the server in a separate thread.
1123: *
1124: * <p> Note that this method should be called only the first time this
1125: * object is serialized and only if a deep copy is not being used. If
1126: * a deep copy is used there is no need to serve clients data on demand.
1127: * However if data service is being provided, there is no need to create
1128: * multiple threads for the single object as a single server thread
1129: * should be able to service multiple remote objects.
1130: */
1131: private synchronized void openServer() throws IOException,
1132: SocketException {
1133: if (!serverOpen) {
1134: // Create a ServerSocket.
1135: serverSocket = new ServerSocket(0);
1136:
1137: // Set the ServerSocket accept() method timeout period.
1138: serverSocket.setSoTimeout(SERVER_TIMEOUT);
1139:
1140: // Initialize the port field.
1141: port = serverSocket.getLocalPort();
1142:
1143: // Set the server availability flag.
1144: serverOpen = true;
1145:
1146: // Spawn a child thread and return the parent thread to the caller.
1147: serverThread = new Thread(new TileServer());
1148: serverThread.setDaemon(true);
1149: serverThread.start();
1150:
1151: // Increment the remote reference count.
1152: incrementRemoteReferenceCount(this );
1153: }
1154: }
1155:
1156: /**
1157: * Transmit a message to the data server to indicate that the client
1158: * will no longer request socket connections.
1159: */
1160: private void closeClient() {
1161:
1162: // Connect to the data server.
1163: Socket socket = connectToServer();
1164:
1165: // Get the socket output stream and wrap an object
1166: // output stream around it.
1167: OutputStream out = null;
1168: ObjectOutputStream objectOut = null;
1169: ObjectInputStream objectIn = null;
1170: try {
1171: out = socket.getOutputStream();
1172: objectOut = new ObjectOutputStream(out);
1173: objectIn = new ObjectInputStream(socket.getInputStream());
1174: } catch (IOException e) {
1175: sendExceptionToListener(
1176: JaiI18N.getString("SerializableRenderedImage7"),
1177: new ImagingException(JaiI18N
1178: .getString("SerializableRenderedImage7"), e));
1179: // throw new RuntimeException(e.getMessage());
1180: }
1181:
1182: // Write CLOSE_MESSAGE to the object output stream.
1183: try {
1184: objectOut.writeObject(CLOSE_MESSAGE);
1185: } catch (IOException e) {
1186: sendExceptionToListener(JaiI18N
1187: .getString("SerializableRenderedImage13"),
1188: new ImagingException(JaiI18N
1189: .getString("SerializableRenderedImage13"),
1190: e));
1191: // throw new RuntimeException(e.getMessage());
1192: }
1193:
1194: try {
1195: objectIn.readObject();
1196: } catch (IOException e) {
1197: sendExceptionToListener(
1198: JaiI18N.getString("SerializableRenderedImage8"),
1199: new ImagingException(JaiI18N
1200: .getString("SerializableRenderedImage8"), e));
1201: } catch (ClassNotFoundException cnfe) {
1202: sendExceptionToListener(JaiI18N
1203: .getString("SerializableRenderedImage9"),
1204: new ImagingException(JaiI18N
1205: .getString("SerializableRenderedImage9"),
1206: cnfe));
1207: }
1208:
1209: // Close the streams and the socket.
1210: try {
1211: objectOut.flush();
1212: socket.shutdownOutput();
1213: objectOut.close();
1214: out.close();
1215: objectIn.close();
1216: socket.close();
1217: } catch (IOException e) {
1218: sendExceptionToListener(JaiI18N
1219: .getString("SerializableRenderedImage11"),
1220: new ImagingException(JaiI18N
1221: .getString("SerializableRenderedImage11"),
1222: e));
1223: // throw new RuntimeException(e.getMessage());
1224: }
1225: }
1226:
1227: /**
1228: * Obtain a connection to the data server socket. This is used only if a
1229: * deep copy of the image Raster has not been made.
1230: */
1231: private Socket connectToServer() {
1232: // Open a connection to the data server.
1233: Socket socket = null;
1234: try {
1235: socket = new Socket(host, port);
1236: socket.setSoLinger(true, 1);
1237: } catch (IOException e) {
1238: sendExceptionToListener(JaiI18N
1239: .getString("SerializableRenderedImage14"),
1240: new ImagingException(JaiI18N
1241: .getString("SerializableRenderedImage14"),
1242: e));
1243: // throw new RuntimeException(e.getMessage());
1244: }
1245:
1246: return socket;
1247: }
1248:
1249: /**
1250: * When useTileCodec is set, encode the provided raster into
1251: * a byte array.
1252: */
1253: private byte[] encodeRasterToByteArray(Raster raster) {
1254: ByteArrayOutputStream bos = new ByteArrayOutputStream();
1255: TileEncoder encoder = tileEncoderFactory.createEncoder(bos,
1256: encodingParam, raster.getSampleModel());
1257: try {
1258: encoder.encode(raster);
1259: return bos.toByteArray();
1260: } catch (IOException e) {
1261: sendExceptionToListener(JaiI18N
1262: .getString("SerializableRenderedImage15"),
1263: new ImagingException(JaiI18N
1264: .getString("SerializableRenderedImage15"),
1265: e));
1266: // throw new RuntimeException(e.getMessage());
1267: }
1268: return null;
1269: }
1270:
1271: /**
1272: * When useTileCodec is set, decode the raster from a byte array.
1273: */
1274: private Raster decodeRasterFromByteArray(byte[] buf) {
1275: ByteArrayInputStream bis = new ByteArrayInputStream(buf);
1276:
1277: // Fix 4640094 Tilecodec doesn't work well in SerializableRenderedImage
1278: // Currently, ParameterListDescriptor is singleton to a specific
1279: // tile codec and mode. After deserialization this property is gone.
1280: // So need to copy the parameter values into the newly created object
1281: if (tileDecoderFactory == null) {
1282: // Use the default operation registry as described in the spec
1283: // of the constructor.
1284: if (registry == null)
1285: registry = JAI.getDefaultInstance()
1286: .getOperationRegistry();
1287: tileDecoderFactory = (TileDecoderFactory) registry
1288: .getFactory("tileDecoder", formatName);
1289:
1290: TileCodecParameterList temp = decodingParam;
1291:
1292: if (temp != null) {
1293: TileCodecDescriptor tcd = getTileCodecDescriptor(
1294: "tileDecoder", formatName);
1295: ParameterListDescriptor pld = tcd
1296: .getParameterListDescriptor("tileDecoder");
1297: decodingParam = new TileCodecParameterList(formatName,
1298: new String[] { "tileDecoder" }, pld);
1299: String[] names = pld.getParamNames();
1300:
1301: if (names != null)
1302: for (int i = 0; i < names.length; i++)
1303: decodingParam.setParameter(names[i], temp
1304: .getObjectParameter(names[i]));
1305:
1306: } else
1307: decodingParam = getTileCodecDescriptor("tileDecoder",
1308: formatName).getDefaultParameters("tileDecoder");
1309: }
1310:
1311: TileDecoder decoder = tileDecoderFactory.createDecoder(bis,
1312: decodingParam);
1313: try {
1314: return decoder.decode();
1315: } catch (IOException e) {
1316: sendExceptionToListener(JaiI18N
1317: .getString("SerializableRenderedImage16"),
1318: new ImagingException(JaiI18N
1319: .getString("SerializableRenderedImage16"),
1320: e));
1321: // throw new RuntimeException(e.getMessage());
1322: }
1323: return null;
1324: }
1325:
1326: /**
1327: * If a deep copy is not being used, unset the data server availability
1328: * flag and wait for the server thread to rejoin the current thread.
1329: */
1330: protected void finalize() throws Throwable {
1331: dispose();
1332:
1333: // Forward to the parent class.
1334: super .finalize();
1335: }
1336:
1337: /**
1338: * Provides a hint that an image will no longer be accessed from a
1339: * reference in user space. The results are equivalent to those
1340: * that occur when the program loses its last reference to this
1341: * image, the garbage collector discovers this, and finalize is
1342: * called. This can be used as a hint in situations where waiting
1343: * for garbage collection would be overly conservative, e.g., there
1344: * are a large number of socket connections which may be opened to
1345: * transmit tile data.
1346: *
1347: * <p> <code>SerializableRenderedImage</code> defines this method to
1348: * behave as follows:
1349: * <ul>
1350: * <li>if the image is acting as a server, i.e., has never been
1351: * serialized and may be providing data to serialized
1352: * versions of itself, it makes itself unavailable to further
1353: * client requests and closes its socket;</li>
1354: * <li>if the image is acting as a client, i.e., has been serialized
1355: * and may be requesting data from a remote, pre-serialization version
1356: * of itself, it sends a message to its remote self indicating that it
1357: * will no longer be making requests.</li>
1358: * </ul>
1359: *
1360: * <p> The results of referencing an image after a call to
1361: * <code>dispose()</code> are undefined.
1362: */
1363: public void dispose() {
1364: // Rejoin the server thread if using a socket-based server.
1365: if (isServer) {
1366: if (serverOpen) {
1367: // Unset availability flag so server loop exits.
1368: serverOpen = false;
1369:
1370: // Wait for the server (child) thread to die.
1371: try {
1372: serverThread.join(2 * SERVER_TIMEOUT);
1373: } catch (Exception e) {
1374: // Ignore the Exception.
1375: }
1376:
1377: // Close the server socket.
1378: try {
1379: serverSocket.close();
1380: } catch (Exception e) {
1381: // Ignore the Exception.
1382: }
1383: }
1384: } else { // client
1385: // Transmit a message to the server to indicate the child's exit.
1386: closeClient();
1387: }
1388: }
1389:
1390: /**
1391: * Custom serialization method. In addition to all non-transient fields,
1392: * the SampleModel, source vector, and properties table are serialized.
1393: * If a deep copy of the source image Raster is being used this is also
1394: * serialized.
1395: */
1396: private void writeObject(ObjectOutputStream out) throws IOException {
1397: if (!useDeepCopy) {
1398: // Start the data server.
1399: try {
1400: openServer();
1401: } catch (Exception e1) {
1402: if (e1 instanceof SocketException) { // setSoTimeout() failed.
1403: if (serverSocket != null) { // XXX Facultative
1404: try {
1405: serverSocket.close();
1406: } catch (IOException e2) {
1407: // Ignore the exception.
1408: }
1409: }
1410: }
1411:
1412: // Since server socket creation failed, use a deep copy.
1413: serverOpen = false; // XXX Facultative
1414: useDeepCopy = true;
1415: }
1416: }
1417:
1418: // Write non-static and non-transient fields.
1419: out.defaultWriteObject();
1420:
1421: // Write RMI properties of RemoteImage.
1422: if (isSourceRemote) {
1423: String remoteClass = source.getClass().getName();
1424: out.writeObject(source.getProperty(remoteClass
1425: + ".serverName"));
1426: out.writeObject(source.getProperty(remoteClass + ".id"));
1427: }
1428:
1429: // Remove non-serializable elements from table of properties.
1430: Hashtable propertyTable = properties;
1431: boolean propertiesCloned = false;
1432: Enumeration keys = propertyTable.keys();
1433: while (keys.hasMoreElements()) {
1434: Object key = keys.nextElement();
1435: if (!(properties.get(key) instanceof Serializable)) {
1436: if (!propertiesCloned) {
1437: propertyTable = (Hashtable) properties.clone();
1438: propertiesCloned = true;
1439: }
1440: propertyTable.remove(key);
1441: }
1442: }
1443:
1444: // Write the source vector and properties table.
1445: out.writeObject(SerializerFactory.getState(sampleModel, null));
1446: out.writeObject(SerializerFactory.getState(colorModel, null));
1447: out.writeObject(propertyTable);
1448:
1449: // Make a deep copy of the image raster.
1450: if (useDeepCopy) {
1451: if (useTileCodec)
1452: out.writeObject(encodeRasterToByteArray(source
1453: .getData()));
1454: else {
1455: out.writeObject(SerializerFactory.getState(source
1456: .getData(), null));
1457: }
1458: }
1459: }
1460:
1461: /**
1462: * Custom deserialization method. In addition to all non-transient fields,
1463: * the SampleModel, source vector, and properties table are deserialized.
1464: * If a deep copy of the source image Raster is being used this is also
1465: * deserialized.
1466: */
1467: private void readObject(ObjectInputStream in) throws IOException,
1468: ClassNotFoundException {
1469: isServer = false;
1470: source = null;
1471: serverOpen = false;
1472: serverSocket = null;
1473: serverThread = null;
1474: colorModel = null;
1475:
1476: // Read non-static and non-transient fields.
1477: in.defaultReadObject();
1478:
1479: if (isSourceRemote) {
1480: // Read RMI properties of RemoteImage.
1481: String serverName = (String) in.readObject();
1482: Long id = (Long) in.readObject();
1483:
1484: // Recreate remote source using the ID directly.
1485: source = new RemoteImage(
1486: serverName + "::" + id.longValue(),
1487: (RenderedImage) null);
1488: }
1489:
1490: // Read the source vector and properties table.
1491: SerializableState smState = (SerializableState) in.readObject();
1492: sampleModel = (SampleModel) smState.getObject();
1493: SerializableState cmState = (SerializableState) in.readObject();
1494: colorModel = (ColorModel) cmState.getObject();
1495: properties = (Hashtable) in.readObject();
1496:
1497: // Read the image Raster.
1498: if (useDeepCopy) {
1499: if (useTileCodec)
1500: imageRaster = decodeRasterFromByteArray((byte[]) in
1501: .readObject());
1502: else {
1503: SerializableState rasState = (SerializableState) in
1504: .readObject();
1505: imageRaster = (Raster) rasState.getObject();
1506: }
1507: }
1508: }
1509:
1510: private TileCodecDescriptor getTileCodecDescriptor(
1511: String registryMode, String formatName) {
1512: if (registry == null)
1513: return (TileCodecDescriptor) JAI.getDefaultInstance()
1514: .getOperationRegistry().getDescriptor(registryMode,
1515: formatName);
1516: return (TileCodecDescriptor) registry.getDescriptor(
1517: registryMode, formatName);
1518: }
1519:
1520: void sendExceptionToListener(String message, Exception e) {
1521: ImagingListener listener = JAI.getDefaultInstance()
1522: .getImagingListener();
1523: listener.errorOccurred(message, e, this , false);
1524: }
1525: }
|