1 /*
2  * $RCSfile: JAIRMIImageServer.java,v $
3  *
4  * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
5  *
6  * Use is subject to license terms.
7  *
8  * $Revision: 1.1 $
9  * $Date: 2005/02/11 04:56:51 $
10  * $State: Exp $
11  */package com.lightcrafts.media.jai.rmi;
12 
13 import java.awt.Dimension;
14 import java.awt.Rectangle;
15 import java.awt.RenderingHints;
16 import java.awt.Shape;
17 import java.awt.geom.Rectangle2D;
18 import java.awt.image.Raster;
19 import java.awt.image.RenderedImage;
20 import java.awt.image.renderable.ContextualRenderedImageFactory;
21 import java.awt.image.renderable.ParameterBlock;
22 import java.awt.image.renderable.RenderContext;
23 import java.awt.image.renderable.RenderableImage;
24 import java.io.ByteArrayOutputStream;
25 import java.io.Serializable;
26 import java.net.InetAddress;
27 import java.rmi.Naming;
28 import java.rmi.RemoteException;
29 import java.rmi.RMISecurityManager;
30 import java.rmi.server.UnicastRemoteObject;
31 import java.util.Hashtable;
32 import java.util.List;
33 import java.util.Iterator;
34 import java.util.Vector;
35 import com.lightcrafts.mediax.jai.CollectionImage;
36 import com.lightcrafts.mediax.jai.JAI;
37 import com.lightcrafts.mediax.jai.OperationDescriptor;
38 import com.lightcrafts.mediax.jai.OperationRegistry;
39 import com.lightcrafts.mediax.jai.OpImage;
40 import com.lightcrafts.mediax.jai.ParameterListDescriptor;
41 import com.lightcrafts.mediax.jai.PlanarImage;
42 import com.lightcrafts.mediax.jai.PropertySource;
43 import com.lightcrafts.mediax.jai.RenderingChangeEvent;
44 import com.lightcrafts.mediax.jai.RenderableOp;
45 import com.lightcrafts.mediax.jai.RenderedOp;
46 import com.lightcrafts.mediax.jai.registry.CRIFRegistry;
47 import com.lightcrafts.mediax.jai.remote.JAIRMIDescriptor;
48 import com.lightcrafts.mediax.jai.remote.RemoteImagingException;
49 import com.lightcrafts.mediax.jai.remote.NegotiableCapability;
50 import com.lightcrafts.mediax.jai.remote.NegotiableCapabilitySet;
51 import com.lightcrafts.mediax.jai.remote.SerializableRenderedImage;
52 import com.lightcrafts.mediax.jai.remote.SerializableState;
53 import com.lightcrafts.mediax.jai.remote.SerializerFactory;
54 import com.lightcrafts.mediax.jai.tilecodec.TileCodecDescriptor;
55 import com.lightcrafts.mediax.jai.tilecodec.TileCodecParameterList;
56 import com.lightcrafts.mediax.jai.tilecodec.TileEncoder;
57 import com.lightcrafts.mediax.jai.tilecodec.TileEncoderFactory;
58 import com.lightcrafts.mediax.jai.util.ImagingListener;
59 import com.lightcrafts.media.jai.util.ImageUtil;
60 import com.lightcrafts.media.jai.util.Service;
61 import com.lightcrafts.media.jai.remote.JAIServerConfigurationSpi;
62 
63 /**
64  * The server-side implementation of the ImageServer interface.  A
65  * JAIRMIImageServer has a RenderedImage source, acquired via one of three
66  * setSource() methods.  The first takes a RenderedImage directly as
67  * its parameter; this image is simply copied over the network using
68  * the normal RMI mechanisms.  Note that not every image can be
69  * transferred in this way -- for example, attempting to pass an
70  * OpImage that uses native code or that depends on the availability
71  * of a class not resident on the server as a parameter will cause an
72  * exception to be thrown.
73  *
74  * <p> The second and third ways of setting sources make use of the
75  * RenderedOp and RenderableOp classes to send a high-level
76  * description of an image chain based on operation names.  This
77  * chain will be copied over to the server using RMI, where it will be
78  * expanded into an OpImage chain using the server's registry.  This
79  * is the preferred method since it requires less data transfer and
80  * offers a better chance of success.  It may still fail if the
81  * sources or parameters of any operation in the chain are not
82  * serializable.
83  *
84  * <p> RMI requires all remote methods to declare `throws
85  * RemoteException' in their signatures.  It is up to the client to
86  * deal with errors.  A simple implementation of error handling may be
87  * found in the RemoteRenderedImage class.
88  *
89  * <p> This class contains a main() method that should be run on the
90  * server after starting the RMI registry.  The registry will then
91  * construct new instances of JAIRMIImageServer on demand.
92  *
93  * @see ImageServer
94  * @see RenderedOp
95  *
96  * @since 1.1
97  */
98 public class JAIRMIImageServer extends UnicastRemoteObject
99     implements ImageServer {
100 
101     private boolean DEBUG = true;
102 
103     /** Tag to represent a null property. */
104     public static final Object NULL_PROPERTY = RMIImageImpl.NULL_PROPERTY;
105 
106     /** Identifier counter for the remote images. */
107     private static long idCounter = 0;
108 
109     /**
110      * The RenderedImage nodes hashed by an ID string which must be unique
111      * across all possible clients of this object.
112      */
113     private static Hashtable nodes = new Hashtable();
114 
115     /**
116      * Hashtable to store the negotiated values for each id.
117      */
118     private static Hashtable negotiated = new Hashtable();
119 
120     /**
121      * Hashtable to store the number of references existing to a
122      * particular id on this server.
123      */
124     private static Hashtable refCount = new Hashtable();
125 
126     /**
127      * Retrieve a PlanarImage source from the Hashtable of sources.
128      *
129      * @param id The unique ID of the source.
130      * @return The source.
131      */
getSource(Long id)132     private static PlanarImage getSource(Long id) throws RemoteException {
133 	Object obj = null;
134 	if(nodes == null ||
135 	   (obj = nodes.get(id)) == null) {
136 	    throw new RemoteException(JaiI18N.getString("RMIImageImpl2"));
137 	}
138 	return (PlanarImage)obj;
139     }
140 
141     /**
142      * Retrieve a PropertySource from the Hashtable of PropertySources.
143      *
144      * @param id The unique ID of the source.
145      * @return The PropertySource.
146      */
getPropertySource(Long id)147     private static PropertySource getPropertySource(Long id)
148         throws RemoteException {
149 
150 	Object obj = nodes.get(id);
151 	return (PropertySource)obj;
152     }
153 
154     /**
155      * Constructs a JAIRMIImageServer with a source to be specified
156      * later.
157      */
JAIRMIImageServer(int serverport)158     public JAIRMIImageServer(int serverport) throws RemoteException {
159         super(serverport);
160     }
161 
162     /**
163      * Returns the identifier of the remote image. This method should be
164      * called to return an identifier before any other methods are invoked.
165      * The same ID must be used in all subsequent references to the remote
166      * image.
167      */
getRemoteID()168     public synchronized Long getRemoteID() throws RemoteException {
169         return new Long(++idCounter);
170     }
171 
172     /**
173      * Disposes of any resouces allocated to the client object with
174      * the specified ID.
175      */
dispose(Long id)176     public synchronized void dispose(Long id)  throws RemoteException {
177 
178 	int count = ((Integer)refCount.get(id)).intValue();
179 
180 	if (count == 1) {
181 
182 	    // If this was the last reference, remove all Objects
183 	    // associated with this id in various Hashtables.
184 	    if (nodes != null) {
185 		nodes.remove(id);
186 		negotiated.remove(id);
187 	    }
188 
189 	    refCount.remove(id);
190 
191 	} else {
192 
193 	    // Decrement count of references to this id.
194 	    count--;
195 	    if (count == 0) {
196 		refCount.remove(id);
197 	    }
198 	    refCount.put(id, new Integer(count));
199 	}
200     }
201 
202     /**
203      * Increments the reference count for this id, i.e. increments the
204      * number of RMIServerProxy objects that currently reference this id.
205      */
incrementRefCount(Long id)206     public void incrementRefCount(Long id) throws RemoteException {
207 	Integer iCount = (Integer)refCount.get(id);
208 	int count = 0;
209 	if (iCount != null) {
210 	    count = iCount.intValue();
211 	}
212 	count++;
213 	refCount.put(id, new Integer(count));
214     }
215 
216     /** Gets a property from the property set of this image.  If the
217      *	property is undefined the constant NULL_PROPERTY is returned.
218      */
getProperty(Long id, String name)219     public Object getProperty(Long id, String name) throws RemoteException {
220 
221 	PropertySource ps = getPropertySource(id);
222 	Object property = ps.getProperty(name);
223 
224 	if (property == null ||
225 	    property.equals(java.awt.Image.UndefinedProperty)) {
226 	    property = NULL_PROPERTY;
227 	}
228 
229 	return property;
230     }
231 
232     /**
233      * Returns a list of names recognized by getProperty().
234      *
235      * @return an array of Strings representing property names.
236      */
getPropertyNames(Long id)237     public String[] getPropertyNames(Long id) throws RemoteException {
238 
239 	PropertySource ps = getPropertySource(id);
240 	return ps.getPropertyNames();
241 
242     }
243 
244     /**
245      * Returns a list of names recognized by getProperty().
246      *
247      * @return an array of Strings representing property names.
248      */
getPropertyNames(String opName)249     public String[] getPropertyNames(String opName) throws RemoteException {
250 
251 	return (CRIFRegistry.get(null, opName)).getPropertyNames();
252     }
253 
254     /** Returns the minimum X coordinate of the ImageServer. */
getMinX(Long id)255     public int getMinX(Long id) throws RemoteException {
256 	return getSource(id).getMinX();
257     }
258 
259     /** Returns the smallest X coordinate to the right of the ImageServer. */
getMaxX(Long id)260     public int getMaxX(Long id) throws RemoteException {
261 
262 	return getSource(id).getMaxX();
263     }
264 
265     /** Returns the minimum Y coordinate of the ImageServer. */
getMinY(Long id)266     public int getMinY(Long id) throws RemoteException {
267 
268 	return getSource(id).getMinY();
269     }
270 
271     /** Returns the smallest Y coordinate below the ImageServer. */
getMaxY(Long id)272     public int getMaxY(Long id) throws RemoteException {
273 
274 	return getSource(id).getMaxY();
275     }
276 
277     /** Returns the width of the ImageServer. */
getWidth(Long id)278     public int getWidth(Long id) throws RemoteException {
279 
280 	return getSource(id).getWidth();
281     }
282 
283     /** Returns the height of the ImageServer. */
getHeight(Long id)284     public int getHeight(Long id) throws RemoteException {
285 
286 	return getSource(id).getHeight();
287     }
288 
289     /** Returns the width of a tile in pixels. */
getTileWidth(Long id)290     public int getTileWidth(Long id) throws RemoteException {
291 
292 	return getSource(id).getTileWidth();
293     }
294 
295     /** Returns the height of a tile in pixels. */
getTileHeight(Long id)296     public int getTileHeight(Long id) throws RemoteException {
297 
298 	return getSource(id).getTileHeight();
299     }
300 
301     /**
302      * Returns the X coordinate of the upper-left pixel of tile (0, 0).
303      */
getTileGridXOffset(Long id)304     public int getTileGridXOffset(Long id) throws RemoteException {
305 
306 	return getSource(id).getTileGridXOffset();
307     }
308 
309     /**
310      * Returns the Y coordinate of the upper-left pixel of tile (0, 0).
311      */
getTileGridYOffset(Long id)312     public int getTileGridYOffset(Long id) throws RemoteException {
313 
314         return getSource(id).getTileGridYOffset();
315     }
316 
317     /** Returns the index of the leftmost column of tiles. */
getMinTileX(Long id)318     public int getMinTileX(Long id) throws RemoteException {
319 
320 	return getSource(id).getMinTileX();
321     }
322 
323     /**
324      * Returns the number of tiles along the tile grid in the horizontal
325      * direction.
326      */
getNumXTiles(Long id)327     public int getNumXTiles(Long id) throws RemoteException {
328 
329 	return getSource(id).getNumXTiles();
330     }
331 
332     /** Returns the index of the uppermost row of tiles. */
getMinTileY(Long id)333     public int getMinTileY(Long id) throws RemoteException {
334 
335 	return getSource(id).getMinTileY();
336     }
337 
338     /**
339      * Returns the number of tiles along the tile grid in the vertical
340      * direction.
341      */
getNumYTiles(Long id)342     public int getNumYTiles(Long id) throws RemoteException {
343 
344 	return getSource(id).getNumYTiles();
345     }
346 
347     /** Returns the index of the rightmost column of tiles. */
getMaxTileX(Long id)348     public int getMaxTileX(Long id) throws RemoteException {
349 
350 	return getSource(id).getMaxTileX();
351     }
352 
353     /** Returns the index of the bottom row of tiles. */
getMaxTileY(Long id)354     public int getMaxTileY(Long id) throws RemoteException {
355 
356 	return getSource(id).getMaxTileY();
357     }
358 
359     /** Returns the SampleModel associated with this image. */
getSampleModel(Long id)360     public SerializableState getSampleModel(Long id) throws RemoteException {
361         return SerializerFactory.getState(getSource(id).getSampleModel(),
362 					  null);
363     }
364 
365     /** Returns the ColorModel associated with this image. */
getColorModel(Long id)366     public SerializableState getColorModel(Long id) throws RemoteException {
367         return SerializerFactory.getState(getSource(id).getColorModel(), null);
368     }
369 
370     /** Returns a Rectangle indicating the image bounds. */
getBounds(Long id)371     public Rectangle getBounds(Long id) throws RemoteException {
372 
373 	return getSource(id).getBounds();
374     }
375 
376     /**
377      * Returns tile (x, y).  Note that x and y are indices into the
378      * tile array, not pixel locations.  Unlike in the true RenderedImage
379      * interface, the Raster that is returned should be considered a copy.
380      *
381      * @param id An ID for the source which must be unique across all clients.
382      * @param tileX the X index of the requested tile in the tile array.
383      * @param tileY the Y index of the requested tile in the tile array.
384      * @return the tile as a Raster.
385      */
getTile(Long id, int tileX, int tileY)386     public SerializableState getTile(Long id, int tileX, int tileY)
387         throws RemoteException {
388 
389 	Raster r = getSource(id).getTile(tileX, tileY);
390 	return SerializerFactory.getState(r, null);
391     }
392 
393     /**
394      * Compresses tile (x, y) and returns the compressed tile's contents
395      * as a byte array.  Note that x and y are indices into the
396      * tile array, not pixel locations.  Unlike in the true RenderedImage
397      * interface, the Raster that is returned should be considered a copy.
398      *
399      * @param id An ID for the source which must be unique across all clients.
400      * @param x the x index of the requested tile in the tile array
401      * @param y the y index of the requested tile in the tile array
402      * @return a byte array containing the compressed tile contents.
403      */
getCompressedTile(Long id, int x, int y)404     public byte[] getCompressedTile(Long id, int x, int y)
405 	throws RemoteException {
406 
407 	TileCodecParameterList tcpl = null;
408 	TileEncoderFactory tef = null;
409 	NegotiableCapability codecCap = null;
410 
411 	if (negotiated != null) {
412 	    codecCap = ((NegotiableCapabilitySet)negotiated.get(id)).
413 		getNegotiatedValue("tileCodec");
414 	}
415 
416 	if (codecCap != null) {
417 
418 	    String category = codecCap.getCategory();
419 	    String capabilityName = codecCap.getCapabilityName();
420 	    List generators = codecCap.getGenerators();
421 
422 	    Class factory;
423 	    for (Iterator i=generators.iterator(); i.hasNext(); ) {
424 
425 		factory = (Class)i.next();
426 		if (tef == null &&
427 		    TileEncoderFactory.class.isAssignableFrom(factory)) {
428 		    try {
429 			tef = (TileEncoderFactory)factory.newInstance();
430 		    } catch (InstantiationException ie) {
431 			throw new RuntimeException(ie.getMessage());
432 		    } catch (IllegalAccessException iae) {
433 			throw new RuntimeException(iae.getMessage());
434 		    }
435 		}
436 	    }
437 
438 	    if (tef == null) {
439 		throw new RuntimeException(
440 				     JaiI18N.getString("JAIRMIImageServer0"));
441 	    }
442 
443 	    TileCodecDescriptor tcd =
444 		(TileCodecDescriptor)JAI.getDefaultInstance().
445 		getOperationRegistry().getDescriptor("tileEncoder",
446 						     capabilityName);
447 
448 	    if (tcd.includesSampleModelInfo() == false ||
449 		tcd.includesLocationInfo() == false) {
450 		throw new RuntimeException(
451 				     JaiI18N.getString("JAIRMIImageServer1"));
452 	    }
453 
454 	    ParameterListDescriptor pld =
455 		tcd.getParameterListDescriptor("tileEncoder");
456 
457 	    tcpl = new TileCodecParameterList(capabilityName,
458 					      new String[] {"tileEncoder"},
459 					      pld);
460 
461 	    if (pld != null) {
462 
463 		String paramNames[] = pld.getParamNames();
464 		String currParam;
465 		Object currValue;
466 
467 		if (paramNames != null) {
468 		    for (int i=0; i<paramNames.length; i++) {
469 			currParam = paramNames[i];
470 			try {
471 			    currValue = codecCap.getNegotiatedValue(currParam);
472 			} catch (IllegalArgumentException iae) {
473 			    continue;
474 			}
475 			tcpl.setParameter(currParam, currValue);
476 		    }
477 		}
478 	    }
479 
480 	    Raster r = getSource(id).getTile(x, y);
481 	    ByteArrayOutputStream stream = new ByteArrayOutputStream();
482 	    TileEncoder encoder = tef.createEncoder(stream, tcpl,
483 						    r.getSampleModel());
484 
485 	    try {
486 		encoder.encode(r);
487 	    } catch (java.io.IOException ioe) {
488 		throw new RuntimeException(ioe.getMessage());
489 	    }
490 
491 	    return stream.toByteArray();
492 	} else {
493 	    throw new RuntimeException(
494 				     JaiI18N.getString("JAIRMIImageServer2"));
495 	}
496     }
497 
498     /**
499      * Returns the entire image as a single Raster.
500      *
501      * @return a Raster containing a copy of this image's data.
502      */
getData(Long id)503     public SerializableState getData(Long id) throws RemoteException {
504         return SerializerFactory.getState(getSource(id).getData(), null);
505     }
506 
507     /**
508      * Returns an arbitrary rectangular region of the RenderedImage
509      * in a Raster.  The rectangle of interest will be clipped against
510      * the image bounds.
511      *
512      * @param id An ID for the source which must be unique across all clients.
513      * @param rect the region of the RenderedImage to be returned.
514      * @return a SerializableState containing a copy of the desired data.
515      */
getData(Long id, Rectangle bounds)516     public SerializableState getData(Long id, Rectangle bounds)
517         throws RemoteException {
518         if (bounds == null) {
519             return getData(id);
520         } else {
521             bounds = bounds.intersection(getBounds(id));
522             return SerializerFactory.getState(getSource(id).getData(bounds),
523 					      null);
524         }
525     }
526 
527     /**
528      * Returns the same result as getData(Rectangle) would for the
529      * same rectangular region.
530      */
copyData(Long id, Rectangle bounds)531     public SerializableState copyData(Long id, Rectangle bounds)
532         throws RemoteException {
533         return getData(id, bounds);
534     }
535 
536 
537 
538 
539     /**
540      * Creates a RenderedOp on the server side with a parameter block
541      * empty of sources. The sources are set by separate calls depending
542      * upon the type and serializabilty of the source.
543      */
createRenderedOp(Long id, String opName, ParameterBlock pb, SerializableState hints)544     public void createRenderedOp(Long id, String opName,
545 				 ParameterBlock pb,
546 				 SerializableState hints)
547 	throws RemoteException {
548 
549 	RenderingHints rh = (RenderingHints)hints.getObject();
550 
551 	// Check whether any of the parameters are Strings which represent
552 	// images either on this server or another server.
553 	JAIRMIUtil.checkServerParameters(pb, nodes);
554 
555 	RenderedOp node = new RenderedOp(opName, pb, rh);
556 
557 	// Remove all sinks so that no events are sent automatically
558 	// to the sinks
559 	node.removeSinks();
560 
561 	nodes.put(id, node);
562     }
563 
564     /**
565      * Calls for Rendering of the Op and returns true if the RenderedOp
566      * could be rendered else false
567      */
getRendering(Long id)568     public boolean getRendering(Long id) throws RemoteException {
569 
570 	RenderedOp op = getNode(id);
571 	if (op.getRendering() == null) {
572 	    return false;
573 	} else {
574 	    return true;
575 	}
576     }
577 
578     /**
579      * Retrieve a node from the hashtable
580      *
581      */
getNode(Long id)582     public RenderedOp getNode(Long id) throws RemoteException {
583 	return (RenderedOp)nodes.get(id);
584     }
585 
586     /**
587      *  Sets the source of the image as a RenderedImage on the server side
588      */
setRenderedSource(Long id, RenderedImage source, int index)589     public synchronized void setRenderedSource(Long id,
590 					       RenderedImage source,
591 					       int index)
592 	throws RemoteException {
593 
594 	PlanarImage pi = PlanarImage.wrapRenderedImage(source);
595 
596 	Object obj = nodes.get(id);
597 
598 	if (obj instanceof RenderedOp) {
599 	    RenderedOp op = (RenderedOp)obj;
600 	    op.setSource(pi, index);
601 	    ((PlanarImage)op.getSourceObject(index)).removeSinks();
602 	} else if (obj instanceof RenderableOp) {
603 	    ((RenderableOp)obj).setSource(pi, index);
604 	}
605     }
606 
607     /**
608      *  Sets the source of the image as a RenderedOp on the server side
609      */
setRenderedSource(Long id, RenderedOp source, int index)610     public synchronized void setRenderedSource(Long id,
611 					       RenderedOp source,
612 					       int index)
613 	throws RemoteException {
614 
615 	Object obj = nodes.get(id);
616 	if (obj instanceof RenderedOp) {
617 	    RenderedOp op = (RenderedOp)obj;
618 	    op.setSource(source.getRendering(), index);
619 	    ((PlanarImage)op.getSourceObject(index)).removeSinks();
620 	} else if (obj instanceof RenderableOp) {
621 	    ((RenderableOp)obj).setSource(source.getRendering(), index);
622 	}
623     }
624 
625     /**
626      * Sets the source of the image which is on the same
627      * server
628      */
setRenderedSource(Long id, Long sourceId, int index)629     public synchronized void setRenderedSource(Long id,
630 					       Long sourceId,
631 					       int index)
632 	throws RemoteException {
633 
634 	Object obj = nodes.get(id);
635 	if (obj instanceof RenderedOp) {
636 	    RenderedOp op = (RenderedOp)obj;
637 	    op.setSource(nodes.get(sourceId), index);
638 	    ((PlanarImage)nodes.get(sourceId)).removeSinks();
639 	} else if (obj instanceof RenderableOp) {
640 	    ((RenderableOp)obj).setSource(nodes.get(sourceId), index);
641 	}
642     }
643 
644     /**
645      * Sets the source of the image which is on a different
646      * server
647      */
setRenderedSource(Long id, Long sourceId, String serverName, String opName, int index)648     public synchronized void setRenderedSource(Long id,
649 					       Long sourceId,
650 					       String serverName,
651 					       String opName,
652 					       int index)
653 	throws RemoteException {
654 
655 	Object obj = nodes.get(id);
656 
657 	if (obj instanceof RenderedOp) {
658 	    RenderedOp node = (RenderedOp)obj;
659 	    node.setSource(new RMIServerProxy((serverName+"::"+sourceId),
660 					      opName,
661 					      null),
662 			   index);
663 	    ((PlanarImage)node.getSourceObject(index)).removeSinks();
664 	} else if (obj instanceof RenderableOp) {
665 	    ((RenderableOp)obj).setSource(new RMIServerProxy((serverName +
666 							      "::" + sourceId),
667 							     opName,
668 							     null),
669 					  index);
670 	}
671     }
672 
673     /// Renderable Mode Methods
674 
675     /**
676      * Gets the minimum X coordinate of the rendering-independent image
677      * stored against the given ID.
678      *
679      * @return the minimum X coordinate of the rendering-independent image
680      * data.
681      */
getRenderableMinX(Long id)682     public float getRenderableMinX(Long id) throws RemoteException {
683 
684 	RenderableImage ri = (RenderableImage)nodes.get(id);
685 	return ri.getMinX();
686     }
687 
688     /**
689      * Gets the minimum Y coordinate of the rendering-independent image
690      * stored against the given ID.
691      *
692      * @return the minimum X coordinate of the rendering-independent image
693      * data.
694      */
getRenderableMinY(Long id)695     public float getRenderableMinY(Long id) throws RemoteException {
696 	RenderableImage ri = (RenderableImage)nodes.get(id);
697 	return ri.getMinY();
698     }
699 
700     /**
701      * Gets the width (in user coordinate space) of the
702      * <code>RenderableImage</code> stored against the given ID.
703      *
704      * @return the width of the renderable image in user coordinates.
705      */
getRenderableWidth(Long id)706     public float getRenderableWidth(Long id) throws RemoteException {
707 	RenderableImage ri = (RenderableImage)nodes.get(id);
708 	return ri.getWidth();
709     }
710 
711     /**
712      * Gets the height (in user coordinate space) of the
713      * <code>RenderableImage</code> stored against the given ID.
714      *
715      * @return the height of the renderable image in user coordinates.
716      */
getRenderableHeight(Long id)717     public float getRenderableHeight(Long id) throws RemoteException {
718 	RenderableImage ri = (RenderableImage)nodes.get(id);
719 	return ri.getHeight();
720     }
721 
722     /**
723      * Creates a RenderedImage instance of this image with width w, and
724      * height h in pixels.  The RenderContext is built automatically
725      * with an appropriate usr2dev transform and an area of interest
726      * of the full image.  All the rendering hints come from hints
727      * passed in.
728      *
729      * <p> If w == 0, it will be taken to equal
730      * Math.round(h*(getWidth()/getHeight())).
731      * Similarly, if h == 0, it will be taken to equal
732      * Math.round(w*(getHeight()/getWidth())).  One of
733      * w or h must be non-zero or else an IllegalArgumentException
734      * will be thrown.
735      *
736      * <p> The created RenderedImage may have a property identified
737      * by the String HINTS_OBSERVED to indicate which RenderingHints
738      * were used to create the image.  In addition any RenderedImages
739      * that are obtained via the getSources() method on the created
740      * RenderedImage may have such a property.
741      *
742      * @param w the width of rendered image in pixels, or 0.
743      * @param h the height of rendered image in pixels, or 0.
744      * @param hints a RenderingHints object containg hints.
745      * @return a RenderedImage containing the rendered data.
746      */
createScaledRendering(Long id, int w, int h, SerializableState hintsState)747     public RenderedImage createScaledRendering(Long id,
748 					       int w,
749 					       int h,
750 					       SerializableState hintsState)
751 	throws RemoteException {
752 
753 	RenderableImage ri = (RenderableImage)nodes.get(id);
754 	RenderingHints hints = (RenderingHints)hintsState.getObject();
755 	RenderedImage rendering = ri.createScaledRendering(w, h, hints);
756 	if (rendering instanceof Serializable) {
757 	    return rendering;
758 	} else {
759 	    return new SerializableRenderedImage(rendering);
760 	}
761     }
762 
763     /**
764      * Returnd a RenderedImage instance of this image with a default
765      * width and height in pixels.  The RenderContext is built
766      * automatically with an appropriate usr2dev transform and an area
767      * of interest of the full image.  The rendering hints are
768      * empty.  createDefaultRendering may make use of a stored
769      * rendering for speed.
770      *
771      * @return a RenderedImage containing the rendered data.
772      */
createDefaultRendering(Long id)773     public RenderedImage createDefaultRendering(Long id)
774 	throws RemoteException {
775 
776 	RenderableImage ri = (RenderableImage)nodes.get(id);
777 	RenderedImage rendering = ri.createDefaultRendering();
778 	if (rendering instanceof Serializable) {
779 	    return rendering;
780 	} else {
781 	    return new SerializableRenderedImage(rendering);
782 	}
783     }
784 
785     /**
786      * Creates a RenderedImage that represented a rendering of this image
787      * using a given RenderContext.  This is the most general way to obtain a
788      * rendering of a RenderableImage.
789      *
790      * <p> The created RenderedImage may have a property identified
791      * by the String HINTS_OBSERVED to indicate which RenderingHints
792      * (from the RenderContext) were used to create the image.
793      * In addition any RenderedImages
794      * that are obtained via the getSources() method on the created
795      * RenderedImage may have such a property.
796      *
797      * @param renderContext the RenderContext to use to produce the rendering.
798      * @return a RenderedImage containing the rendered data.
799      */
createRendering(Long id, SerializableState renderContextState)800     public RenderedImage createRendering(Long id,
801 					 SerializableState renderContextState)
802 	throws RemoteException {
803 
804 	RenderableImage ri = (RenderableImage)nodes.get(id);
805 	RenderContext renderContext =
806 	    (RenderContext)renderContextState.getObject();
807 	RenderedImage rendering = ri.createRendering(renderContext);
808 	if (rendering instanceof Serializable) {
809 	    return rendering;
810 	} else {
811 	    return new SerializableRenderedImage(rendering);
812 	}
813     }
814 
815     /**
816      * Creates a RenderableOp on the server side with a parameter block
817      * empty of sources. The sources are set by separate calls depending
818      * upon the type and serializabilty of the source.
819      */
createRenderableOp(Long id, String opName, ParameterBlock pb)820     public synchronized void createRenderableOp(Long id,
821 						String opName,
822 						ParameterBlock pb)
823 	throws RemoteException {
824 
825 	// XXX Since RMIServerProxy does not do a checkClientParameters, this
826 	// side obviously does not do the corresponding
827 	// checkServerParameters. Look at RMIServerProxy's renderable
828 	// constructor for reasoning. aastha, 09/26/01
829 
830 	RenderableOp node = new RenderableOp(opName, pb);
831 	nodes.put(id, node);
832     }
833 
834     /**
835      * Calls for rendering of a RenderableOp with the given SerializableState
836      */
getRendering(Long id, SerializableState rcs)837     public synchronized Long getRendering(Long id, SerializableState rcs)
838 	throws RemoteException {
839 
840 	RenderableOp op = (RenderableOp)nodes.get(id);
841 	PlanarImage pi = PlanarImage.wrapRenderedImage(op.createRendering(
842 					     (RenderContext)rcs.getObject()));
843 
844 	Long renderingID = getRemoteID();
845 	nodes.put(renderingID, pi);
846 
847 	// Put the op's negotiated result values for its rendering too.
848 	setServerNegotiatedValues(renderingID, (NegotiableCapabilitySet)
849 				  negotiated.get(id));
850 	return renderingID;
851     }
852 
853     /**
854      * Sets the source of the image which is on the same
855      * server
856      */
setRenderableSource(Long id, Long sourceId, int index)857     public synchronized void setRenderableSource(Long id,
858 						 Long sourceId,
859 						 int index)
860 	throws RemoteException {
861 
862 	RenderableOp node = (RenderableOp)nodes.get(id);
863 	Object obj = nodes.get(sourceId);
864 	if (obj instanceof RenderableOp){
865 	    node.setSource((RenderableOp)obj, index);
866 	} else if (obj instanceof RenderedImage) {
867 	    node.setSource(PlanarImage.wrapRenderedImage((RenderedImage)obj),
868 			   index);
869 	}
870     }
871 
872     /**
873      * Sets the source of the image which is on a different
874      * server
875      */
setRenderableSource(Long id, Long sourceId, String serverName, String opName, int index)876     public synchronized void setRenderableSource(Long id,
877 						 Long sourceId,
878 						 String serverName,
879 						 String opName,
880 						 int index)
881 	throws RemoteException {
882 
883 	RenderableOp node = (RenderableOp)nodes.get(id);
884 	node.setSource(new RMIServerProxy((serverName+"::"+sourceId),
885 					  opName,
886 					  null),
887 		       index);
888 
889     }
890 
891     /**
892      * Sets the source of the image which is on a different
893      * server
894      */
setRenderableRMIServerProxyAsSource( Long id, Long sourceId, String serverName, String opName, int index)895     public synchronized void setRenderableRMIServerProxyAsSource(
896 							    Long id,
897 							    Long sourceId,
898 							    String serverName,
899 							    String opName,
900 							    int index)
901 	throws RemoteException {
902 
903 	RenderableOp node = (RenderableOp)nodes.get(id);
904 	node.setSource(new RenderableRMIServerProxy(serverName, opName, null,
905 						    sourceId), index);
906     }
907 
908     /**
909      * when source is set to a  RenderableOp and isnt supposed to be
910      * rendered yet. like at the time of getBounds2D
911      *
912      * Sets the source of the image as a RenderableOp on the server side
913      *
914      */
setRenderableSource(Long id, RenderableOp source, int index)915     public synchronized void setRenderableSource(Long id, RenderableOp source,
916 						 int index)
917 	throws RemoteException {
918 	RenderableOp op = (RenderableOp)nodes.get(id);
919 	op.setSource(source, index);
920     }
921 
922     /**
923      * Sets the source of the image as a RenderableImage on the server side
924      */
setRenderableSource(Long id, SerializableRenderableImage s, int index)925     public synchronized void setRenderableSource(Long id,
926 						 SerializableRenderableImage s,
927 						 int index)
928 	throws RemoteException {
929 	RenderableOp op = (RenderableOp)nodes.get(id);
930 	op.setSource(s, index);
931     }
932 
933     /**
934      *  Sets the source of the image as a RenderedImage on the server side
935      */
setRenderableSource(Long id, RenderedImage source, int index)936     public synchronized void setRenderableSource(Long id,
937 						 RenderedImage source,
938 						 int index)
939 	throws RemoteException {
940 
941 	PlanarImage pi = PlanarImage.wrapRenderedImage(source);
942 	RenderableOp op = (RenderableOp)nodes.get(id);
943 	op.setSource(pi, index);
944     }
945 
946     /**
947      * Maps the RenderContext for the remote Image
948      */
mapRenderContext(int id, Long nodeId, String operationName, SerializableState rcs)949     public SerializableState mapRenderContext(int id, Long nodeId,
950 					      String operationName,
951 					      SerializableState rcs)
952 	throws RemoteException {
953 
954 	// Retrieve the RenderableOp for the rendering of which
955 	// the mapRenderContext call is being made.
956 	RenderableOp rop = (RenderableOp)nodes.get(nodeId);
957 
958 	//Find the CRIF for the respective operation
959 	ContextualRenderedImageFactory crif =
960 	    CRIFRegistry.get(rop.getRegistry(), operationName);
961 
962 	if (crif == null) {
963 	    throw new RuntimeException(
964 				    JaiI18N.getString("JAIRMIImageServer3"));
965 	}
966 
967 	RenderContext rc =
968 	    crif.mapRenderContext(id,
969 				  (RenderContext)rcs.getObject(),
970 				  (ParameterBlock)rop.getParameterBlock(),
971 				  rop);
972 	return SerializerFactory.getState(rc, null);
973     }
974 
975     /**
976      * Gets the Bounds2D of the specified Remote Image
977      */
getBounds2D(Long nodeId, String operationName)978     public SerializableState getBounds2D(Long nodeId, String operationName)
979 	throws RemoteException {
980 
981 	// Retrieve the RenderableOp for whose RIF
982 	// the mapRenderContext call is being made.
983 	RenderableOp rop = (RenderableOp)nodes.get(nodeId);
984 
985 	//Find the CRIF for the respective operation
986 	ContextualRenderedImageFactory crif =
987 	    CRIFRegistry.get(rop.getRegistry(), operationName);
988 
989 	if (crif == null) {
990 	    throw new RuntimeException(
991 				    JaiI18N.getString("JAIRMIImageServer3"));
992 	}
993 
994 	Rectangle2D r2D =
995 	    crif.getBounds2D((ParameterBlock)rop.getParameterBlock());
996 
997 	return SerializerFactory.getState(r2D, null);
998     }
999 
1000     /**
1001      * Returns <code>true</code> if successive renderings with the same
1002      * arguments may produce different results for this opName
1003      *
1004      * @return <code>false</code> indicating that the rendering is static.
1005      */
isDynamic(String opName)1006     public boolean isDynamic(String opName) throws RemoteException {
1007 
1008 	return (CRIFRegistry.get(null, opName)).isDynamic();
1009     }
1010 
1011     /**
1012      * Returns <code>true</code> if successive renderings with the same
1013      * arguments may produce different results for the node represented
1014      * by the given id.
1015      */
isDynamic(Long id)1016     public boolean isDynamic(Long id) throws RemoteException {
1017 
1018 	RenderableImage node = (RenderableImage)nodes.get(id);
1019 	return node.isDynamic();
1020     }
1021 
1022     /**
1023      * Gets the operation names supported on the Server
1024      */
getServerSupportedOperationNames()1025     public String[] getServerSupportedOperationNames()
1026 	throws RemoteException {
1027 	return JAI.getDefaultInstance().getOperationRegistry().
1028 	    getDescriptorNames(OperationDescriptor.class);
1029     }
1030 
1031     /**
1032      * Gets the <code>OperationDescriptor</code>s of the operations
1033      * supported on this server.
1034      */
getOperationDescriptors()1035     public List getOperationDescriptors() throws RemoteException {
1036 	return JAI.getDefaultInstance().getOperationRegistry().
1037 	    getDescriptors(OperationDescriptor.class);
1038     }
1039 
1040     /**
1041      * Calculates the region over which two distinct renderings
1042      * of an operation may be expected to differ.
1043      *
1044      * <p> The class of the returned object will vary as a function of
1045      * the nature of the operation.  For rendered and renderable two-
1046      * dimensional images this should be an instance of a class which
1047      * implements <code>java.awt.Shape</code>.
1048      *
1049      * @return The region over which the data of two renderings of this
1050      *         operation may be expected to be invalid or <code>null</code>
1051      *         if there is no common region of validity.
1052      */
getInvalidRegion( Long id, ParameterBlock oldParamBlock, SerializableState oldRHints, ParameterBlock newParamBlock, SerializableState newRHints)1053     public synchronized SerializableState getInvalidRegion(
1054 						 Long id,
1055 						 ParameterBlock oldParamBlock,
1056 						 SerializableState oldRHints,
1057 						 ParameterBlock newParamBlock,
1058 						 SerializableState newRHints)
1059 	throws RemoteException {
1060 
1061 	RenderingHints oldHints = (RenderingHints)oldRHints.getObject();
1062 	RenderingHints newHints = (RenderingHints)newRHints.getObject();
1063 
1064 	RenderedOp op = (RenderedOp)nodes.get(id);
1065 
1066 	OperationDescriptor od = (OperationDescriptor)
1067 	    JAI.getDefaultInstance().getOperationRegistry().
1068 	    getDescriptor("rendered", op.getOperationName());
1069 
1070 	boolean samePBs = false;
1071 	if (oldParamBlock == newParamBlock)
1072 	    samePBs = true;
1073 
1074 	Vector oldSources = oldParamBlock.getSources();
1075 	oldParamBlock.removeSources();
1076 	Vector oldReplacedSources =
1077 	    JAIRMIUtil.replaceIdWithSources(oldSources,
1078 					    nodes,
1079 					    op.getOperationName(),
1080 					    op.getRenderingHints());
1081 	oldParamBlock.setSources(oldReplacedSources);
1082 
1083 	if (samePBs) {
1084 	    newParamBlock = oldParamBlock;
1085 	} else {
1086 	    Vector newSources = newParamBlock.getSources();
1087 	    newParamBlock.removeSources();
1088 	    Vector newReplacedSources =
1089 		JAIRMIUtil.replaceIdWithSources(newSources,
1090 						nodes,
1091 						op.getOperationName(),
1092 						op.getRenderingHints());
1093 
1094 	    newParamBlock.setSources(newReplacedSources);
1095 	}
1096 
1097 	Object invalidRegion = od.getInvalidRegion("rendered",
1098 						   oldParamBlock,
1099 						   oldHints,
1100 						   newParamBlock,
1101 						   newHints,
1102 						   op);
1103 
1104 	SerializableState shapeState =
1105 	    SerializerFactory.getState((Shape)invalidRegion, null);
1106 
1107 	return shapeState;
1108     }
1109 
1110     /**
1111      * Returns a conservative estimate of the destination region that
1112      * can potentially be affected by the pixels of a rectangle of a
1113      * given source.
1114      *
1115      * @param id          A <code>Long</code> identifying the node for whom
1116      *                    the destination region needs to be calculated .
1117      * @param sourceRect  The <code>Rectangle</code> in source coordinates.
1118      * @param sourceIndex The index of the source image.
1119      *
1120      * @return A <code>Rectangle</code> indicating the potentially
1121      *         affected destination region, or <code>null</code> if
1122      *         the region is unknown.
1123      */
mapSourceRect(Long id, Rectangle sourceRect, int sourceIndex)1124     public Rectangle mapSourceRect(Long id,
1125 				   Rectangle sourceRect,
1126 				   int sourceIndex) throws RemoteException {
1127 
1128 	RenderedOp op = (RenderedOp)nodes.get(id);
1129 	OpImage rendering = (OpImage)(op.getRendering());
1130 	return rendering.mapSourceRect(sourceRect, sourceIndex);
1131     }
1132 
1133     /**
1134      * Returns a conservative estimate of the region of a specified
1135      * source that is required in order to compute the pixels of a
1136      * given destination rectangle.
1137      *
1138      * @param id         A <code>Long</code> identifying the node for whom
1139      *                   the source region needs to be calculated .
1140      * @param destRect   The <code>Rectangle</code> in destination coordinates.
1141      * @param sourceIndex The index of the source image.
1142      *
1143      * @return A <code>Rectangle</code> indicating the required source region.
1144      */
mapDestRect(Long id, Rectangle destRect, int sourceIndex)1145     public Rectangle mapDestRect(Long id, Rectangle destRect, int sourceIndex)
1146 	throws RemoteException {
1147 
1148 	RenderedOp op = (RenderedOp)nodes.get(id);
1149 	OpImage rendering = (OpImage)(op.getRendering());
1150 	return rendering.mapDestRect(destRect, sourceIndex);
1151     }
1152 
1153     /**
1154      * A method that handles the given event.
1155      */
handleEvent(Long renderedOpID, String propName, Object oldValue, Object newValue)1156     public synchronized Long handleEvent(Long renderedOpID, String propName,
1157 					 Object oldValue, Object newValue)
1158 	throws RemoteException {
1159 
1160 	RenderedOp op = (RenderedOp)nodes.get(renderedOpID);
1161 	PlanarImage rendering = op.getRendering();
1162 
1163 	// Get a new unique ID
1164 	Long id = getRemoteID();
1165 	// Cache the old rendering against the new id
1166 	nodes.put(id, rendering);
1167 
1168 	// Put the op's negotiated result values for its rendering too.
1169 	setServerNegotiatedValues(id, (NegotiableCapabilitySet)
1170 				  negotiated.get(renderedOpID));
1171 
1172 	// A PropertyChangeEventJAI with name "operationregistry",
1173 	// "protocolname", "protocolandservername" or "servername" should
1174 	// never be received here, since it is handled entirely on the
1175 	// client side, so we don't handle those here.
1176 
1177 	if (propName.equals("operationname")) {
1178 
1179 	    op.setOperationName((String)newValue);
1180 
1181 	} else if (propName.equals("parameterblock")) {
1182 
1183 	    ParameterBlock newPB = (ParameterBlock)newValue;
1184 	    Vector newSrcs = newPB.getSources();
1185 	    newPB.removeSources();
1186 
1187 	    JAIRMIUtil.checkServerParameters(newPB, nodes);
1188 
1189 	    Vector replacedSources =
1190 		JAIRMIUtil.replaceIdWithSources(newSrcs,
1191 						nodes,
1192 						op.getOperationName(),
1193 						op.getRenderingHints());
1194 	    newPB.setSources(replacedSources);
1195 
1196 	    op.setParameterBlock(newPB);
1197 
1198 	    // Remove the newly created sinks of the srcs in the newPB
1199 	    Vector newSources = newPB.getSources();
1200             if(newSources != null && newSources.size() > 0) {
1201                 Iterator it = newSources.iterator();
1202                 while(it.hasNext()) {
1203                     Object src = it.next();
1204                     if(src instanceof PlanarImage) {
1205                         ((PlanarImage)src).removeSinks();
1206                     } else if(src instanceof CollectionImage) {
1207                         ((CollectionImage)src).removeSinks();
1208                     }
1209                 }
1210             }
1211 
1212 	} else if (propName.equals("sources")) {
1213 
1214 	    Vector replacedSources =
1215 		JAIRMIUtil.replaceIdWithSources((Vector)newValue,
1216 						nodes,
1217 						op.getOperationName(),
1218 						op.getRenderingHints());
1219 	    op.setSources(replacedSources);
1220 
1221 	    // Remove the newly created sinks for the replacedSources
1222 	    if(replacedSources != null && replacedSources.size() > 0) {
1223                 Iterator it = replacedSources.iterator();
1224                 while(it.hasNext()) {
1225                     Object src = it.next();
1226                     if(src instanceof PlanarImage) {
1227                         ((PlanarImage)src).removeSinks();
1228                     } else if(src instanceof CollectionImage) {
1229                         ((CollectionImage)src).removeSinks();
1230                     }
1231                 }
1232             }
1233 
1234 
1235 	} else if (propName.equals("parameters")) {
1236 
1237 	    Vector parameters = (Vector)newValue;
1238 	    JAIRMIUtil.checkServerParameters(parameters, nodes);
1239 	    op.setParameters(parameters);
1240 
1241 	} else if (propName.equals("renderinghints")) {
1242 
1243 	    SerializableState newState = (SerializableState)newValue;
1244 	    op.setRenderingHints((RenderingHints)newState.getObject());
1245 	}
1246 
1247 	return id;
1248     }
1249 
1250     /**
1251      * A method that handles a change in one of it's source's rendering,
1252      * i.e. a change that would be signalled by RenderingChangeEvent.
1253      */
handleEvent(Long renderedOpID, int srcIndex, SerializableState srcInvalidRegion, Object oldRendering)1254     public synchronized Long handleEvent(Long renderedOpID,
1255 					 int srcIndex,
1256 					 SerializableState srcInvalidRegion,
1257 					 Object oldRendering)
1258 	throws RemoteException {
1259 
1260 	RenderedOp op = (RenderedOp)nodes.get(renderedOpID);
1261 	PlanarImage rendering = op.getRendering();
1262 
1263 	// Get a new unique ID
1264 	Long id = getRemoteID();
1265 	// Cache the old rendering against the new id
1266 	nodes.put(id, rendering);
1267 
1268 	// Put the op's negotiated result values for its rendering too.
1269 	setServerNegotiatedValues(id, (NegotiableCapabilitySet)
1270 				  negotiated.get(renderedOpID));
1271 
1272 	PlanarImage oldSrcRendering = null, newSrcRendering = null;
1273 	String serverNodeDesc = null;
1274 	Object src = null;
1275 
1276 	if (oldRendering instanceof String) {
1277 
1278 	    serverNodeDesc = (String)oldRendering;
1279 	    int index = serverNodeDesc.indexOf("::");
1280 	    boolean diffServer = index != -1;
1281 
1282 	    if (diffServer) {
1283 		// Create an RMIServerProxy to access the node on a
1284 		// different server
1285 		oldSrcRendering = new RMIServerProxy(serverNodeDesc,
1286 						     op.getOperationName(),
1287 						     op.getRenderingHints());
1288 	    } else {
1289 
1290 		src = nodes.get(Long.valueOf(serverNodeDesc));
1291 
1292 		if (src instanceof RenderedOp) {
1293 		    oldSrcRendering = ((RenderedOp)src).getRendering();
1294 		} else {
1295 		    oldSrcRendering =
1296 			PlanarImage.wrapRenderedImage((RenderedImage)src);
1297 		}
1298 	    }
1299 
1300 	} else {
1301 	    oldSrcRendering =
1302 		PlanarImage.wrapRenderedImage((RenderedImage)oldRendering);
1303 	}
1304 
1305 	Object srcObj = op.getSource(srcIndex);
1306 	if (srcObj instanceof RenderedOp) {
1307 	    newSrcRendering = ((RenderedOp)srcObj).getRendering();
1308 	} else if (srcObj instanceof RenderedImage) {
1309 	    newSrcRendering =
1310 		PlanarImage.wrapRenderedImage((RenderedImage)srcObj);
1311 	}
1312 
1313 	Shape invalidRegion = (Shape)srcInvalidRegion.getObject();
1314 
1315 	RenderingChangeEvent rcEvent =
1316 	    new RenderingChangeEvent((RenderedOp)op.getSource(srcIndex),
1317 				     oldSrcRendering,
1318 				     newSrcRendering,
1319 				     invalidRegion);
1320 	op.propertyChange(rcEvent);
1321 
1322 	return id;
1323     }
1324 
1325     /**
1326      * Returns the server's capabilities. Currently the only capabilities
1327      * that are reported are those dealing with TileCodecs.
1328      */
getServerCapabilities()1329     public synchronized NegotiableCapabilitySet getServerCapabilities() {
1330 
1331 	OperationRegistry registry =
1332 	    JAI.getDefaultInstance().getOperationRegistry();
1333 
1334 	// Note that only the tileEncoder capabilities are returned from
1335 	// this method since there is no way to distinguish between NC's
1336 	// for the encoder and the decoder.
1337 	String modeName = "tileEncoder";
1338 	String[] descriptorNames = registry.getDescriptorNames(modeName);
1339 	TileEncoderFactory tef = null;
1340 
1341 	// Only non-preference NC's can be added.
1342 	NegotiableCapabilitySet capabilities =
1343 	    new NegotiableCapabilitySet(false);
1344 
1345 	Iterator it;
1346 	for (int i=0; i<descriptorNames.length; i++) {
1347 
1348 	    it = registry.getFactoryIterator(modeName, descriptorNames[i]);
1349 	    for (; it.hasNext(); ) {
1350 		tef = (TileEncoderFactory)it.next();
1351 		capabilities.add(tef.getEncodeCapability());
1352 	    }
1353 	}
1354 
1355 	return capabilities;
1356     }
1357 
1358     /**
1359      * Informs the server of the negotiated values that are the result of
1360      * a successful negotiation.
1361      *
1362      * @param negotiatedValues    The result of the negotiation.
1363      */
setServerNegotiatedValues(Long id, NegotiableCapabilitySet negotiatedValues)1364     public void setServerNegotiatedValues(Long id,
1365 					  NegotiableCapabilitySet
1366 					  negotiatedValues)
1367 	throws RemoteException {
1368 	if (negotiatedValues != null)
1369 	    negotiated.put(id, negotiatedValues);
1370 	else
1371 	    negotiated.remove(id);
1372     }
1373 
1374     /**
1375      * Starts a server on a given port.  The RMI registry must be running
1376      * on the server host.
1377      *
1378      * <p> The usage of this class is
1379      *
1380      * <pre>
1381      * java -Djava.rmi.server.codebase=file:$JAI/lib/jai.jar \
1382      * -Djava.rmi.server.useCodebaseOnly=false \
1383      * -Djava.security.policy=\
1384      * file:`pwd`/policy com.lightcrafts.media.jai.rmi.JAIRMIImageServer \
1385      * [-host hostName] [-port portNumber]
1386      * </pre>
1387      *
1388      * The default host is the local host and the default port is 1099.
1389      *
1390      * @param args the port number as a command-line argument.
1391      */
main(String [] args)1392     public static void main(String [] args) {
1393 
1394         // Set the security manager.
1395         if(System.getSecurityManager() == null) {
1396             System.setSecurityManager(new RMISecurityManager());
1397         }
1398 
1399 	// Load all JAIServerConfigurationSpi implementations on the CLASSPATH
1400 	Iterator spiIter = Service.providers(JAIServerConfigurationSpi.class);
1401 	JAI jai = JAI.getDefaultInstance();
1402 
1403 	while (spiIter.hasNext()) {
1404 
1405 	    JAIServerConfigurationSpi serverSpi =
1406 		(JAIServerConfigurationSpi)spiIter.next();
1407 	    serverSpi.updateServer(jai);
1408 	}
1409 
1410         // Set the host name and port number.
1411         String host = null;
1412 	int rmiRegistryPort = 1099; // default port is 1099
1413 	int serverport = 0;
1414 
1415 	if (args.length != 0) {
1416 
1417 	    String value;
1418 
1419 	    for (int i=0; i<args.length; i++) {
1420 
1421 		if (args[i].equalsIgnoreCase("-help")) {
1422 
1423 		    System.out.println("Usage: java -Djava.rmi.server.codebase=file:$JAI/lib/jai.jar \\");
1424 		    System.out.println("-Djava.rmi.server.useCodebaseOnly=false \\");
1425 		    System.out.println("-Djava.security.policy=file:`pwd`/policy \\");
1426 		    System.out.println("com.lightcrafts.media.jai.rmi.JAIRMIImageServer \\");
1427 		    System.out.println("\nwhere options are:");
1428 		    System.out.println("\t-host <string> The server name or server IP address");
1429 		    System.out.println("\t-port <integer> The port that rmiregistry is running on");
1430 		    System.out.println("\t-rmiRegistryPort <integer> Same as -port option");
1431 		    System.out.println("\t-serverPort <integer> The port that the server should listen on, for connections from clients");
1432 		    System.out.println("\t-cacheMemCapacity <long> The memory capacity in bytes.");
1433 		    System.out.println("\t-cacheMemThreshold <float> The memory threshold, which is the fractional amount of cache memory to retain during tile removal");
1434 		    System.out.println("\t-disableDefaultCache Disable use of default tile cache. Tiles are not stored.");
1435 		    System.out.println("\t-schedulerParallelism <integer> The degree of parallelism of the default TileScheduler");
1436 		    System.out.println("\t-schedulerPrefetchParallelism <integer> The degree of parallelism of the default TileScheduler for tile prefetching");
1437 		    System.out.println("\t-schedulerPriority <integer> The priority of tile scheduling for the default TileScheduler");
1438 		    System.out.println("\t-schedulerPrefetchPriority <integer> The priority of tile prefetch scheduling for the default TileScheduler");
1439 		    System.out.println("\t-defaultTileSize <integer>x<integer> The default tile dimensions in the form <xSize>x<ySize>");
1440 		    System.out.println("\t-defaultRenderingSize <integer>x<integer> The default size to render a RenderableImage to, in the form <xSize>x<ySize>");
1441 		    System.out.println("\t-serializeDeepCopy <boolean> Whether a deep copy of the image data should be used when serializing images");
1442 		    System.out.println("\t-tileCodecFormat <string> The default format to be used for tile serialization via TileCodecs");
1443 		    System.out.println("\t-retryInterval <integer> The retry interval value to be used for dealing with network errors during remote imaging");
1444 		    System.out.println("\t-numRetries <integer> The number of retries to be used for dealing with network errors during remote imaging");
1445 
1446 		} else if (args[i].equalsIgnoreCase("-host")) {
1447 
1448 		    host = args[++i];
1449 
1450 		} else if (args[i].equalsIgnoreCase("-port") ||
1451 			   args[i].equalsIgnoreCase("-rmiRegistryPort")) {
1452 
1453 		    rmiRegistryPort = Integer.parseInt(args[++i]);
1454 
1455 		} else if (args[i].equalsIgnoreCase("-serverport")) {
1456 
1457 		    serverport = Integer.parseInt(args[++i]);
1458 
1459 		} else if (args[i].equalsIgnoreCase("-cacheMemCapacity")) {
1460 
1461 		    jai.getTileCache().setMemoryCapacity(
1462 						  Long.parseLong(args[++i]));
1463 
1464 		} else if (args[i].equalsIgnoreCase("-cacheMemThreshold")) {
1465 
1466 		    jai.getTileCache().setMemoryThreshold(
1467 						  Float.parseFloat(args[++i]));
1468 
1469 		} else if (args[i].equalsIgnoreCase("-disableDefaultCache")) {
1470 
1471 		    jai.disableDefaultTileCache();
1472 
1473 		} else if (args[i].equalsIgnoreCase("-schedulerParallelism")) {
1474 
1475 		    jai.getTileScheduler().setParallelism(
1476 						  Integer.parseInt(args[++i]));
1477 
1478 		} else if (args[i].equalsIgnoreCase("-schedulerPrefetchParallelism")) {
1479 
1480 		    jai.getTileScheduler().setPrefetchParallelism(
1481 						  Integer.parseInt(args[++i]));
1482 
1483 		} else if (args[i].equalsIgnoreCase("-schedulerPriority")) {
1484 
1485 		    jai.getTileScheduler().setPriority(
1486 						  Integer.parseInt(args[++i]));
1487 
1488 		} else if (args[i].equalsIgnoreCase("-schedulerPrefetchPriority")) {
1489 
1490 		    jai.getTileScheduler().setPrefetchPriority(
1491 						  Integer.parseInt(args[++i]));
1492 
1493 		} else if (args[i].equalsIgnoreCase("-defaultTileSize")) {
1494 
1495 		    value = args[++i].toLowerCase();
1496 		    int xpos = value.indexOf("x");
1497 		    int xSize = Integer.parseInt(value.substring(0, xpos));
1498 		    int ySize = Integer.parseInt(value.substring(xpos+1));
1499 
1500 		    jai.setDefaultTileSize(new Dimension(xSize, ySize));
1501 
1502 		} else if (args[i].equalsIgnoreCase("-defaultRenderingSize")) {
1503 
1504 		    value = args[++i].toLowerCase();
1505 		    int xpos = value.indexOf("x");
1506 		    int xSize = Integer.parseInt(value.substring(0, xpos));
1507 		    int ySize = Integer.parseInt(value.substring(xpos+1));
1508 
1509 		    jai.setDefaultRenderingSize(new Dimension(xSize, ySize));
1510 
1511 		} else if (args[i].equalsIgnoreCase("-serializeDeepCopy")) {
1512 
1513 		    jai.setRenderingHint(JAI.KEY_SERIALIZE_DEEP_COPY,
1514 					 Boolean.valueOf(args[++i]));
1515 
1516 		} else if (args[i].equalsIgnoreCase("-tileCodecFormat")) {
1517 
1518 		    jai.setRenderingHint(JAI.KEY_TILE_CODEC_FORMAT, args[++i]);
1519 
1520 		} else if (args[i].equalsIgnoreCase("-retryInterval")) {
1521 
1522 		    jai.setRenderingHint(JAI.KEY_RETRY_INTERVAL,
1523 					 Integer.valueOf(args[++i]));
1524 
1525 		} else if (args[i].equalsIgnoreCase("-numRetries")) {
1526 
1527 		    jai.setRenderingHint(JAI.KEY_NUM_RETRIES,
1528 					 Integer.valueOf(args[++i]));
1529 		}
1530 	    }
1531 	}
1532 
1533         // Default to the local host if the host was not specified.
1534         if(host == null) {
1535             try {
1536                 host = InetAddress.getLocalHost().getHostAddress();
1537             } catch(java.net.UnknownHostException e) {
1538                 String message = JaiI18N.getString("RMIImageImpl1");
1539                 sendExceptionToListener(message,
1540                                         new RemoteImagingException(message, e));
1541 /*
1542                 System.err.println(JaiI18N.getString("RMIImageImpl1") +
1543                                    e.getMessage());
1544                 e.printStackTrace();
1545 */
1546             }
1547         }
1548 
1549         System.out.println(JaiI18N.getString("RMIImageImpl3")+" "+
1550                            host + ":" + rmiRegistryPort);
1551 
1552         try {
1553             JAIRMIImageServer im = new JAIRMIImageServer(serverport);
1554             String serverName =
1555 		new String("rmi://" +
1556                            host + ":" + rmiRegistryPort + "/" +
1557                            JAIRMIDescriptor.IMAGE_SERVER_BIND_NAME);
1558             System.out.println(JaiI18N.getString("RMIImageImpl4")+" \""+
1559                                serverName+"\".");
1560             Naming.rebind(serverName, im);
1561             System.out.println(JaiI18N.getString("RMIImageImpl5"));
1562         } catch (Exception e) {
1563             String message = JaiI18N.getString("RMIImageImpl1");
1564             sendExceptionToListener(message,
1565                                    new RemoteImagingException(message, e));
1566 /*
1567             System.err.println(JaiI18N.getString("RMIImageImpl0") +
1568                                e.getMessage());
1569             e.printStackTrace();
1570 */
1571         }
1572     }
1573 
sendExceptionToListener(String message, Exception e)1574     private static void sendExceptionToListener(String message, Exception e) {
1575         ImagingListener listener =
1576             ImageUtil.getImagingListener((RenderingHints)null);
1577         listener.errorOccurred(message,
1578                                new RemoteImagingException(message, e),
1579                                JAIRMIImageServer.class, false);
1580     }
1581 }
1582