001: /*
002: * $RCSfile: PlanarImageServerProxy.java,v $
003: *
004: * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Use is subject to license terms.
007: *
008: * $Revision: 1.1 $
009: * $Date: 2005/02/11 04:57:51 $
010: * $State: Exp $
011: */package javax.media.jai.remote;
012:
013: import java.awt.Image;
014: import java.awt.Rectangle;
015: import java.awt.RenderingHints;
016: import java.awt.image.RenderedImage;
017: import java.awt.image.Raster;
018: import java.awt.image.SampleModel;
019: import java.awt.image.ColorModel;
020: import java.awt.image.renderable.ParameterBlock;
021: import java.io.IOException;
022: import java.util.Vector;
023: import java.util.Hashtable;
024: import javax.media.jai.ImageLayout;
025: import javax.media.jai.JAI;
026: import javax.media.jai.OperationRegistry;
027: import javax.media.jai.PlanarImage;
028: import javax.media.jai.RenderedOp;
029: import javax.media.jai.TileCache;
030: import javax.media.jai.util.ImagingListener;
031: import com.sun.media.jai.util.ImageUtil;
032:
033: /**
034: * A subclass of <code>PlanarImage</code> which represents an image on a
035: * remote server machine. This class is also an implementation of the
036: * <code>RemoteRenderedImage</code> interface. This class allows processing
037: * to occur on the remote server machine.
038: *
039: * <p>Conceptually this class is like a No-op, all it provides is a mechanism
040: * allowing the processing to occur on a server. Note that this class does
041: * not mandate that the client-server communication rely on any particular
042: * wire protocol or communication protocol. A subclass can choose any wire
043: * or communication protocol to communicate with its server. This is
044: * accomplished by having the subclass implement the methods declared to
045: * be abstract in this class. All functionality in this class is then
046: * implemented in terms of these abstract methods.
047: *
048: * <p>Network errors (detected via throws of
049: * <code>RemoteImagingException</code>) are dealt with through the use of
050: * retry intervals and retries. Retries refers to the maximum number of
051: * times a remote operation will be retried. The retry interval refers to
052: * the amount of time in milliseconds between two consecutive retries. If
053: * errors are encountered at each retry and the number of specified retries
054: * has been exhausted, a <code>RemoteImagingException</code> will be thrown.
055: * Time outs (When the amount of time taken to get a response or
056: * the result of an operation from the remote machine exceeds a limit) are
057: * not dealt with, and must be taken care of by the network
058: * imaging protocol implementation. The implementation must be responsible
059: * for monitoring time outs, but on encountering one can deal with it by
060: * throwing a <code>RemoteImagingException</code>, which will then be dealt
061: * with using retries and retry intervals.
062: *
063: * <p> The resultant image layout is computed and provided by the concrete
064: * subclass by implementing the abstract method <code>getImageLayout</code>.
065: * All the accessor methods dealing with the layout variables namely
066: * <code>getMinX()</code>, <code>getMinY()</code>, <code>getWidth()</code>,
067: * <code>getHeight()</code>, <code>getMaxX()</code>, <code>getMaxY()</code>,
068: * <code>getTileWidth()</code>, <code>getTileHeight()</code>,
069: * <code>getTileGridXOffset()</code>, <code>getTileGridYOffset()</code>,
070: * <code>getColorModel()</code> and <code>getSampleModel()</code> are
071: * implemented in terms of the <code>getImageLayout()</code> method. The
072: * implementation of these methods uses retries and retry intervals to
073: * deal with Network errors, such that the subclass implementing
074: * <code>getImageLayout()</code> does not need to worry about Network errors
075: * except to signal them by throwing a <code>RemoteImagingException</code>.
076: * The same applies to the other abstract methods implemented by sub-classes
077: * namely <code>getRemoteProperty()</code>,
078: * <code>getRemotePropertyNames()</code> and <code>computeTile()</code>.
079: *
080: * <p> The <code>getTile</code> method (abstract in this class' superclass),
081: * is implemented in terms of the <code>computeTile</code> method. It provides
082: * the additional functionality of caching the tiles on the client, as well
083: * as that of dealing with Network errors as mentioned above.
084: *
085: * @see RemoteImagingException
086: *
087: * @since JAI 1.1
088: */
089: public abstract class PlanarImageServerProxy extends PlanarImage
090: implements RemoteRenderedImage {
091:
092: /** Time in milliseconds between retries. */
093: protected int retryInterval;
094:
095: /** The number of retries. */
096: protected int numRetries;
097:
098: /** A reference to a centralized TileCache object. */
099: protected transient TileCache cache;
100:
101: /**
102: * Metric used to produce an ordered list of tiles. This determines
103: * which tiles are removed from the cache first if a memory control
104: * operation is required.
105: */
106: protected Object tileCacheMetric;
107:
108: /** A reference to the OperationRegistry object. */
109: protected transient OperationRegistry registry;
110:
111: /** The String representing the remote server machine. */
112: protected String serverName;
113:
114: /** The name of the protocol to be used for remote communication. */
115: protected String protocolName;
116:
117: /** The name of the operation to be performed remotely. */
118: protected String operationName;
119:
120: /** The sources and/or arguments to the operation. */
121: protected ParameterBlock paramBlock;
122:
123: /** The RenderingHints for the operation. */
124: protected RenderingHints hints;
125:
126: // The layout of the image
127: private ImageLayout layout = null;
128:
129: /** The preferences to be utilized in the negotiation. */
130: protected NegotiableCapabilitySet preferences;
131:
132: /**
133: * The set of properties agreed upon after the negotiation process
134: * between the client and the server has been completed.
135: */
136: protected NegotiableCapabilitySet negotiated;
137:
138: /** The server capabilities. */
139: NegotiableCapabilitySet serverCapabilities;
140:
141: /** The client capabilities. */
142: NegotiableCapabilitySet clientCapabilities;
143:
144: /**
145: * Checks that all the layout parameters have been specified.
146: *
147: * @throws IllegalArgumentException if layout is null.
148: * @throws Error if all the layout fields are not initialized.
149: */
150: private static void checkLayout(ImageLayout layout) {
151:
152: if (layout == null) {
153: throw new IllegalArgumentException("layout is null.");
154: }
155:
156: if (layout.getValidMask() != 0x3ff) {
157: throw new Error(JaiI18N
158: .getString("PlanarImageServerProxy3"));
159: }
160: }
161:
162: /**
163: * Constructs a <code>PlanarImageServerProxy</code> using the specified
164: * name of the server to perform the specified operation on, using the
165: * sources and parameters specified by the supplied
166: * <code>ParameterBlock</code> and supplied <code>RenderingHints</code>.
167: * If hints relating to the <code>OperationRegistry</code>,
168: * <code>TileCache</code>, retry interval, number of retries,
169: * tile caching metric or negotiation preferences are included in the
170: * specified <code>RenderingHints</code> object, they will be honored.
171: *
172: * <p> An <code>IllegalArgumentException</code> may
173: * be thrown by the protocol specific classes at a later point, if
174: * null is provided as the serverName argument and null is not
175: * considered a valid server name by the specified protocol.
176: *
177: * @param serverName The <code>String</code> identifying the remote
178: * server machine.
179: * @param protocolName The name of the remote imaging protocol.
180: * @param operationName The name of the operation.
181: * @param paramBlock The source(s) and/or parameter(s) for the operation.
182: * @param hints The hints for the operation.
183: *
184: * @throws IllegalArgumentException if operationName is null.
185: */
186: public PlanarImageServerProxy(String serverName,
187: String protocolName, String operationName,
188: ParameterBlock paramBlock, RenderingHints hints) {
189:
190: // To initialize property and event management stuff, as done by
191: // superclass PlanarImage constructor.
192: super (null, null, null);
193:
194: if (operationName == null) {
195: throw new IllegalArgumentException(JaiI18N
196: .getString("PlanarImageServerProxy1"));
197: }
198:
199: this .serverName = serverName;
200: this .protocolName = protocolName;
201: this .operationName = operationName;
202: this .paramBlock = paramBlock;
203: this .hints = hints;
204:
205: if (hints == null) {
206: // If there are no hints specified, use default values
207: registry = JAI.getDefaultInstance().getOperationRegistry();
208: cache = JAI.getDefaultInstance().getTileCache();
209: retryInterval = RemoteJAI.DEFAULT_RETRY_INTERVAL;
210: numRetries = RemoteJAI.DEFAULT_NUM_RETRIES;
211:
212: // Do negotiation even though there are no preferences, so that
213: // negotiation takes place between the client and the server.
214: setNegotiationPreferences(null);
215: } else {
216:
217: registry = (OperationRegistry) hints
218: .get(JAI.KEY_OPERATION_REGISTRY);
219: if (registry == null) {
220: registry = JAI.getDefaultInstance()
221: .getOperationRegistry();
222: }
223:
224: cache = (TileCache) hints.get(JAI.KEY_TILE_CACHE);
225: if (cache == null) {
226: cache = JAI.getDefaultInstance().getTileCache();
227: }
228:
229: Integer integer = (Integer) hints
230: .get(JAI.KEY_RETRY_INTERVAL);
231: if (integer == null) {
232: retryInterval = RemoteJAI.DEFAULT_RETRY_INTERVAL;
233: } else {
234: retryInterval = integer.intValue();
235: }
236:
237: integer = (Integer) hints.get(JAI.KEY_NUM_RETRIES);
238: if (integer == null) {
239: numRetries = RemoteJAI.DEFAULT_NUM_RETRIES;
240: } else {
241: numRetries = integer.intValue();
242: }
243:
244: tileCacheMetric = (Object) hints
245: .get(JAI.KEY_TILE_CACHE_METRIC);
246:
247: // Cause negotiation to take place.
248: setNegotiationPreferences((NegotiableCapabilitySet) hints
249: .get(JAI.KEY_NEGOTIATION_PREFERENCES));
250: }
251:
252: if (paramBlock != null) {
253: setSources(paramBlock.getSources());
254: }
255: }
256:
257: /**
258: * Returns the <code>String</code> that identifies the server.
259: */
260: public String getServerName() {
261: return serverName;
262: }
263:
264: /**
265: * Returns the <code>String</code> that identifies the remote imaging
266: * protocol.
267: */
268: public String getProtocolName() {
269: return protocolName;
270: }
271:
272: /**
273: * Returns the operation name as a <code>String</code>.
274: */
275: public String getOperationName() {
276: return operationName;
277: }
278:
279: /**
280: * Returns the <code>ParameterBlock</code> that specifies the
281: * sources and parameters for the operation to be performed by
282: * this <code>PlanarImageServerProxy</code>.
283: */
284: public ParameterBlock getParameterBlock() {
285: return paramBlock;
286: }
287:
288: /**
289: * Returns the <code>RenderingHints</code> associated with the
290: * operation to be performed by this <code>PlanarImageServerProxy</code>.
291: */
292: public RenderingHints getRenderingHints() {
293: return hints;
294: }
295:
296: /**
297: * Returns the tile cache object of this image by reference.
298: * If this image does not have a tile cache, this method returns
299: * <code>null</code>.
300: */
301: public TileCache getTileCache() {
302: return cache;
303: }
304:
305: /**
306: * Sets the tile cache object of this image. A <code>null</code>
307: * input indicates that this image should have no tile cache and
308: * subsequently computed tiles will not be cached.
309: *
310: * <p> The existing cache object is informed to release all the
311: * currently cached tiles of this image.
312: *
313: * @param cache A cache object to be used for caching this image's
314: * tiles, or <code>null</code> if no tile caching is desired.
315: */
316: public void setTileCache(TileCache cache) {
317: if (this .cache != null) {
318: this .cache.removeTiles(this );
319: }
320: this .cache = cache;
321: }
322:
323: /**
324: * Returns the <code>tileCacheMetric</code> instance variable by reference.
325: */
326: public Object getTileCacheMetric() {
327: return tileCacheMetric;
328: }
329:
330: /**
331: * Get the layout of the image. This could be implemented by either
332: * asking the server to specify the layout, or have the client compute
333: * the image layout. The <code>ImageLayout</code> object returned must
334: * have all its fields initialized, else an <code>Error</code> will be
335: * thrown. Network errors encountered should be signalled
336: * by throwing a <code>RemoteImagingException</code>.
337: *
338: * @throws RemoteImagingException if an error condition during remote
339: * image processing occurs
340: * @throws Error if all the fields in the ImageLayout are not initialized.
341: */
342: public abstract ImageLayout getImageLayout()
343: throws RemoteImagingException;
344:
345: /**
346: * Returns a property from the property set generated on the remote
347: * server machine. Network errors encountered should be signalled
348: * by throwing a <code>RemoteImagingException</code>. If the property
349: * name is not recognized, java.awt.Image.UndefinedProperty will be
350: * returned.
351: *
352: * @throws RemoteImagingException if an error condition during remote
353: * image processing occurs
354: * @throws IllegalArgumentException if name is null.
355: */
356: public abstract Object getRemoteProperty(String name)
357: throws RemoteImagingException;
358:
359: /**
360: * Returns a list of names recognized by the <code>getRemoteProperty</code>
361: * method. Network errors encountered should be signalled by
362: * throwing a <code>RemoteImagingException</code>.
363: *
364: * @throws RemoteImagingException if an error condition during remote
365: * image processing occurs
366: */
367: public abstract String[] getRemotePropertyNames()
368: throws RemoteImagingException;
369:
370: /**
371: * Returns a conservative estimate of the destination region that
372: * can potentially be affected by the pixels of a rectangle of a
373: * given source. This can be implemented by either asking the server
374: * to compute the destination region, or by having the client compute
375: * the destination region. Network errors encountered should be
376: * signalled by throwing a <code>RemoteImagingException</code>.
377: *
378: * @param sourceRect The <code>Rectangle</code> in source coordinates.
379: * @param sourceIndex The index of the source image.
380: *
381: * @return A <code>Rectangle</code> indicating the potentially
382: * affected destination region, or <code>null</code> if
383: * the region is unknown.
384: *
385: * @throws IllegalArgumentException If the source index is
386: * negative or greater than that of the last source.
387: * @throws IllegalArgumentException If <code>sourceRect</code> is
388: * <code>null</code>.
389: */
390: public abstract Rectangle mapSourceRect(Rectangle sourceRect,
391: int sourceIndex) throws RemoteImagingException;
392:
393: /**
394: * Returns a conservative estimate of the region of a specified
395: * source that is required in order to compute the pixels of a
396: * given destination rectangle. Either the server or the client can
397: * compute the source region to implement this method. Network errors
398: * encountered should be signalled by throwing a
399: * <code>RemoteImagingException</code>.
400: *
401: * @param destRect The <code>Rectangle</code> in destination coordinates.
402: * @param sourceIndex The index of the source image.
403: *
404: * @return A <code>Rectangle</code> indicating the required source region.
405: *
406: * @throws IllegalArgumentException If the source index is
407: * negative or greater than that of the last source.
408: * @throws IllegalArgumentException If <code>destRect</code> is
409: * <code>null</code>.
410: */
411: public abstract Rectangle mapDestRect(Rectangle destRect,
412: int sourceIndex) throws RemoteImagingException;
413:
414: /**
415: * Returns tile (tileX, tileY) as computed on the remote server machine.
416: * Note that tileX and tileY are indices into the tile array, not pixel
417: * locations. The <code>Raster</code> that is returned is a copy.
418: * Network errors encountered should be signalled by throwing a
419: * <code>RemoteImagingException</code>.
420: *
421: * <p> Subclasses must implement this method to return a
422: * non-<code>null</code> value for all tile indices between
423: * <code>getMinTile{X,Y}</code> and <code>getMaxTile{X,Y}</code>,
424: * inclusive. Tile indices outside of this region should result
425: * in a return value of <code>null</code>.
426: *
427: * @param tileX the X index of the requested tile in the tile array.
428: * @param tileY the Y index of the requested tile in the tile array.
429: * @throws RemoteImagingException if an error condition during remote
430: * image processing occurs
431: */
432: public abstract Raster computeTile(int tileX, int tileY)
433: throws RemoteImagingException;
434:
435: /**
436: * Returns the amount of time between retries in milliseconds.
437: */
438: public int getRetryInterval() {
439: return retryInterval;
440: }
441:
442: /**
443: * Sets the amount of time between retries in milliseconds.
444: *
445: * @param retryInterval The amount of time (in milliseconds) to wait
446: * between retries.
447: * @throws IllegalArgumentException if retryInterval is negative.
448: */
449: public void setRetryInterval(int retryInterval) {
450: if (retryInterval < 0) {
451: throw new IllegalArgumentException(JaiI18N
452: .getString("Generic3"));
453: }
454: this .retryInterval = retryInterval;
455: }
456:
457: /**
458: * Returns the number of retries.
459: */
460: public int getNumRetries() {
461: return numRetries;
462: }
463:
464: /**
465: * Sets the number of retries.
466: *
467: * @param numRetries The number of times an operation should be retried
468: * in case of a network error.
469: * @throws IllegalArgumentException if numRetries is negative.
470: */
471: public void setNumRetries(int numRetries) {
472: if (numRetries < 0) {
473: throw new IllegalArgumentException(JaiI18N
474: .getString("Generic4"));
475: }
476: this .numRetries = numRetries;
477: }
478:
479: /**
480: * Overrides the method in <code>PlanarImage</code> to return the X
481: * coordinate of the leftmost column of the remote image.
482: */
483: public int getMinX() {
484: requestLayout();
485: return minX;
486: }
487:
488: /**
489: * Overrides the method in <code>PlanarImage</code> to return the X
490: * coordinate of the column immediately to the right of the rightmost
491: * column of the remote image.
492: */
493: public int getMaxX() {
494: requestLayout();
495: return minX + width;
496: }
497:
498: /**
499: * Overrides the method in <code>PlanarImage</code> to return the Y
500: * coordinate of the uppermost row of the remote image.
501: */
502: public int getMinY() {
503: requestLayout();
504: return minY;
505: }
506:
507: /**
508: * Overrides the method in <code>PlanarImage</code> to return the Y
509: * coordinate of the row immediately below the bottom row of the
510: * remote image.
511: */
512: public int getMaxY() {
513: requestLayout();
514: return minY + height;
515: }
516:
517: /**
518: * Overrides the method in <code>PlanarImage</code> to return the width
519: * of the remote image.
520: */
521: public int getWidth() {
522: requestLayout();
523: return width;
524: }
525:
526: /**
527: * Overrides the method in <code>PlanarImage</code> to return the height
528: * of the remote image.
529: */
530: public int getHeight() {
531: requestLayout();
532: return height;
533: }
534:
535: /**
536: * Overrides the method in <code>PlanarImage</code> to return the width
537: * of a tile remotely.
538: */
539: public int getTileWidth() {
540: requestLayout();
541: return tileWidth;
542: }
543:
544: /**
545: * Overrides the method in <code>PlanarImage</code> to return the height
546: * of a tile remotely.
547: */
548: public int getTileHeight() {
549: requestLayout();
550: return tileHeight;
551: }
552:
553: /**
554: * Overrides the method in <code>PlanarImage</code> to return the X
555: * coordinate of the upper-left pixel of tile (0, 0) remotely.
556: */
557: public int getTileGridXOffset() {
558: requestLayout();
559: return tileGridXOffset;
560: }
561:
562: /**
563: * Overrides the method in <code>PlanarImage</code> to return the Y
564: * coordinate of the upper-left pixel of tile (0, 0) remotely.
565: */
566: public int getTileGridYOffset() {
567: requestLayout();
568: return tileGridYOffset;
569: }
570:
571: /**
572: * Overrides the method in <code>PlanarImage</code> to return the
573: * <code>SampleModel</code> of the remote image.
574: */
575: public SampleModel getSampleModel() {
576: requestLayout();
577: return sampleModel;
578: }
579:
580: /**
581: * Overrides the method in <code>PlanarImage</code> to return the
582: * <code>ColorModel</code> of the remote image.
583: */
584: public ColorModel getColorModel() {
585: requestLayout();
586: return colorModel;
587: }
588:
589: /**
590: * Cause the subclass' <code>getImageLayout</code> method to be called,
591: * caching the <code>ImageLayout</code> object locally.
592: *
593: * @throws IllegalArgumentException if getImageLayout returns null.
594: * @throws Error if all the fields of the layout are not initialized.
595: * @throws RemoteImagingException if the limit of retries is exceeded.
596: */
597: private ImageLayout requestLayout() {
598:
599: if (layout != null)
600: return layout;
601:
602: Exception rieSave = null;
603: int count = 0;
604: while (count++ < numRetries) {
605: try {
606: layout = getImageLayout();
607: // Check that all the fields are initialized
608: checkLayout(layout);
609:
610: // Set all the super class variables
611: minX = layout.getMinX(null);
612: minY = layout.getMinY(null);
613: width = layout.getWidth(null);
614: height = layout.getHeight(null);
615: tileWidth = layout.getTileWidth(null);
616: tileHeight = layout.getTileHeight(null);
617: tileGridXOffset = layout.getTileGridXOffset(null);
618: tileGridYOffset = layout.getTileGridYOffset(null);
619: sampleModel = layout.getSampleModel(null);
620: colorModel = layout.getColorModel(null);
621: break;
622: } catch (RemoteImagingException e) {
623: System.err.println(JaiI18N
624: .getString("PlanarImageServerProxy0"));
625: rieSave = e;
626: // Sleep for retryInterval milliseconds before retrying.
627: try {
628: Thread.sleep(retryInterval);
629: } catch (InterruptedException f) {
630: }
631: }
632: }
633:
634: if (layout == null) {
635: sendExceptionToListener(rieSave);
636: }
637:
638: return layout;
639: }
640:
641: /**
642: * Gets a property from the property set of this image. If the
643: * property name is not recognized,
644: * <code>java.awt.Image.UndefinedProperty</code> will be returned.
645: * The property to be returned is first looked for in the set of
646: * locally cached properties. If not found, the
647: * <code>getRemoteProperty</code> method is called to retrieve the
648: * property. Network errors that might be encountered during the
649: * <code>getRemoteProperty</code> call are dealt with by retries and
650: * retry intervals.
651: *
652: * @param name the name of the property to get, as a <code>String</code>.
653: * @return a reference to the property <code>Object</code>, or the value
654: * <code>java.awt.Image.UndefinedProperty</code>.
655: *
656: * @throws RemoteImagingException if the limit of retries is exceeded.
657: */
658: public Object getProperty(String name) {
659:
660: // Try to get property locally.
661: Object property = super .getProperty(name);
662:
663: if (property == null || property == Image.UndefinedProperty) {
664:
665: Exception rieSave = null;
666: int count = 0;
667: while (count++ < numRetries) {
668: try {
669: property = getRemoteProperty(name);
670: if (property != Image.UndefinedProperty) {
671: setProperty(name, property); // Cache property locally
672: }
673: return property;
674: } catch (RemoteImagingException rie) {
675: System.err.println(JaiI18N
676: .getString("PlanarImageServerProxy0"));
677: rieSave = rie;
678: try {
679: Thread.sleep(retryInterval);
680: } catch (InterruptedException ie) {
681: }
682: }
683: }
684:
685: sendExceptionToListener(rieSave);
686: return property;
687: } else {
688: return property;
689: }
690: }
691:
692: /**
693: * Returns a list of property names that are recognized by this image
694: * or <code>null</code> if none are recognized. The list of recognized
695: * property names consists of the locally cached property names
696: * (retrieved via <code>super.getPropertyNames</code>) as well as
697: * those that might be generated by the operations performed on the
698: * remote server machine (retrieved via
699: * <code>getRemotePropertyNames</code>). Network errors that might be
700: * encountered during the <code>getRemotePropertyNames</code> method
701: * are dealt with by retries and retry intervals.
702: *
703: * @return an array of <code>String</code>s containing valid
704: * property names.
705: *
706: * @throws RemoteImagingException if the limit of retries is exceeded.
707: */
708: public String[] getPropertyNames() {
709:
710: // Retrieve local property names.
711: String[] localPropertyNames = super .getPropertyNames();
712:
713: Vector names = new Vector();
714: // Put local names in a Vector, if any
715: if (localPropertyNames != null) {
716: for (int i = 0; i < localPropertyNames.length; i++) {
717: names.add(localPropertyNames[i]);
718: }
719: }
720:
721: int count = 0;
722: String[] remotePropertyNames = null;
723: Exception rieSave = null;
724:
725: while (count++ < numRetries) {
726: try {
727: remotePropertyNames = getRemotePropertyNames();
728: break;
729: } catch (RemoteImagingException rie) {
730: System.err.println(JaiI18N
731: .getString("PlanarImageServerProxy0"));
732: rieSave = rie;
733: try {
734: Thread.sleep(retryInterval);
735: } catch (InterruptedException ie) {
736: }
737: }
738: }
739:
740: if (count > numRetries) {
741: sendExceptionToListener(rieSave);
742: }
743:
744: // Put the remote names, if any, in the Vector.
745: if (remotePropertyNames != null) {
746: for (int i = 0; i < remotePropertyNames.length; i++) {
747: if (!names.contains(remotePropertyNames[i])) {
748: names.add(remotePropertyNames[i]);
749: }
750: }
751: }
752:
753: // Set the return value from the vector.
754: String propertyNames[] = names.size() == 0 ? null
755: : (String[]) names.toArray(new String[names.size()]);
756:
757: return propertyNames;
758: }
759:
760: /**
761: * Returns the tile (tileX, tileY). Note the tileX and tileY are indices
762: * into the tile array and not pixel positions. This method is
763: * implemented in terms of the <code>computeTile</code> method. This
764: * method deals with Network errors (recognized as
765: * <code>RemoteImagingExceptions</code>) through retries and retry
766: * intervals. This method also performs caching of tiles, so that
767: * an already computed tile does not need to be re-computed.
768: *
769: * @param tileX the X index of the tile.
770: * @param tileY the Y index of the tile.
771: * @throws RemoteImagingException if limit of retries is exceeded.
772: */
773: public Raster getTile(int tileX, int tileY) {
774:
775: Raster tile = null;
776:
777: // Make sure the requested tile is inside this image's boundary.
778: if (tileX >= getMinTileX() && tileX <= getMaxTileX()
779: && tileY >= getMinTileY() && tileY <= getMaxTileY()) {
780:
781: // Check if tile is available in the cache.
782: tile = cache != null ? cache.getTile(this , tileX, tileY)
783: : null;
784:
785: if (tile == null) { // tile not in cache
786: // Ask the subclass for the tile
787: int count = 0;
788: Exception rieSave = null;
789: while (count++ < numRetries) {
790: try {
791: tile = computeTile(tileX, tileY);
792: break;
793: } catch (RemoteImagingException rie) {
794: System.err.println(JaiI18N
795: .getString("PlanarImageServerProxy0"));
796: rieSave = rie;
797: try {
798: Thread.sleep(retryInterval);
799: } catch (InterruptedException ie) {
800:
801: }
802: }
803: }
804:
805: if (count > numRetries) {
806: sendExceptionToListener(rieSave);
807: }
808:
809: // Cache the result tile.
810: if (cache != null) {
811: cache
812: .add(this , tileX, tileY, tile,
813: tileCacheMetric);
814: }
815: }
816: }
817:
818: return tile;
819: }
820:
821: /**
822: * Uncaches all the tiles when this image is garbage collected.
823: */
824: protected void finalize() throws Throwable {
825: if (cache != null) {
826: cache.removeTiles(this );
827: }
828: super .finalize();
829: }
830:
831: //
832: // NEGOTIATION RELATED METHODS
833: //
834:
835: /**
836: * Returns the current negotiation preferences or null, if none were
837: * set previously.
838: */
839: public NegotiableCapabilitySet getNegotiationPreferences() {
840: return preferences;
841: }
842:
843: /**
844: * Sets the preferences to be used in the client-server
845: * communication. These preferences are utilized in the negotiation
846: * process. Note that preferences for more than one category can be
847: * specified using this method. Also each preference can be a list
848: * of values in decreasing order of preference, each value specified
849: * as a <code>NegotiableCapability</code>. The
850: * <code>NegotiableCapability</code> first (for a particular category)
851: * in this list is given highest priority in the negotiation process
852: * (for that category).
853: *
854: * <p> It may be noted that this method allows for multiple negotiation
855: * cycles. Everytime this method is called, new preferences are
856: * specified for the negotiation, which takes place anew to produce
857: * a new set of negotiated resultant values to be used in the
858: * remote communication. If the subclass wants to ignore the
859: * negotiation preferences newly set, this method can be overridden to
860: * do so.
861: *
862: * @param preferences The preferences to be used in the negotiation
863: * process.
864: */
865: public void setNegotiationPreferences(
866: NegotiableCapabilitySet preferences) {
867: this .preferences = preferences;
868:
869: // Every time new preferences are set, invalidate old Negotiation
870: // results and do the negotiation again.
871: negotiated = null;
872:
873: getNegotiatedValues();
874: }
875:
876: /**
877: * Returns the results of the negotiation process. This will return null
878: * if no negotiation preferences were set, and no negotiation was
879: * performed, or if the negotiation failed.
880: */
881: public synchronized NegotiableCapabilitySet getNegotiatedValues()
882: throws RemoteImagingException {
883:
884: // If negotiation was not performed before, or if new preferences
885: // have invalidated the old negotiated results.
886: if (negotiated == null) {
887:
888: // Initialize the clientCapabilities and serverCapabilities
889: // variables
890: getCapabilities();
891:
892: // Do the negotiation
893: negotiated = RemoteJAI.negotiate(preferences,
894: serverCapabilities, clientCapabilities);
895:
896: // Inform the server of the negotiated values.
897: setServerNegotiatedValues(negotiated);
898: }
899:
900: return negotiated;
901: }
902:
903: /**
904: * Returns the results of the negotiation process for the given category.
905: * This will return null if no negotiation preferences were set, and
906: * no negotiation was performed, or if the negotiation failed.
907: *
908: * @param category The category to return the negotiated results for.
909: * @throws IllegalArgumentException if category is null.
910: */
911: public NegotiableCapability getNegotiatedValue(String category)
912: throws RemoteImagingException {
913:
914: // We do not need to check for category being null, since that
915: // check will be made by the methods called from within this method.
916:
917: // If negotiation was not performed before, or if new preferences
918: // have invalidated the old negotiated results.
919: if (negotiated == null) {
920:
921: getCapabilities();
922:
923: // Do the negotiation
924: return RemoteJAI.negotiate(preferences, serverCapabilities,
925: clientCapabilities, category);
926: } else {
927: // If negotiated is not null, then the negotiated results are
928: // current and the result for the given category can just be
929: // extracted from there and returned.
930: return negotiated.getNegotiatedValue(category);
931: }
932: }
933:
934: // Get the client and server capabilities
935: private void getCapabilities() {
936:
937: String mode = "remoteRendered";
938: if (serverCapabilities == null) {
939:
940: RemoteDescriptor desc = (RemoteDescriptor) registry
941: .getDescriptor(mode, protocolName);
942:
943: int count = 0;
944: Exception rieSave = null;
945: while (count++ < numRetries) {
946: try {
947: serverCapabilities = desc
948: .getServerCapabilities(serverName);
949: break;
950: } catch (RemoteImagingException rie) {
951: System.err.println(JaiI18N
952: .getString("PlanarImageServerProxy0"));
953: rieSave = rie;
954: try {
955: Thread.sleep(retryInterval);
956: } catch (InterruptedException ie) {
957: }
958: }
959: }
960:
961: if (count > numRetries) {
962: sendExceptionToListener(rieSave);
963: }
964: }
965:
966: if (clientCapabilities == null) {
967: RemoteRIF rrif = (RemoteRIF) registry.getFactory(mode,
968: protocolName);
969:
970: clientCapabilities = rrif.getClientCapabilities();
971: }
972: }
973:
974: void sendExceptionToListener(Exception e) {
975: ImagingListener listener = null;
976: if (hints != null)
977: listener = (ImagingListener) hints
978: .get(JAI.KEY_IMAGING_LISTENER);
979: else
980: listener = JAI.getDefaultInstance().getImagingListener();
981: String message = JaiI18N.getString("PlanarImageServerProxy2");
982: listener.errorOccurred(message, new RemoteImagingException(
983: message, e), this , false);
984: }
985: }
|