0001: /*
0002: * $RCSfile: SerializableRenderableImage.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:56:54 $
0010: * $State: Exp $
0011: */package com.sun.media.jai.rmi;
0012:
0013: /*
0014: XXX
0015: See if the SerializableRenderedImage can be sent by requests instead of
0016: deep copy.
0017: */
0018:
0019: import java.awt.Image;
0020: import java.awt.RenderingHints;
0021: import java.awt.image.RenderedImage;
0022: import java.awt.image.renderable.RenderableImage;
0023: import java.awt.image.renderable.RenderContext;
0024: import java.io.InputStream;
0025: import java.io.InterruptedIOException;
0026: import java.io.IOException;
0027: import java.io.NotSerializableException;
0028: import java.io.OutputStream;
0029: import java.io.ObjectInputStream;
0030: import java.io.ObjectOutputStream;
0031: import java.io.Serializable;
0032: import java.net.InetAddress;
0033: import java.net.Socket;
0034: import java.net.SocketException;
0035: import java.net.ServerSocket;
0036: import java.net.UnknownHostException;
0037: import java.util.Enumeration;
0038: import java.util.Hashtable;
0039: import java.util.Vector;
0040: import javax.media.jai.OperationRegistry;
0041: import javax.media.jai.remote.SerializableState;
0042: import javax.media.jai.remote.SerializerFactory;
0043: import javax.media.jai.remote.SerializableRenderedImage;
0044: import javax.media.jai.tilecodec.TileCodecParameterList;
0045: import javax.media.jai.tilecodec.TileDecoder;
0046: import javax.media.jai.tilecodec.TileEncoder;
0047: import javax.media.jai.tilecodec.TileDecoderFactory;
0048: import javax.media.jai.tilecodec.TileEncoderFactory;
0049: import javax.media.jai.util.CaselessStringKey;
0050:
0051: /**
0052: * A serializable wrapper class for classes which implement the
0053: * <code>RenderableImage</code> interface.
0054: *
0055: * <p> A <code>SerializableRenderableImage</code> provides a means to
0056: * serialize a <code>RenderableImage</code>. Transient fields are handled
0057: * using <code>Serializer</code>s registered with the
0058: * <code>SerializerFactory</code>. Since no data is associated with a
0059: * <code>RenderableImage</code>, <code>SerializableRenderableImage</code>
0060: * does not provide any renderable image data. The only way to access image
0061: * data from a <code>SerializableRenderableImage</code> is by calling any
0062: * one of the <code>createDefaultRendering</code>,
0063: * <code>createRendering</code>, or <code>createScaledRendering</code>
0064: * methods. The resultant <code>RenderedImage</code> is created on the remote
0065: * host and provided via deep copy of the image data. If the request is
0066: * made on the local host, the image data are provided by forwarding
0067: * the request to the wrapped <code>RenderableImage</code>. Note that a single
0068: * <code>SerializableRenderableImage</code> object should be able to service
0069: * multiple remote hosts.
0070: *
0071: * <p> An example of the usage of this class is as follows:
0072: *
0073: * <pre>
0074: * import java.io.IOException;
0075: * import java.io.ObjectInputStream;
0076: * import java.io.ObjectOutputStream;
0077: * import java.io.Serializable;
0078: *
0079: * public class SomeSerializableClass implements Serializable {
0080: * protected transient RenderableImage image;
0081: *
0082: * // Fields omitted.
0083: *
0084: * public SomeSerializableClass(RenderableImage image) {
0085: * this.image = image;
0086: * }
0087: *
0088: * // Methods omitted.
0089: *
0090: * // Serialization method.
0091: * private void writeObject(ObjectOutputStream out) throws IOException {
0092: * out.defaultWriteObject();
0093: * out.writeObject(new SerializableRenderableImage(image));
0094: * }
0095: *
0096: * // Deserialization method.
0097: * private void readObject(ObjectInputStream in)
0098: * throws IOException, ClassNotFoundException {
0099: * in.defaultReadObject();
0100: * image = (RenderableImage)in.readObject();
0101: * }
0102: * }
0103: * </pre>
0104: *
0105: * @see java.awt.image.renderable.RenderableImage
0106: * @see javax.media.jai.RenderableOp
0107: */
0108: public final class SerializableRenderableImage implements
0109: RenderableImage, Serializable {
0110:
0111: /** Value to indicate the server socket timeout period (milliseconds). */
0112: private static final int SERVER_TIMEOUT = 60000; // XXX 1 minute?
0113:
0114: /** Message indicating that a client will not connect again. */
0115: private static final String CLOSE_MESSAGE = "CLOSE";
0116:
0117: /** Flag indicating whether this is a data server. */
0118: private transient boolean isServer;
0119:
0120: /** The RenderableImage source of this object (server only). */
0121: private transient RenderableImage source;
0122:
0123: /** The X coordinate of the image's upper-left pixel. */
0124: private float minX;
0125:
0126: /** The Y coordinate of the image's upper-left pixel. */
0127: private float minY;
0128:
0129: /** The image's width in pixels. */
0130: private float width;
0131:
0132: /** The image's height in pixels. */
0133: private float height;
0134:
0135: /** The image's sources, stored in a Vector. */
0136: private transient Vector sources = null;
0137:
0138: /** A Hashtable containing the image properties. */
0139: private transient Hashtable properties = null;
0140:
0141: /** */
0142: private boolean isDynamic;
0143:
0144: /** The Internet Protocol (IP) address of the instantiating host. */
0145: private InetAddress host;
0146:
0147: /** The port on which the data server is listening. */
0148: private int port;
0149:
0150: /** Flag indicating that the server is available for connections. */
0151: private transient boolean serverOpen = false;
0152:
0153: /** The server socket for image data transfer (server only). */
0154: private transient ServerSocket serverSocket = null;
0155:
0156: /** The thread in which the data server is running (server only). */
0157: private transient Thread serverThread;
0158:
0159: /**
0160: * A table of counts of remote references to instances of this class
0161: * (server only).
0162: *
0163: * <p> This table consists of entries with the keys being instances of
0164: * <code>SerializableRenderableImage</code> and the values being
0165: * <code>Integer</code>s the int value of which represents the number
0166: * of remote <code>SerializableRenderableImage</code> objects which could
0167: * potentially request a socket connection with the associated key. This
0168: * table is necessary to prevent the garbage collector of the interpreter
0169: * in which the server <code>SerializableRenderableImage</code> object is
0170: * instantiated from finalizing the object - and thereby closing its
0171: * server socket - when that object could still receive socket connection
0172: * requests from its remote clients. The reference to the object in the
0173: * static class variable ensures that the object will not be prematurely
0174: * finalized.
0175: */
0176: private static transient Hashtable remoteReferenceCount;
0177:
0178: /** Indicate that tilecodec is used in the transfering or not */
0179: private boolean useTileCodec = false;
0180:
0181: /**
0182: * The <code>OperationRegistry</code> to be used to find the
0183: * <code>TileEncoderFactory</code> and <code>TileDecoderFactory</code>
0184: */
0185: private OperationRegistry registry = null;
0186:
0187: /** The name of the TileCodec format. */
0188: private String formatName = null;
0189:
0190: /** Cache the encoding/decoding parameters */
0191: private TileCodecParameterList encodingParam = null;
0192: private TileCodecParameterList decodingParam = null;
0193:
0194: /**
0195: * Increment the remote reference count of the argument.
0196: *
0197: * <p> If the argument is not already in the remote reference table,
0198: * add it to the table with a count value of unity. If it exists in
0199: * table, increment its count value.
0200: *
0201: * @parameter o The object the count value of which is to be incremented.
0202: */
0203: private static synchronized void incrementRemoteReferenceCount(
0204: Object o) {
0205: if (remoteReferenceCount == null) {
0206: remoteReferenceCount = new Hashtable();
0207: remoteReferenceCount.put(o, new Integer(1));
0208: } else {
0209: Integer count = (Integer) remoteReferenceCount.get(o);
0210: if (count == null) {
0211: remoteReferenceCount.put(o, new Integer(1));
0212: } else {
0213: remoteReferenceCount.put(o, new Integer(count
0214: .intValue() + 1));
0215: }
0216: }
0217: }
0218:
0219: /**
0220: * Decrement the remote reference count of the argument.
0221: *
0222: * <p> If the count value of the argument exists in the table its count
0223: * value is decremented unless the count value is unity in which case the
0224: * entry is removed from the table.
0225: *
0226: * @parameter o The object the count value of which is to be decremented.
0227: */
0228: private static synchronized void decrementRemoteReferenceCount(
0229: Object o) {
0230: if (remoteReferenceCount != null) {
0231: Integer count = (Integer) remoteReferenceCount.get(o);
0232: if (count != null) {
0233: if (count.intValue() == 1) {
0234: remoteReferenceCount.remove(o);
0235: } else {
0236: remoteReferenceCount.put(o, new Integer(count
0237: .intValue() - 1));
0238: }
0239: }
0240: }
0241: }
0242:
0243: /**
0244: * The default constructor.
0245: */
0246: SerializableRenderableImage() {
0247: }
0248:
0249: /**
0250: * Constructs a <code>SerializableRenderableImage</code> wrapper for a
0251: * <code>RenderableImage</code> source. The image data of the rendering
0252: * will be serialized via a single deep copy. Tile encoding and
0253: * decoding may be effected via a <code>TileEncoder</code> and
0254: * <code>TileDecoder</code> specified by format name.
0255: *
0256: * @param source The <code>RenderableImage</code> source.
0257: * @param registry The <code>OperationRegistry</code> to use in
0258: * creating the <code>TileEncoder</code>. The
0259: * <code>TileDecoder</code> will of necessity be
0260: * created using the default <code>OperationRegistry</code>
0261: * as the specified <code>OperationRegistry</code> is not
0262: * serialized. If <code>null</code> the default registry
0263: * will be used.
0264: * @param formatName The name of the format used to encode the data.
0265: * If <code>null</code> simple tile serialization will
0266: * be performed either directly or by use of a "raw"
0267: * <code>TileCodec</code>.
0268: * @param encodingParam The parameters to be used for data encoding. If
0269: * <code>null</code> the default encoding
0270: * <code>TileCodecParameterList</code> for this
0271: * format will be used. Ignored if
0272: * <code>formatName</code> is <code>null</code>.
0273: * @param decodingParam The parameters to be used for data decoding. If
0274: * <code>null</code> a complementary
0275: * <code>TileCodecParameterList</code> will be
0276: * derived from <code>encodingParam</code>. Ignored
0277: * if <code>formatName</code> is <code>null</code>.
0278: *
0279: * @exception IllegalArgumentException if <code>source</code>
0280: * or <code>formatName</code> is <code>null</code>.
0281: * @exception IllegalArgumentException if <code>encodingParam</code> and
0282: * <code>decodingParam</code> do not have the same format name as the
0283: * supplied <code>formatName</code>.
0284: */
0285: public SerializableRenderableImage(RenderableImage source,
0286: OperationRegistry registry, String formatName,
0287: TileCodecParameterList encodingParam,
0288: TileCodecParameterList decodingParam) {
0289:
0290: this (source);
0291:
0292: this .registry = registry;
0293: this .formatName = formatName;
0294: this .encodingParam = encodingParam;
0295: this .decodingParam = decodingParam;
0296:
0297: if (formatName == null) {
0298: throw new IllegalArgumentException(JaiI18N
0299: .getString("SerializableRenderableImage2"));
0300: }
0301:
0302: if (!formatName.equals(encodingParam.getFormatName())) {
0303: throw new IllegalArgumentException(JaiI18N
0304: .getString("UseTileCodec0"));
0305: }
0306:
0307: if (!formatName.equals(decodingParam.getFormatName())) {
0308: throw new IllegalArgumentException(JaiI18N
0309: .getString("UseTileCodec1"));
0310: }
0311:
0312: TileEncoderFactory tileEncoderFactory = (TileEncoderFactory) registry
0313: .getFactory("tileEncoder", formatName);
0314: TileDecoderFactory tileDecoderFactory = (TileDecoderFactory) registry
0315: .getFactory("tileDecoder", formatName);
0316: if (tileEncoderFactory == null || tileDecoderFactory == null)
0317: throw new RuntimeException(JaiI18N
0318: .getString("UseTileCodec2"));
0319:
0320: useTileCodec = true;
0321: }
0322:
0323: /**
0324: * Constructs a <code>SerializableRenderableImage</code> wrapper for a
0325: * <code>RenderableImage</code> source. Image data of the rendering of
0326: * the <code>RenderableImage</code> will be serialized via a single deep
0327: * copy. No <code>TileCodec</code> will be used, i.e., data will be
0328: * transmitted using the serialization protocol for <code>Raster</code>s.
0329: *
0330: * @param source The <code>RenderableImage</code> source.
0331: * @exception IllegalArgumentException if <code>source</code>
0332: * or <code>formatName</code> is <code>null</code>.
0333: */
0334: public SerializableRenderableImage(RenderableImage source) {
0335:
0336: if (source == null)
0337: throw new IllegalArgumentException(JaiI18N
0338: .getString("SerializableRenderableImage1"));
0339:
0340: // Set server flag.
0341: isServer = true;
0342:
0343: // Cache the parameter.
0344: this .source = source;
0345:
0346: // Initialize RenderableImage fields.
0347: minX = source.getMinX();
0348: minY = source.getMinY();
0349: width = source.getWidth();
0350: height = source.getHeight();
0351: isDynamic = source.isDynamic();
0352:
0353: sources = new Vector();
0354: sources.add(source);
0355:
0356: properties = new Hashtable();
0357: String[] propertyNames = source.getPropertyNames();
0358: String propertyName;
0359: if (propertyNames != null) {
0360: for (int i = 0; i < propertyNames.length; i++) {
0361: propertyName = propertyNames[i];
0362: properties.put(new CaselessStringKey(propertyName),
0363: source.getProperty(propertyName));
0364: }
0365: }
0366:
0367: // Initialize the host field.
0368: try {
0369: host = InetAddress.getLocalHost();
0370: } catch (UnknownHostException e) {
0371: throw new RuntimeException(e.getMessage());
0372: }
0373:
0374: // Unset the server availability flag.
0375: serverOpen = false;
0376: }
0377:
0378: /**
0379: * Private implementation of tile server.
0380: */
0381: private class RenderingServer implements Runnable {
0382:
0383: /**
0384: * Provide Rasters to clients on request.
0385: *
0386: * <p> This method is called by the data server thread when a deep copy
0387: * of the source image Raster is not being used. A socket connection is
0388: * set up at a well known address to which clients may connect. After a
0389: * client connects it transmits a Rectangle object which is read by
0390: * this method. The Raster corresponding to this Rectangle is then
0391: * retrieved from the source image and transmitted back over the
0392: * socket connection.
0393: *
0394: * <p> The server loop will continue until this object is garbage
0395: * collected.
0396: */
0397: public void run() {
0398: // Loop while the server availability flag is set.
0399: while (serverOpen) {
0400: // Wait for a client connection request.
0401: Socket socket = null;
0402: try {
0403: socket = serverSocket.accept();
0404: socket.setSoLinger(true, 1);
0405: } catch (InterruptedIOException e) {
0406: // accept() timeout: restart loop to check
0407: // availability flag.
0408: continue;
0409: } catch (Exception e) {
0410: throw new RuntimeException(e.getMessage());
0411: }
0412:
0413: // Get the socket input and output streams and wrap object
0414: // input and output streams around them, respectively.
0415: InputStream in = null;
0416: OutputStream out = null;
0417: ObjectInputStream objectIn = null;
0418: ObjectOutputStream objectOut = null;
0419: try {
0420: in = socket.getInputStream();
0421: out = socket.getOutputStream();
0422: objectIn = new ObjectInputStream(in);
0423: objectOut = new ObjectOutputStream(out);
0424: } catch (Exception e) {
0425: throw new RuntimeException(e.getMessage());
0426: }
0427:
0428: // Read the Object from the object stream.
0429: Object obj = null;
0430: try {
0431: obj = objectIn.readObject();
0432: } catch (Exception e) {
0433: throw new RuntimeException(e.getMessage());
0434: }
0435:
0436: RenderedImage ri = null;
0437: SerializableRenderedImage sri;
0438: // Switch according to object class; ignore unsupported types.
0439: if (obj instanceof String) {
0440: String str = (String) obj;
0441:
0442: if (str.equals(CLOSE_MESSAGE)) {
0443: // Decrement the remote reference count.
0444: decrementRemoteReferenceCount(this );
0445:
0446: } else {
0447: if (str.equals("createDefaultRendering")) {
0448:
0449: ri = source.createDefaultRendering();
0450:
0451: } else if (str.equals("createRendering")) {
0452:
0453: // Read the Object from the object stream.
0454: obj = null;
0455: try {
0456: obj = objectIn.readObject();
0457: } catch (Exception e) {
0458: throw new RuntimeException(e
0459: .getMessage());
0460: }
0461:
0462: SerializableState ss = (SerializableState) obj;
0463: RenderContext rc = (RenderContext) ss
0464: .getObject();
0465:
0466: ri = source.createRendering(rc);
0467:
0468: } else if (str.equals("createScaledRendering")) {
0469:
0470: // Read the Object from the object stream.
0471: obj = null;
0472: try {
0473: obj = objectIn.readObject();
0474: } catch (Exception e) {
0475: throw new RuntimeException(e
0476: .getMessage());
0477: }
0478:
0479: int w = ((Integer) obj).intValue();
0480:
0481: try {
0482: obj = objectIn.readObject();
0483: } catch (Exception e) {
0484: throw new RuntimeException(e
0485: .getMessage());
0486: }
0487:
0488: int h = ((Integer) obj).intValue();
0489:
0490: try {
0491: obj = objectIn.readObject();
0492: } catch (Exception e) {
0493: throw new RuntimeException(e
0494: .getMessage());
0495: }
0496:
0497: SerializableState ss = (SerializableState) obj;
0498: RenderingHints rh = (RenderingHints) ss
0499: .getObject();
0500:
0501: ri = source.createScaledRendering(w, h, rh);
0502: }
0503:
0504: if (useTileCodec) {
0505: try {
0506: sri = new SerializableRenderedImage(ri,
0507: true, registry, formatName,
0508: encodingParam, decodingParam);
0509: } catch (java.io.NotSerializableException nse) {
0510: throw new RuntimeException(nse
0511: .getMessage());
0512: }
0513: } else {
0514: sri = new SerializableRenderedImage(ri,
0515: true);
0516: }
0517:
0518: try {
0519: objectOut.writeObject(sri);
0520: } catch (Exception e) {
0521: throw new RuntimeException(e.getMessage());
0522: }
0523: }
0524: } else {
0525: throw new RuntimeException(JaiI18N
0526: .getString("SerializableRenderableImage0"));
0527: }
0528:
0529: // XXX Concerning serialization of properties, perhaps the
0530: // best approach would be to serialize all the properties up
0531: // front if a deep copy were being made but otherwise to wait
0532: // until the first property request was received before
0533: // transmitting any property values. When the first request
0534: // was made, all property values would be transmitted and then
0535: // cached. Up front serialization might in both cases include
0536: // transmitting all names. If property serialization were
0537: // deferred, then a new message branch would be added here
0538: // to retrieve the properties which could be obtained as
0539: // a PropertySourceImpl. If properties are also served up
0540: // then this inner class should be renamed "DataServer".
0541:
0542: // Close the various streams and the socket itself.
0543: try {
0544: objectOut.close();
0545: objectIn.close();
0546: out.close();
0547: in.close();
0548: socket.close();
0549: } catch (Exception e) {
0550: throw new RuntimeException(e.getMessage());
0551: }
0552: }
0553: }
0554: }
0555:
0556: // --- Begin implementation of java.awt.image.RenderableImage. ---
0557:
0558: /**
0559: * Returns a <code>RenderedImage</code> which is the result of
0560: * calling <code>createDefaultRendering</code> on the wrapped
0561: * <code>RenderableImage</code>.
0562: */
0563: public RenderedImage createDefaultRendering() {
0564:
0565: if (isServer) {
0566: return source.createDefaultRendering();
0567: }
0568:
0569: // Connect to the data server.
0570: Socket socket = connectToServer();
0571:
0572: // Get the socket input and output streams and wrap object
0573: // input and output streams around them, respectively.
0574: OutputStream out = null;
0575: ObjectOutputStream objectOut = null;
0576: InputStream in = null;
0577: ObjectInputStream objectIn = null;
0578: try {
0579: out = socket.getOutputStream();
0580: objectOut = new ObjectOutputStream(out);
0581: in = socket.getInputStream();
0582: objectIn = new ObjectInputStream(in);
0583: } catch (Exception e) {
0584: throw new RuntimeException(e.getMessage());
0585: }
0586:
0587: // Write the name of the method to the object output stream.
0588: try {
0589: objectOut.writeObject("createDefaultRendering");
0590: } catch (Exception e) {
0591: throw new RuntimeException(e.getMessage());
0592: }
0593:
0594: // Read serialized form of the RenderedImage from object output stream.
0595: Object object = null;
0596: try {
0597: object = objectIn.readObject();
0598: } catch (Exception e) {
0599: throw new RuntimeException(e.getMessage());
0600: }
0601:
0602: RenderedImage ri;
0603: if (object instanceof SerializableRenderedImage) {
0604: ri = (RenderedImage) object;
0605: } else {
0606: ri = null;
0607: }
0608:
0609: // Close the various streams and the socket.
0610: try {
0611: out.close();
0612: objectOut.close();
0613: in.close();
0614: objectIn.close();
0615: socket.close();
0616: } catch (Exception e) {
0617: throw new RuntimeException(e.getMessage());
0618: }
0619:
0620: return ri;
0621: }
0622:
0623: public RenderedImage createRendering(RenderContext renderContext) {
0624:
0625: if (isServer) {
0626: return source.createRendering(renderContext);
0627: }
0628:
0629: // Connect to the data server.
0630: Socket socket = connectToServer();
0631:
0632: // Get the socket input and output streams and wrap object
0633: // input and output streams around them, respectively.
0634: OutputStream out = null;
0635: ObjectOutputStream objectOut = null;
0636: InputStream in = null;
0637: ObjectInputStream objectIn = null;
0638: try {
0639: out = socket.getOutputStream();
0640: objectOut = new ObjectOutputStream(out);
0641: in = socket.getInputStream();
0642: objectIn = new ObjectInputStream(in);
0643: } catch (Exception e) {
0644: throw new RuntimeException(e.getMessage());
0645: }
0646:
0647: // Write the name of the method and the RenderContext to the
0648: // object output stream.
0649: try {
0650: objectOut.writeObject("createRendering");
0651: objectOut.writeObject(SerializerFactory.getState(
0652: renderContext, null));
0653: } catch (Exception e) {
0654: throw new RuntimeException(e.getMessage());
0655: }
0656:
0657: // Read serialized form of the RenderedImage from object output stream.
0658: Object object = null;
0659: try {
0660: object = objectIn.readObject();
0661: } catch (Exception e) {
0662: throw new RuntimeException(e.getMessage());
0663: }
0664:
0665: RenderedImage ri = (RenderedImage) object;
0666:
0667: // Close the various streams and the socket.
0668: try {
0669: out.close();
0670: objectOut.close();
0671: in.close();
0672: objectIn.close();
0673: socket.close();
0674: } catch (Exception e) {
0675: throw new RuntimeException(e.getMessage());
0676: }
0677:
0678: return ri;
0679: }
0680:
0681: public RenderedImage createScaledRendering(int w, int h,
0682: RenderingHints hints) {
0683:
0684: if (isServer) {
0685: return source.createScaledRendering(w, h, hints);
0686: }
0687:
0688: // Connect to the data server.
0689: Socket socket = connectToServer();
0690:
0691: // Get the socket input and output streams and wrap object
0692: // input and output streams around them, respectively.
0693: OutputStream out = null;
0694: ObjectOutputStream objectOut = null;
0695: InputStream in = null;
0696: ObjectInputStream objectIn = null;
0697: try {
0698: out = socket.getOutputStream();
0699: objectOut = new ObjectOutputStream(out);
0700: in = socket.getInputStream();
0701: objectIn = new ObjectInputStream(in);
0702: } catch (Exception e) {
0703: throw new RuntimeException(e.getMessage());
0704: }
0705:
0706: // Write the name of the method and the necessary method argument
0707: // to the object output stream.
0708: try {
0709: objectOut.writeObject("createScaledRendering");
0710: objectOut.writeObject(new Integer(w));
0711: objectOut.writeObject(new Integer(h));
0712: objectOut.writeObject(SerializerFactory.getState(hints,
0713: null));
0714: } catch (Exception e) {
0715: throw new RuntimeException(e.getMessage());
0716: }
0717:
0718: // Read serialized form of the RenderedImage from object output stream.
0719: Object object = null;
0720: try {
0721: object = objectIn.readObject();
0722: } catch (Exception e) {
0723: throw new RuntimeException(e.getMessage());
0724: }
0725:
0726: RenderedImage ri = (RenderedImage) object;
0727:
0728: // Close the various streams and the socket.
0729: try {
0730: out.close();
0731: objectOut.close();
0732: in.close();
0733: objectIn.close();
0734: socket.close();
0735: } catch (Exception e) {
0736: throw new RuntimeException(e.getMessage());
0737: }
0738:
0739: return ri;
0740: }
0741:
0742: public float getHeight() {
0743: return height;
0744: }
0745:
0746: public float getMinX() {
0747: return minX;
0748: }
0749:
0750: public float getMinY() {
0751: return minY;
0752: }
0753:
0754: // XXX Should getProperty() request property values over a socket
0755: // connection also?
0756: public Object getProperty(String name) {
0757: Object property = properties.get(new CaselessStringKey(name));
0758: return property == null ? Image.UndefinedProperty : property;
0759: }
0760:
0761: public String[] getPropertyNames() {
0762: String[] names = null;
0763: if (!properties.isEmpty()) {
0764: names = new String[properties.size()];
0765: Enumeration keys = properties.keys();
0766: int index = 0;
0767: CaselessStringKey key;
0768: while (keys.hasMoreElements()) {
0769: key = (CaselessStringKey) keys.nextElement();
0770: names[index++] = key.getName();
0771: }
0772: }
0773: return names;
0774: }
0775:
0776: /**
0777: * If this <code>SerializableRenderableImage</code> has not been
0778: * serialized, this method returns a <code>Vector</code> containing
0779: * only the <code>RenderableImage</code> passed to the constructor; if
0780: * this image has been deserialized, it returns <code>null</code>.
0781: */
0782: public Vector getSources() {
0783: return sources;
0784: }
0785:
0786: /**
0787: *
0788: */
0789: public boolean isDynamic() {
0790: return isDynamic;
0791: }
0792:
0793: public float getWidth() {
0794: return width;
0795: }
0796:
0797: // --- End implementation of java.awt.image.RenderableImage. ---
0798:
0799: /**
0800: * Create a server socket and start the server in a separate thread.
0801: *
0802: * <p> Note that this method should be called only the first time this
0803: * object is serialized and only if a deep copy is not being used. If
0804: * a deep copy is used there is no need to serve clients data on demand.
0805: * However if data service is being provided, there is no need to create
0806: * multiple threads for the single object as a single server thread
0807: * should be able to service multiple remote objects.
0808: */
0809: private synchronized void openServer() throws IOException,
0810: SocketException {
0811: if (!serverOpen) {
0812: // Create a ServerSocket.
0813: serverSocket = new ServerSocket(0);
0814:
0815: // Set the ServerSocket accept() method timeout period.
0816: serverSocket.setSoTimeout(SERVER_TIMEOUT);
0817:
0818: // Initialize the port field.
0819: port = serverSocket.getLocalPort();
0820:
0821: // Set the server availability flag.
0822: serverOpen = true;
0823:
0824: // Spawn a child thread and return the parent thread to the caller.
0825: serverThread = new Thread(new RenderingServer());
0826: serverThread.start();
0827:
0828: // Increment the remote reference count.
0829: incrementRemoteReferenceCount(this );
0830: }
0831: }
0832:
0833: /**
0834: * Transmit a message to the data server to indicate that the client
0835: * will no longer request socket connections.
0836: */
0837: private void closeClient() {
0838: // Connect to the data server.
0839: Socket socket = connectToServer();
0840:
0841: // Get the socket output stream and wrap an object
0842: // output stream around it.
0843: OutputStream out = null;
0844: ObjectOutputStream objectOut = null;
0845: try {
0846: out = socket.getOutputStream();
0847: objectOut = new ObjectOutputStream(out);
0848: } catch (Exception e) {
0849: throw new RuntimeException(e.getMessage());
0850: }
0851:
0852: // Write CLOSE_MESSAGE to the object output stream.
0853: try {
0854: objectOut.writeObject(CLOSE_MESSAGE);
0855: } catch (Exception e) {
0856: throw new RuntimeException(e.getMessage());
0857: }
0858:
0859: // Close the streams and the socket.
0860: try {
0861: out.close();
0862: objectOut.close();
0863: socket.close();
0864: } catch (Exception e) {
0865: throw new RuntimeException(e.getMessage());
0866: }
0867: }
0868:
0869: /**
0870: * Obtain a connection to the data server socket. This is used only if a
0871: * deep copy of the image Raster has not been made.
0872: */
0873: private Socket connectToServer() {
0874: // Open a connection to the data server.
0875: Socket socket = null;
0876: try {
0877: socket = new Socket(host, port);
0878: socket.setSoLinger(true, 1);
0879: } catch (Exception e) {
0880: throw new RuntimeException(e.getMessage());
0881: }
0882:
0883: return socket;
0884: }
0885:
0886: /**
0887: * If a deep copy is not being used, unset the data server availability
0888: * flag and wait for the server thread to rejoin the current thread.
0889: */
0890: protected void finalize() throws Throwable {
0891: dispose();
0892:
0893: // Forward to the parent class.
0894: super .finalize();
0895: }
0896:
0897: /**
0898: * Provides a hint that an image will no longer be accessed from a
0899: * reference in user space. The results are equivalent to those
0900: * that occur when the program loses its last reference to this
0901: * image, the garbage collector discovers this, and finalize is
0902: * called. This can be used as a hint in situations where waiting
0903: * for garbage collection would be overly conservative, e.g., there
0904: * are a large number of socket connections which may be opened to
0905: * transmit tile data.
0906: *
0907: * <p> <code>SerializableRenderableImage</code> defines this method to
0908: * behave as follows:
0909: * <ul>
0910: * <li>if the image is acting as a server, i.e., has never been
0911: * serialized and may be providing data to serialized
0912: * versions of itself, it makes itself unavailable to further
0913: * client requests and closes its socket;</li>
0914: * <li>if the image is acting as a client, i.e., has been serialized
0915: * and may be requesting data from a remote, pre-serialization version
0916: * of itself, it sends a message to its remote self indicating that it
0917: * will no longer be making requests.</li>
0918: * </ul>
0919: *
0920: * <p> The results of referencing an image after a call to
0921: * <code>dispose()</code> are undefined.
0922: */
0923: public void dispose() {
0924: // Rejoin the server thread if using a socket-based server.
0925: if (isServer) {
0926: if (serverOpen) {
0927: // Unset availability flag so server loop exits.
0928: serverOpen = false;
0929:
0930: // Wait for the server (child) thread to die.
0931: try {
0932: serverThread.join(2 * SERVER_TIMEOUT);
0933: } catch (Exception e) {
0934: // Ignore the Exception.
0935: }
0936:
0937: // Close the server socket.
0938: try {
0939: serverSocket.close();
0940: } catch (Exception e) {
0941: // Ignore the Exception.
0942: }
0943: }
0944: } else { // client
0945: // Transmit a message to the server to indicate the child's exit.
0946: closeClient();
0947: }
0948: }
0949:
0950: /**
0951: * Custom serialization method. In addition to all non-transient fields,
0952: * the SampleModel, source vector, and properties table are serialized.
0953: * If a deep copy of the source image Raster is being used this is also
0954: * serialized.
0955: */
0956: private void writeObject(ObjectOutputStream out) throws IOException {
0957:
0958: // Start the data server.
0959: try {
0960: openServer();
0961: } catch (Exception e1) {
0962: if (e1 instanceof SocketException) { // setSoTimeout() failed.
0963: if (serverSocket != null) { // XXX Facultative
0964: try {
0965: serverSocket.close();
0966: } catch (IOException e2) {
0967: // Ignore the exception.
0968: }
0969: }
0970: }
0971:
0972: // Since server socket creation failed, use a deep copy.
0973: serverOpen = false; // XXX Facultative
0974: }
0975:
0976: // Write non-static and non-transient fields.
0977: out.defaultWriteObject();
0978:
0979: // Remove non-serializable elements from table of properties.
0980: Hashtable propertyTable = properties;
0981: boolean propertiesCloned = false;
0982: Enumeration keys = propertyTable.keys();
0983: while (keys.hasMoreElements()) {
0984: Object key = keys.nextElement();
0985: if (!(properties.get(key) instanceof Serializable)) {
0986: if (!propertiesCloned) {
0987: propertyTable = (Hashtable) properties.clone();
0988: propertiesCloned = true;
0989: }
0990: propertyTable.remove(key);
0991: }
0992: }
0993:
0994: // Write the properties table.
0995: out.writeObject(propertyTable);
0996: }
0997:
0998: /**
0999: * Custom deserialization method. In addition to all non-transient fields,
1000: * the SampleModel, source vector, and properties table are deserialized.
1001: * If a deep copy of the source image Raster is being used this is also
1002: * deserialized.
1003: */
1004: private void readObject(ObjectInputStream in) throws IOException,
1005: ClassNotFoundException {
1006: isServer = false;
1007: source = null;
1008: serverOpen = false;
1009: serverSocket = null;
1010: serverThread = null;
1011:
1012: // Read non-static and non-transient fields.
1013: in.defaultReadObject();
1014:
1015: // Read the properties table.
1016: properties = (Hashtable) in.readObject();
1017: }
1018: }
|