Package com.lightcrafts.media.jai.rmi

Source Code of com.lightcrafts.media.jai.rmi.SerializableRenderableImage

/*
* $RCSfile: SerializableRenderableImage.java,v $
*
* Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
*
* Use is subject to license terms.
*
* $Revision: 1.1 $
* $Date: 2005/02/11 04:56:54 $
* $State: Exp $
*/package com.lightcrafts.media.jai.rmi;

/*
XXX
See if the SerializableRenderedImage can be sent by requests instead of
deep copy.
*/

import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.RenderableImage;
import java.awt.image.renderable.RenderContext;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.ServerSocket;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import com.lightcrafts.mediax.jai.OperationRegistry;
import com.lightcrafts.mediax.jai.remote.SerializableState;
import com.lightcrafts.mediax.jai.remote.SerializerFactory;
import com.lightcrafts.mediax.jai.remote.SerializableRenderedImage;
import com.lightcrafts.mediax.jai.tilecodec.TileCodecParameterList;
import com.lightcrafts.mediax.jai.tilecodec.TileDecoderFactory;
import com.lightcrafts.mediax.jai.tilecodec.TileEncoderFactory;
import com.lightcrafts.mediax.jai.util.CaselessStringKey;

/**
* A serializable wrapper class for classes which implement the
* <code>RenderableImage</code> interface.
*
* <p> A <code>SerializableRenderableImage</code> provides a means to
* serialize a <code>RenderableImage</code>.  Transient fields are handled
* using <code>Serializer</code>s registered with the
* <code>SerializerFactory</code>. Since no data is associated with a
* <code>RenderableImage</code>, <code>SerializableRenderableImage</code>
* does not provide any renderable image data. The only way to access image
* data from a <code>SerializableRenderableImage</code> is by calling any
* one of the <code>createDefaultRendering</code>,
* <code>createRendering</code>, or <code>createScaledRendering</code>
* methods. The resultant <code>RenderedImage</code> is created on the remote
* host and provided via deep copy of the image data. If the request is
* made on the local host, the image data are provided by forwarding
* the request to the wrapped <code>RenderableImage</code>.  Note that a single
* <code>SerializableRenderableImage</code> object should be able to service
* multiple remote hosts.
*
* <p> An example of the usage of this class is as follows:
*
* <pre>
* import java.io.IOException;
* import java.io.ObjectInputStream;
* import java.io.ObjectOutputStream;
* import java.io.Serializable;
*
* public class SomeSerializableClass implements Serializable {
*     protected transient RenderableImage image;
*
*     // Fields omitted.
*
*     public SomeSerializableClass(RenderableImage image) {
*         this.image = image;
*     }
*
*     // Methods omitted.
*
*     // Serialization method.
*     private void writeObject(ObjectOutputStream out) throws IOException {
*         out.defaultWriteObject();
*         out.writeObject(new SerializableRenderableImage(image));
*     }
*
*     // Deserialization method.
*     private void readObject(ObjectInputStream in)
*         throws IOException, ClassNotFoundException {
*         in.defaultReadObject();
*         image = (RenderableImage)in.readObject();
*     }
* }
* </pre>
*
* @see java.awt.image.renderable.RenderableImage
* @see com.lightcrafts.mediax.jai.RenderableOp
*/
public final class SerializableRenderableImage implements RenderableImage,
    Serializable {
   
    /** Value to indicate the server socket timeout period (milliseconds). */
    private static final int SERVER_TIMEOUT = 60000; // XXX 1 minute?

    /** Message indicating that a client will not connect again. */
    private static final String CLOSE_MESSAGE = "CLOSE";

    /** Flag indicating whether this is a data server. */
    private transient boolean isServer;

    /** The RenderableImage source of this object (server only). */
    private transient RenderableImage source;

    /** The X coordinate of the image's upper-left pixel. */
    private float minX;

    /** The Y coordinate of the image's upper-left pixel. */
    private float minY;

    /** The image's width in pixels. */
    private float width;

    /** The image's height in pixels. */
    private float height;

    /** The image's sources, stored in a Vector. */
    private transient Vector sources = null;

    /** A Hashtable containing the image properties. */
    private transient Hashtable properties = null;

    /** */
    private boolean isDynamic;

    /** The Internet Protocol (IP) address of the instantiating host. */
    private InetAddress host;

    /** The port on which the data server is listening. */
    private int port;

    /** Flag indicating that the server is available for connections. */
    private transient boolean serverOpen = false;

    /** The server socket for image data transfer (server only). */
    private transient ServerSocket serverSocket = null;

    /** The thread in which the data server is running (server only). */
    private transient Thread serverThread;

    /**
     * A table of counts of remote references to instances of this class
     * (server only).
     *
     * <p> This table consists of entries with the keys being instances of
     * <code>SerializableRenderableImage</code> and the values being
     * <code>Integer</code>s the int value of which represents the number
     * of remote <code>SerializableRenderableImage</code> objects which could
     * potentially request a socket connection with the associated key. This
     * table is necessary to prevent the garbage collector of the interpreter
     * in which the server <code>SerializableRenderableImage</code> object is
     * instantiated from finalizing the object - and thereby closing its
     * server socket - when that object could still receive socket connection
     * requests from its remote clients. The reference to the object in the
     * static class variable ensures that the object will not be prematurely
     * finalized.
     */
    private static transient Hashtable remoteReferenceCount;

    /** Indicate that tilecodec is used in the transfering or not */
    private boolean useTileCodec = false;

    /**
     * The <code>OperationRegistry</code> to be used to find the
     * <code>TileEncoderFactory</code> and <code>TileDecoderFactory</code>
     */
    private OperationRegistry registry = null;

    /** The name of the TileCodec format. */
    private String formatName = null;

    /** Cache the encoding/decoding parameters */
    private TileCodecParameterList encodingParam = null;
    private TileCodecParameterList decodingParam = null;

    /**
     * Increment the remote reference count of the argument.
     *
     * <p> If the argument is not already in the remote reference table,
     * add it to the table with a count value of unity. If it exists in
     * table, increment its count value.
     *
     * @parameter o The object the count value of which is to be incremented.
     */
    private static synchronized void incrementRemoteReferenceCount(Object o) {
        if (remoteReferenceCount == null) {
            remoteReferenceCount = new Hashtable();
            remoteReferenceCount.put(o, new Integer(1));
        } else {
            Integer count = (Integer)remoteReferenceCount.get(o);
            if (count == null) {
                remoteReferenceCount.put(o, new Integer(1));
            } else {
                remoteReferenceCount.put(o,
                                         new Integer(count.intValue()+1));
            }
        }
    }

    /**
     * Decrement the remote reference count of the argument.
     *
     * <p> If the count value of the argument exists in the table its count
     * value is decremented unless the count value is unity in which case the
     * entry is removed from the table.
     *
     * @parameter o The object the count value of which is to be decremented.
     */
    private static synchronized void decrementRemoteReferenceCount(Object o) {
        if (remoteReferenceCount != null) {
            Integer count = (Integer)remoteReferenceCount.get(o);
            if (count != null) {
                if (count.intValue() == 1) {
                    remoteReferenceCount.remove(o);
                } else {
                    remoteReferenceCount.put(o,
                                             new Integer(count.intValue()-1));
                }
            }
        }
    }

    /**
     * The default constructor.
     */
    SerializableRenderableImage() {}

    /**
     * Constructs a <code>SerializableRenderableImage</code> wrapper for a
     * <code>RenderableImage</code> source.  The image data of the rendering
     * will be serialized via a single deep copy.  Tile encoding and
     * decoding may be effected via a <code>TileEncoder</code> and
     * <code>TileDecoder</code> specified by format name.
     *
     * @param source The <code>RenderableImage</code> source.
     * @param registry The <code>OperationRegistry</code> to use in
     *                 creating the <code>TileEncoder</code>.  The
     *                 <code>TileDecoder</code> will of necessity be
     *                 created using the default <code>OperationRegistry</code>
     *                 as the specified <code>OperationRegistry</code> is not
     *                 serialized.  If <code>null</code> the default registry
     *                 will be used.
     * @param formatName The name of the format used to encode the data.
     *                   If <code>null</code> simple tile serialization will
     *                   be performed either directly or by use of a "raw"
     *                   <code>TileCodec</code>.
     * @param encodingParam The parameters to be used for data encoding.  If
     *                      <code>null</code> the default encoding
     *                      <code>TileCodecParameterList</code> for this
     *                      format will be used.  Ignored if
     *                      <code>formatName</code> is <code>null</code>.
     * @param decodingParam The parameters to be used for data decoding.  If
     *                      <code>null</code> a complementary
     *                      <code>TileCodecParameterList</code> will be
     *                      derived from <code>encodingParam</code>.  Ignored
     *                      if <code>formatName</code> is <code>null</code>.
     *
     * @exception IllegalArgumentException if <code>source</code>
     *            or <code>formatName</code> is <code>null</code>.
     * @exception IllegalArgumentException if <code>encodingParam</code> and
     * <code>decodingParam</code> do not have the same format name as the
     * supplied <code>formatName</code>.
     */
    public SerializableRenderableImage(RenderableImage source,
               OperationRegistry registry,
               String formatName,
               TileCodecParameterList encodingParam,
               TileCodecParameterList decodingParam) {

        this(source);
 
  this.registry = registry;
  this.formatName = formatName;
  this.encodingParam = encodingParam;
  this.decodingParam = decodingParam;

  if (formatName == null) {
      throw new IllegalArgumentException(
         JaiI18N.getString("SerializableRenderableImage2"));
  }

        if (!formatName.equals(encodingParam.getFormatName())) {
            throw new IllegalArgumentException(
            JaiI18N.getString("UseTileCodec0"));
        }

        if (!formatName.equals(decodingParam.getFormatName())) {
            throw new IllegalArgumentException(
            JaiI18N.getString("UseTileCodec1"));
        }

        TileEncoderFactory tileEncoderFactory =
            (TileEncoderFactory)registry.getFactory("tileEncoder", formatName);
        TileDecoderFactory tileDecoderFactory =
            (TileDecoderFactory)registry.getFactory("tileDecoder", formatName);
        if (tileEncoderFactory == null || tileDecoderFactory == null)
            throw new RuntimeException(JaiI18N.getString("UseTileCodec2"));

        useTileCodec = true;
    }

    /**
     * Constructs a <code>SerializableRenderableImage</code> wrapper for a
     * <code>RenderableImage</code> source.  Image data of the rendering of
     * the <code>RenderableImage</code> will be serialized via a single deep
     * copy.  No <code>TileCodec</code> will be used, i.e., data will be
     * transmitted using the serialization protocol for <code>Raster</code>s.
     *
     * @param source The <code>RenderableImage</code> source.
     * @exception IllegalArgumentException if <code>source</code>
     *            or <code>formatName</code> is <code>null</code>.
     */
    public SerializableRenderableImage(RenderableImage source) {

  if (source == null)
      throw new IllegalArgumentException(
          JaiI18N.getString("SerializableRenderableImage1"));

        // Set server flag.
        isServer = true;

        // Cache the parameter.
        this.source = source;

        // Initialize RenderableImage fields.
        minX = source.getMinX();
        minY = source.getMinY();
        width = source.getWidth();
        height = source.getHeight();
  isDynamic = source.isDynamic();

        sources = new Vector();
        sources.add(source);

        properties = new Hashtable();
        String[] propertyNames = source.getPropertyNames();
  String propertyName;
        if (propertyNames != null) {
            for (int i = 0; i < propertyNames.length; i++) {
    propertyName = propertyNames[i];
                properties.put(new CaselessStringKey(propertyName),
                               source.getProperty(propertyName));
            }
        }

        // Initialize the host field.
        try {
            host = InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
            throw new RuntimeException(e.getMessage());
        }

        // Unset the server availability flag.
        serverOpen = false;
    }

    /**
     * Private implementation of tile server.
     */
    private class RenderingServer implements Runnable {

        /**
         * Provide Rasters to clients on request.
         *
         * <p> This method is called by the data server thread when a deep copy
         * of the source image Raster is not being used. A socket connection is
         * set up at a well known address to which clients may connect. After a
         * client connects it transmits a Rectangle object which is read by
         * this method. The Raster corresponding to this Rectangle is then
         * retrieved from the source image and transmitted back over the
         * socket connection.
         *
         * <p> The server loop will continue until this object is garbage
         * collected.
         */
        public void run() {
            // Loop while the server availability flag is set.
            while (serverOpen) {
                // Wait for a client connection request.
                Socket socket = null;
                try {
                    socket = serverSocket.accept();
        socket.setSoLinger(true,1);
                } catch (InterruptedIOException e) {
                    // accept() timeout: restart loop to check
                    // availability flag.
                    continue;
                } catch (Exception e) {
                    throw new RuntimeException(e.getMessage());
                }

                // Get the socket input and output streams and wrap object
                // input and output streams around them, respectively.
                InputStream in = null;
                OutputStream out = null;
                ObjectInputStream objectIn = null;
                ObjectOutputStream objectOut = null;
                try {
                    in = socket.getInputStream();
                    out = socket.getOutputStream();
                    objectIn = new ObjectInputStream(in);
                    objectOut = new ObjectOutputStream(out);
                } catch (Exception e) {
                    throw new RuntimeException(e.getMessage());
                }

                // Read the Object from the object stream.
                Object obj = null;
                try {
                    obj = objectIn.readObject();
                } catch (Exception e) {
                    throw new RuntimeException(e.getMessage());
                }

    RenderedImage ri = null;
    SerializableRenderedImage sri;
                // Switch according to object class; ignore unsupported types.
                if (obj instanceof String) {
        String str = (String)obj;

                    if (str.equals(CLOSE_MESSAGE)) {
      // Decrement the remote reference count.
      decrementRemoteReferenceCount(this);
     
        } else {
      if (str.equals("createDefaultRendering")) {
         
          ri = source.createDefaultRendering();
         
      } else if (str.equals("createRendering")) {
         
          // Read the Object from the object stream.
          obj = null;
          try {
        obj = objectIn.readObject();
          } catch (Exception e) {
        throw new RuntimeException(e.getMessage());
          }
         
          SerializableState ss = (SerializableState)obj;
          RenderContext rc = (RenderContext)ss.getObject();
         
          ri = source.createRendering(rc);

      } else if (str.equals("createScaledRendering")) {
         
          // Read the Object from the object stream.
          obj = null;
          try {
        obj = objectIn.readObject();
          } catch (Exception e) {
        throw new RuntimeException(e.getMessage());
          }
         
          int w = ((Integer)obj).intValue();
         
          try {
        obj = objectIn.readObject();
          } catch (Exception e) {
        throw new RuntimeException(e.getMessage());
          }
         
          int h = ((Integer)obj).intValue();
         
          try {
        obj = objectIn.readObject();
          } catch (Exception e) {
        throw new RuntimeException(e.getMessage());
          }
         
          SerializableState ss = (SerializableState)obj;
          RenderingHints rh = (RenderingHints)ss.getObject();
         
          ri = source.createScaledRendering(w, h, rh);
      }

      if (useTileCodec) {
          try {
        sri = new SerializableRenderedImage(ri,
                    true,
                    registry,
                    formatName,
                    encodingParam,
                    decodingParam);
          } catch (java.io.NotSerializableException nse) {
        throw new RuntimeException(nse.getMessage());
          }
      } else {
          sri = new SerializableRenderedImage(ri, true);
      }
     
      try {
          objectOut.writeObject(sri);
      } catch (Exception e) {
          throw new RuntimeException(e.getMessage());
      }
        }
                } else {
        throw new RuntimeException(
          JaiI18N.getString("SerializableRenderableImage0"));
    }

                // XXX Concerning serialization of properties, perhaps the
                // best approach would be to serialize all the properties up
                // front if a deep copy were being made but otherwise to wait
                // until the first property request was received before
                // transmitting any property values. When the first request
                // was made, all property values would be transmitted and then
                // cached. Up front serialization might in both cases include
                // transmitting all names. If property serialization were
                // deferred, then a new message branch would be added here
                // to retrieve the properties which could be obtained as
                // a PropertySourceImpl. If properties are also served up
                // then this inner class should be renamed "DataServer".

                // Close the various streams and the socket itself.
                try {
                    objectOut.close();
                    objectIn.close();
                    out.close();
                    in.close();
                    socket.close();
                } catch (Exception e) {
                    throw new RuntimeException(e.getMessage());
                }
            }
        }
    }

    // --- Begin implementation of java.awt.image.RenderableImage. ---

    /**
     * Returns a <code>RenderedImage</code> which is the result of
     * calling <code>createDefaultRendering</code> on the wrapped
     * <code>RenderableImage</code>.
     */
    public RenderedImage createDefaultRendering() {
 
  if (isServer) {
      return source.createDefaultRendering();
  }

  // Connect to the data server.
  Socket socket = connectToServer();
 
  // Get the socket input and output streams and wrap object
  // input and output streams around them, respectively.
  OutputStream out = null;
  ObjectOutputStream objectOut = null;
  InputStream in = null;
  ObjectInputStream objectIn = null;
  try {
      out = socket.getOutputStream();
      objectOut = new ObjectOutputStream(out);
      in = socket.getInputStream();
      objectIn = new ObjectInputStream(in);
  } catch (Exception e) {
      throw new RuntimeException(e.getMessage());
  }

  // Write the name of the method to the object output stream.
  try {
      objectOut.writeObject("createDefaultRendering");
  } catch (Exception e) {
      throw new RuntimeException(e.getMessage());
  }
 
  // Read serialized form of the RenderedImage from object output stream.
  Object object = null;
  try {
      object = objectIn.readObject();
  } catch (Exception e) {
      throw new RuntimeException(e.getMessage());
  }   

  RenderedImage ri;
  if (object instanceof SerializableRenderedImage) {
      ri = (RenderedImage)object;
  } else {
      ri = null;
  }

  // Close the various streams and the socket.
  try {
      out.close();
      objectOut.close();
      in.close();
      objectIn.close();
      socket.close();
  } catch (Exception e) {
      throw new RuntimeException(e.getMessage());
  }

  return ri;
    }


    public RenderedImage createRendering(RenderContext renderContext) {
 
  if (isServer) {
      return source.createRendering(renderContext);
  }

  // Connect to the data server.
  Socket socket = connectToServer();
 
  // Get the socket input and output streams and wrap object
  // input and output streams around them, respectively.
  OutputStream out = null;
  ObjectOutputStream objectOut = null;
  InputStream in = null;
  ObjectInputStream objectIn = null;
  try {
      out = socket.getOutputStream();
      objectOut = new ObjectOutputStream(out);
      in = socket.getInputStream();
      objectIn = new ObjectInputStream(in);
  } catch (Exception e) {
      throw new RuntimeException(e.getMessage());
  }

  // Write the name of the method and the RenderContext to the
  // object output stream.
  try {
      objectOut.writeObject("createRendering");
      objectOut.writeObject(SerializerFactory.getState(renderContext,
                   null));
  } catch (Exception e) {
      throw new RuntimeException(e.getMessage());
  }
 
  // Read serialized form of the RenderedImage from object output stream.
  Object object = null;
  try {
      object = objectIn.readObject();
  } catch (Exception e) {
      throw new RuntimeException(e.getMessage());
  }   

  RenderedImage ri = (RenderedImage)object;

  // Close the various streams and the socket.
  try {
      out.close();
      objectOut.close();
      in.close();
      objectIn.close();
      socket.close();
  } catch (Exception e) {
      throw new RuntimeException(e.getMessage());
  }

  return ri;
    }


    public RenderedImage createScaledRendering(int w, int h,
                 RenderingHints hints) {
 
  if (isServer) {
      return source.createScaledRendering(w, h, hints);
  }

  // Connect to the data server.
  Socket socket = connectToServer();
 
  // Get the socket input and output streams and wrap object
  // input and output streams around them, respectively.
  OutputStream out = null;
  ObjectOutputStream objectOut = null;
  InputStream in = null;
  ObjectInputStream objectIn = null;
  try {
      out = socket.getOutputStream();
      objectOut = new ObjectOutputStream(out);
      in = socket.getInputStream();
      objectIn = new ObjectInputStream(in);
  } catch (Exception e) {
      throw new RuntimeException(e.getMessage());
  }

  // Write the name of the method and the necessary method argument
  // to the object output stream.
  try {
      objectOut.writeObject("createScaledRendering");
      objectOut.writeObject(new Integer(w));
      objectOut.writeObject(new Integer(h));
      objectOut.writeObject(SerializerFactory.getState(hints, null));
  } catch (Exception e) {
      throw new RuntimeException(e.getMessage());
  }
 
  // Read serialized form of the RenderedImage from object output stream.
  Object object = null;
  try {
      object = objectIn.readObject();
  } catch (Exception e) {
      throw new RuntimeException(e.getMessage());
  }   

  RenderedImage ri = (RenderedImage)object;

  // Close the various streams and the socket.
  try {
      out.close();
      objectOut.close();
      in.close();
      objectIn.close();
      socket.close();
  } catch (Exception e) {
      throw new RuntimeException(e.getMessage());
  }

  return ri;
    }

    public float getHeight() {
        return height;
    }

    public float getMinX() {
        return minX;
    }

    public float getMinY() {
        return minY;
    }

    // XXX Should getProperty() request property values over a socket
    // connection also?
    public Object getProperty(String name) {
        Object property = properties.get(new CaselessStringKey(name));
  return property == null ? Image.UndefinedProperty : property;
    }

    public String[] getPropertyNames() {
  String[] names = null;
        if (!properties.isEmpty()) {
            names = new String[properties.size()];
            Enumeration keys = properties.keys();
            int index = 0;
      CaselessStringKey key;
            while (keys.hasMoreElements()) {
    key = (CaselessStringKey)keys.nextElement();
                names[index++] = key.getName();
            }
        }
        return names;
    }

    /**
     * If this <code>SerializableRenderableImage</code> has not been
     * serialized, this method returns a <code>Vector</code> containing
     * only the <code>RenderableImage</code> passed to the constructor; if
     * this image has been deserialized, it returns <code>null</code>.
     */
    public Vector getSources() {
  return sources;
    }

    /**
     *
     */
    public boolean isDynamic() {
  return isDynamic;
    }

    public float getWidth() {
  return width;
    }

    // --- End implementation of java.awt.image.RenderableImage. ---

    /**
     * Create a server socket and start the server in a separate thread.
     *
     * <p> Note that this method should be called only the first time this
     * object is serialized and only if a deep copy is not being used. If
     * a deep copy is used there is no need to serve clients data on demand.
     * However if data service is being provided, there is no need to create
     * multiple threads for the single object as a single server thread
     * should be able to service multiple remote objects.
     */
    private synchronized void openServer()
        throws IOException, SocketException {
        if (!serverOpen) {
            // Create a ServerSocket.
            serverSocket = new ServerSocket(0);

            // Set the ServerSocket accept() method timeout period.
            serverSocket.setSoTimeout(SERVER_TIMEOUT);

            // Initialize the port field.
            port = serverSocket.getLocalPort();

            // Set the server availability flag.
            serverOpen = true;

            // Spawn a child thread and return the parent thread to the caller.
            serverThread = new Thread(new RenderingServer());
            serverThread.start();

            // Increment the remote reference count.
            incrementRemoteReferenceCount(this);
        }
    }

    /**
     * Transmit a message to the data server to indicate that the client
     * will no longer request socket connections.
     */
    private void closeClient() {
        // Connect to the data server.
        Socket socket = connectToServer();

        // Get the socket output stream and wrap an object
        // output stream around it.
        OutputStream out = null;
        ObjectOutputStream objectOut = null;
        try {
            out = socket.getOutputStream();
            objectOut = new ObjectOutputStream(out);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }

        // Write CLOSE_MESSAGE to the object output stream.
        try {
            objectOut.writeObject(CLOSE_MESSAGE);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }

        // Close the streams and the socket.
        try {
            out.close();
            objectOut.close();
            socket.close();
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * Obtain a connection to the data server socket. This is used only if a
     * deep copy of the image Raster has not been made.
     */
    private Socket connectToServer() {
        // Open a connection to the data server.
        Socket socket = null;
        try {
            socket = new Socket(host, port);
      socket.setSoLinger(true,1);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }

        return socket;
    }

    /**
     * If a deep copy is not being used, unset the data server availability
     * flag and wait for the server thread to rejoin the current thread.
     */
    protected void finalize() throws Throwable {
        dispose();

        // Forward to the parent class.
        super.finalize();
    }

    /**
     * Provides a hint that an image will no longer be accessed from a
     * reference in user space.  The results are equivalent to those
     * that occur when the program loses its last reference to this
     * image, the garbage collector discovers this, and finalize is
     * called.  This can be used as a hint in situations where waiting
     * for garbage collection would be overly conservative, e.g., there
     * are a large number of socket connections which may be opened to
     * transmit tile data.
     *
     * <p> <code>SerializableRenderableImage</code> defines this method to
     * behave as follows:
     * <ul>
     * <li>if the image is acting as a server, i.e., has never been
     * serialized and may be providing data to serialized
     * versions of itself, it makes itself unavailable to further
     * client requests and closes its socket;</li>
     * <li>if the image is acting as a client, i.e., has been serialized
     * and may be requesting data from a remote, pre-serialization version
     * of itself, it sends a message to its remote self indicating that it
     * will no longer be making requests.</li>
     * </ul>
     *
     * <p> The results of referencing an image after a call to
     * <code>dispose()</code> are undefined.
     */
    public void dispose() {
        // Rejoin the server thread if using a socket-based server.
        if (isServer) {
            if (serverOpen) {
                // Unset availability flag so server loop exits.
                serverOpen = false;

                // Wait for the server (child) thread to die.
                try {
                    serverThread.join(2*SERVER_TIMEOUT);
                } catch (Exception e) {
                    // Ignore the Exception.
                }

                // Close the server socket.
                try {
                    serverSocket.close();
                } catch (Exception e) {
                    // Ignore the Exception.
                }
            }
        } else { // client
            // Transmit a message to the server to indicate the child's exit.
            closeClient();
        }
    }

    /**
     * Custom serialization method. In addition to all non-transient fields,
     * the SampleModel, source vector, and properties table are serialized.
     * If a deep copy of the source image Raster is being used this is also
     * serialized.
     */
    private void writeObject(ObjectOutputStream out) throws IOException {

  // Start the data server.
  try {
      openServer();
  } catch (Exception e1) {
      if (e1 instanceof SocketException) { // setSoTimeout() failed.
    if (serverSocket != null) { // XXX Facultative
        try {
      serverSocket.close();
        } catch (IOException e2) {
      // Ignore the exception.
        }
    }
      }
     
      // Since server socket creation failed, use a deep copy.
      serverOpen = false; // XXX Facultative
  }

        // Write non-static and non-transient fields.
        out.defaultWriteObject();

        // Remove non-serializable elements from table of properties.
        Hashtable propertyTable = properties;
        boolean propertiesCloned = false;
        Enumeration keys = propertyTable.keys();
        while(keys.hasMoreElements()) {
            Object key = keys.nextElement();
            if (!(properties.get(key) instanceof Serializable)) {
                if (!propertiesCloned) {
                    propertyTable = (Hashtable)properties.clone();
                    propertiesCloned = true;
                }
                propertyTable.remove(key);
            }
        }

        // Write the properties table.
        out.writeObject(propertyTable);
    }

    /**
     * Custom deserialization method. In addition to all non-transient fields,
     * the SampleModel, source vector, and properties table are deserialized.
     * If a deep copy of the source image Raster is being used this is also
     * deserialized.
     */
    private void readObject(ObjectInputStream in)
        throws IOException, ClassNotFoundException {
        isServer = false;
        source = null;
        serverOpen = false;
        serverSocket = null;
        serverThread = null;

        // Read non-static and non-transient fields.
        in.defaultReadObject();

        // Read the properties table.
        properties = (Hashtable)in.readObject();
    }
}
TOP

Related Classes of com.lightcrafts.media.jai.rmi.SerializableRenderableImage

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.