0001: /*
0002: * $RCSfile: RemoteRenderedOp.java,v $
0003: *
0004: * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * Use is subject to license terms.
0007: *
0008: * $Revision: 1.2 $
0009: * $Date: 2006/06/16 22:52:05 $
0010: * $State: Exp $
0011: */package javax.media.jai.remote;
0012:
0013: import java.awt.Point;
0014: import java.awt.Rectangle;
0015: import java.awt.RenderingHints;
0016: import java.awt.Shape;
0017: import java.awt.geom.Area;
0018: import java.awt.geom.GeneralPath;
0019: import java.awt.image.Raster;
0020: import java.awt.image.RenderedImage;
0021: import java.awt.image.renderable.ParameterBlock;
0022: import java.beans.PropertyChangeEvent;
0023: import java.beans.PropertyChangeListener;
0024: import java.util.ArrayList;
0025: import java.util.Collection;
0026: import java.util.HashSet;
0027: import java.util.Locale;
0028: import java.util.Set;
0029: import java.util.Vector;
0030: import java.text.MessageFormat;
0031: import javax.media.jai.CollectionChangeEvent;
0032: import javax.media.jai.CollectionOp;
0033: import javax.media.jai.JAI;
0034: import javax.media.jai.OperationRegistry;
0035: import javax.media.jai.PlanarImage;
0036: import javax.media.jai.PropertyChangeEventJAI;
0037: import javax.media.jai.PropertySourceChangeEvent;
0038: import javax.media.jai.RegistryMode;
0039: import javax.media.jai.RenderedOp;
0040: import javax.media.jai.RenderingChangeEvent;
0041: import javax.media.jai.TileCache;
0042: import javax.media.jai.registry.RemoteRIFRegistry;
0043: import javax.media.jai.util.ImagingException;
0044: import javax.media.jai.util.ImagingListener;
0045: import com.sun.media.jai.util.ImageUtil;
0046:
0047: /**
0048: * A node in a remote rendered imaging chain. This class is a concrete
0049: * implementation of the <code>RemoteRenderedImage</code> interface. A
0050: * <code>RemoteRenderedOp</code> stores a protocol name (as a
0051: * <code>String</code>), a server name (as a <code>String</code>), an
0052: * operation name (as a <code>String</code>), a
0053: * <code>ParameterBlock</code> containing sources and miscellaneous
0054: * parameters, and a <code>RenderingHints</code> containing rendering
0055: * hints. A set of nodes may be joined together via the source
0056: * <code>Vector</code>s within their <code>ParameterBlock</code>s to
0057: * form a <u>d</u>irected <u>a</u>cyclic <u>g</u>raph (DAG). The topology
0058: * i.e., connectivity of the graph may be altered by changing the
0059: * <code>ParameterBlock</code>s; the operation name, parameters, and
0060: * rendering hints may also be changed.
0061: *
0062: * <p> Such chains represent and handle operations that are being
0063: * performed remotely. They convey the structure of an imaging
0064: * chain in a compact representation and can be used to influence the
0065: * remote imaging process (through the use of retry interval, retries and
0066: * negotiation preferences).
0067: *
0068: * <p> <code>RemoteRenderedOp</code>s are a client side representation of
0069: * the chain of operations taking place on the server.
0070: *
0071: * <p> The translation between <code>RemoteRenderedOp</code> chains and
0072: * <code>RemoteRenderedImage</code> (usually
0073: * <code>PlanarImageServerProxy</code>) chains makes use of two levels of
0074: * indirection provided by the <code>OperationRegistry</code> and
0075: * <code>RemoteRIF</code> facilities. First, the
0076: * local <code>OperationRegistry</code> is used to map the protocol
0077: * name into a <code>RemoteRIF</code>. This <code>RemoteRIF</code> then
0078: * constructs one or more <code>RemoteRenderedImage</code>s (usually
0079: * <code>PlanarImageServerProxy</code>s) to do the actual work (or
0080: * returns a <code>RemoteRenderedImage</code> by other means. The
0081: * <code>OperationRegistry</code> maps a protocol name into a
0082: * <code>RemoteRIF</code>, since there is one to one correspondence
0083: * between a protocol name and a <code>RemoteRIF</code>. This differs from
0084: * the case of <code>RenderedOp</code>s, where the
0085: * <code>OperationRegistry</code> maps each operation name to a
0086: * <code>RenderedImageFactory</code> (RIF), since there is a one to one
0087: * correspondence between an operation name and a RIF. The
0088: * <code>RemoteRIF</code>s are therefore protocol-specific and not operation
0089: * specific, while a RIF is operation specific.
0090: *
0091: * <p> Once a protocol name has been mapped into a <code>RemoteRIF</code>,
0092: * the <code>RemoteRIF.create()</code> method is used to create a rendering.
0093: * This rendering is responsible for communicating with the server to
0094: * perform the specified operation remotely.
0095: *
0096: * <p> By virtue of being a subclass of <code>RenderedOp</code>, this class
0097: * participates in Java Bean-style events as specified by
0098: * <code>RenderedOp</code>. This means that <code>PropertyChangeEmitter</code>
0099: * methods may be used to register and unregister
0100: * <code>PropertyChangeListener</code>s. <code>RemoteRenderedOp</code>s
0101: * are also <code>PropertyChangeListener</code>s so that they may be
0102: * registered as listeners of other <code>PropertyChangeEmitter</code>s
0103: * or the equivalent. Each <code>RemoteRenderedOp</code> also automatically
0104: * receives any <code>RenderingChangeEvent</code>s emitted by any of its
0105: * sources which are <code>RenderedOp</code>s.
0106: *
0107: * <p> <code>RemoteRenderedOp</code>s add the server name and the protocol
0108: * name to the critical attributes, the editing (changing) of which,
0109: * coupled with a difference in the old and new rendering over some
0110: * non-empty region, may cause a <code>RenderingChangeEvent</code> to
0111: * be emitted. As with <code>RenderedOp</code>, editing of a critical
0112: * attribute of a <code>RemoteRenderedOp</code> will cause a
0113: * <code>PropertyChangeEventJAI</code> detailing the change to be fired
0114: * to all registered <code>PropertyChangeListener</code>s.
0115: * <code>RemoteRenderedOp</code> registers itself as a
0116: * <code>PropertyChangeListener</code> for all critical attributes, and
0117: * thus receives all <code>PropertyChangeEventJAI</code> events generated
0118: * by itself. This is done in order to allow the event handling code
0119: * to generate a new rendering and reuse any tiles that might be valid
0120: * after the critical argument change.
0121: *
0122: * <p> When a <code>RemoteRenderedOp</code> node receives a
0123: * <code>PropertyChangeEventJAI</code> from itself, the region of
0124: * the current rendering which is invalidated is computed using
0125: * <code>RemoteDescriptor.getInvalidRegion()</code>. When a
0126: * <code>RemoteRenderedOp</code> node receives a
0127: * <code>RenderingChangeEvent</code> from one of its sources, the region of
0128: * the current rendering which is invalidated is computed using
0129: * the <code>mapSourceRect()</code> method of the current rendering and
0130: * the invalid region of the source (retrieved using
0131: * <code>RenderingChangeEvent.getInvalidRegion()</code>)
0132: * If the complement of the invalid region contains any tiles of the
0133: * current rendering, a new rendering of the node will be generated using
0134: * the new source node and its rendering generated using that version of
0135: * <code>RemoteRIF.create</code>() that updates the rendering of the node
0136: * according to the specified <code>PropertyChangeEventJAI</code>. The
0137: * identified tiles will be retained from the old rendering insofar as
0138: * possible. This might involve for example adding tiles to a
0139: * <code>TileCache</code> under the ownership of the new rendering.
0140: * A <code>RenderingChangeEvent</code> will then be fired to all
0141: * <code>PropertyChangeListener</code>s of the node, and to any node sinks
0142: * that are <code>PropertyChangeListener</code>s. The
0143: * <code>newRendering</code> parameter of the event constructor
0144: * (which may be retrieved via the <code>getNewValue()</code> method of
0145: * the event) will be set to either the new rendering of the node or to
0146: * <code>null</code> if it was not possible to retain any tiles of the
0147: * previous rendering.
0148: *
0149: * @see RenderedOp
0150: * @see RemoteRenderedImage
0151: *
0152: * @since JAI 1.1
0153: */
0154: public class RemoteRenderedOp extends RenderedOp implements
0155: RemoteRenderedImage {
0156:
0157: /** The name of the protocol this class provides an implementation for. */
0158: protected String protocolName;
0159:
0160: /** The name of the server. */
0161: protected String serverName;
0162:
0163: // The NegotiableCapabilitySet representing the negotiated values.
0164: private NegotiableCapabilitySet negotiated;
0165:
0166: /**
0167: * The RenderingHints when the node was last rendered, i.e., when
0168: * "theImage" was set to its current value.
0169: */
0170: private transient RenderingHints oldHints;
0171:
0172: /** Node event names. */
0173: private static Set nodeEventNames = null;
0174:
0175: static {
0176: nodeEventNames = new HashSet();
0177: nodeEventNames.add("protocolname");
0178: nodeEventNames.add("servername");
0179: nodeEventNames.add("protocolandservername");
0180: nodeEventNames.add("operationname");
0181: nodeEventNames.add("operationregistry");
0182: nodeEventNames.add("parameterblock");
0183: nodeEventNames.add("sources");
0184: nodeEventNames.add("parameters");
0185: nodeEventNames.add("renderinghints");
0186: }
0187:
0188: /**
0189: * Constructs a <code>RemoteRenderedOp</code> that will be used to
0190: * instantiate a particular rendered operation to be performed remotely
0191: * using the default operation registry, the name of the remote imaging
0192: * protocol, the name of the server to perform the operation on, an
0193: * operation name, a <code>ParameterBlock</code>, and a set of
0194: * rendering hints. All input parameters are saved by reference.
0195: *
0196: * <p> An <code>IllegalArgumentException</code> may
0197: * be thrown by the protocol specific classes at a later point, if
0198: * null is provided as the serverName argument and null is not
0199: * considered a valid server name by the specified protocol.
0200: *
0201: * <p> The <code>RenderingHints</code> may contain negotiation
0202: * preferences specified under the <code>KEY_NEGOTIATION_PREFERENCES</code>
0203: * key.
0204: *
0205: * @param protocolName The protocol name as a String.
0206: * @param serverName The server name as a String.
0207: * @param opName The operation name.
0208: * @param pb The sources and parameters. If <code>null</code>,
0209: * it is assumed that this node has no sources and
0210: * parameters.
0211: * @param hints The rendering hints. If <code>null</code>, it is
0212: * assumed that no hints are associated with the
0213: * rendering.
0214: *
0215: * @throws IllegalArgumentException if <code>protocolName</code> is
0216: * <code>null</code>.
0217: * @throws IllegalArgumentException if <code>opName</code> is
0218: * <code>null</code>.
0219: */
0220: public RemoteRenderedOp(String protocolName, String serverName,
0221: String opName, ParameterBlock pb, RenderingHints hints) {
0222: this (null, protocolName, serverName, opName, pb, hints);
0223: }
0224:
0225: /**
0226: * Constructs a <code>RemoteRenderedOp</code> that will be used to
0227: * instantiate a particular rendered operation to be performed remotely
0228: * using the specified operation registry, the name of the remote imaging
0229: * protocol, the name of the server to perform the operation on, an
0230: * operation name, a <code>ParameterBlock</code>, and a set of
0231: * rendering hints. All input parameters are saved by reference.
0232: *
0233: * <p> An <code>IllegalArgumentException</code> may
0234: * be thrown by the protocol specific classes at a later point, if
0235: * null is provided as the serverName argument and null is not
0236: * considered a valid server name by the specified protocol.
0237: *
0238: * <p> The <code>RenderingHints</code> may contain negotiation
0239: * preferences specified under the <code>KEY_NEGOTIATION_PREFERENCES</code>
0240: * key.
0241: *
0242: * @param registry The <code>OperationRegistry</code> to be used for
0243: * instantiation. if <code>null</code>, the default
0244: * registry is used.
0245: * @param protocolName The protocol name as a String.
0246: * @param serverName The server name as a String.
0247: * @param opName The operation name.
0248: * @param pb The sources and parameters. If <code>null</code>,
0249: * it is assumed that this node has no sources and
0250: * parameters.
0251: * @param hints The rendering hints. If <code>null</code>, it is
0252: * assumed that no hints are associated with the
0253: * rendering.
0254: *
0255: * @throws IllegalArgumentException if <code>protocolName</code> is
0256: * <code>null</code>.
0257: * @throws IllegalArgumentException if <code>opName</code> is
0258: * <code>null</code>.
0259: */
0260: public RemoteRenderedOp(OperationRegistry registry,
0261: String protocolName, String serverName, String opName,
0262: ParameterBlock pb, RenderingHints hints) {
0263:
0264: // This will throw IAE for opName if null
0265: super (registry, opName, pb, hints);
0266:
0267: if (protocolName == null)
0268: throw new IllegalArgumentException(JaiI18N
0269: .getString("Generic1"));
0270:
0271: this .protocolName = protocolName;
0272: this .serverName = serverName;
0273:
0274: // Add the node as a PropertyChangeListener of itself for
0275: // the critical attributes of the node. Superclass RenderedOp
0276: // takes care of all critical attributes except the following.
0277: // Case is ignored in the property names but infix caps are
0278: // used here anyway.
0279: addPropertyChangeListener("ServerName", this );
0280: addPropertyChangeListener("ProtocolName", this );
0281: addPropertyChangeListener("ProtocolAndServerName", this );
0282: }
0283:
0284: /**
0285: * Returns the <code>String</code> that identifies the server.
0286: */
0287: public String getServerName() {
0288: return serverName;
0289: }
0290:
0291: /**
0292: * Sets a <code>String</code> identifying the server.
0293: *
0294: * <p> If the supplied name does not equal the current server name, a
0295: * <code>PropertyChangeEventJAI</code> named "ServerName"
0296: * will be fired and a <code>RenderingChangeEvent</code> may be
0297: * fired if the node has already been rendered. The oldValue
0298: * field in the <code>PropertyChangeEventJAI</code> will contain
0299: * the old server name <code>String</code> and the newValue
0300: * field will contain the new server name <code>String</code>.
0301: *
0302: * @param serverName A <code>String</code> identifying the server.
0303: * @throws IllegalArgumentException if serverName is null.
0304: */
0305: public void setServerName(String serverName) {
0306:
0307: if (serverName == null)
0308: throw new IllegalArgumentException(JaiI18N
0309: .getString("Generic2"));
0310:
0311: if (serverName.equalsIgnoreCase(this .serverName))
0312: return;
0313:
0314: String oldServerName = this .serverName;
0315: this .serverName = serverName;
0316: fireEvent("ServerName", oldServerName, serverName);
0317: nodeSupport.resetPropertyEnvironment(false);
0318: }
0319:
0320: /**
0321: * Returns the <code>String</code> that identifies the remote imaging
0322: * protocol.
0323: */
0324: public String getProtocolName() {
0325: return protocolName;
0326: }
0327:
0328: /**
0329: * Sets a <code>String</code> identifying the remote imaging protocol.
0330: * This method causes this <code>RemoteRenderedOp</code> to use
0331: * the new protocol name with the server name set on this node
0332: * previously. If the server is not compliant with the new
0333: * protocol name, the <code>setProtocolAndServerNames()</code>
0334: * method should be used to set a new protocol name and a compliant
0335: * new server name at the same time.
0336: *
0337: * <p> If the supplied name does not equal the current protocol name, a
0338: * <code>PropertyChangeEventJAI</code> named "ProtocolName"
0339: * will be fired and a <code>RenderingChangeEvent</code> may be
0340: * fired if the node has already been rendered. The oldValue
0341: * field in the <code>PropertyChangeEventJAI</code> will contain
0342: * the old protocol name <code>String</code> and the newValue
0343: * field will contain the new protocol name <code>String</code>.
0344: *
0345: * @param protocolName A <code>String</code> identifying the server.
0346: * @throws IllegalArgumentException if protocolName is null.
0347: */
0348: public void setProtocolName(String protocolName) {
0349:
0350: if (protocolName == null)
0351: throw new IllegalArgumentException(JaiI18N
0352: .getString("Generic1"));
0353:
0354: if (protocolName.equalsIgnoreCase(this .protocolName))
0355: return;
0356:
0357: String oldProtocolName = this .protocolName;
0358: this .protocolName = protocolName;
0359: fireEvent("ProtocolName", oldProtocolName, protocolName);
0360: nodeSupport.resetPropertyEnvironment(false);
0361: }
0362:
0363: /**
0364: * Sets the protocol name and the server name of this
0365: * <code>RemoteRenderedOp</code> to the specified arguments..
0366: *
0367: * <p> If both the supplied protocol name and the supplied server
0368: * name values do not equal the current values, a
0369: * <code>PropertyChangeEventJAI</code> named "ProtocolAndServerName"
0370: * will be fired. The oldValue field in the
0371: * <code>PropertyChangeEventJAI</code> will contain a two element
0372: * array of <code>String</code>s, the old protocol name being the
0373: * first element and the old server name being the second. Similarly
0374: * the newValue field of the <code>PropertyChangeEventJAI</code> will
0375: * contain a two element array of <code>String</code>s, the new protocol
0376: * name being the first element and the new server name being the
0377: * second. If only the supplied protocol name does not equal
0378: * the current protocol name, a <code>PropertyChangeEventJAI</code>
0379: * named "ProtocolName" will be fired. If only the supplied server
0380: * name does not equal the current server name, a
0381: * <code>PropertyChangeEventJAI</code> named "ServerName"
0382: * will be fired.
0383: *
0384: * @param protocolName A <code>String</code> identifying the protocol.
0385: * @param serverName A <code>String</code> identifying the server.
0386: * @throws IllegalArgumentException if protocolName is null.
0387: * @throws IllegalArgumentException if serverName is null.
0388: */
0389: public void setProtocolAndServerNames(String protocolName,
0390: String serverName) {
0391:
0392: if (serverName == null)
0393: throw new IllegalArgumentException(JaiI18N
0394: .getString("Generic2"));
0395:
0396: if (protocolName == null)
0397: throw new IllegalArgumentException(JaiI18N
0398: .getString("Generic1"));
0399:
0400: boolean protocolNotChanged = protocolName
0401: .equalsIgnoreCase(this .protocolName);
0402: boolean serverNotChanged = serverName
0403: .equalsIgnoreCase(this .serverName);
0404:
0405: if (protocolNotChanged) {
0406: if (serverNotChanged)
0407: // Neither changed
0408: return;
0409: else {
0410: // Only serverName changed
0411: setServerName(serverName);
0412: return;
0413: }
0414: } else {
0415: if (serverNotChanged) {
0416: // Only protocolName changed
0417: setProtocolName(protocolName);
0418: return;
0419: }
0420: }
0421:
0422: String oldProtocolName = this .protocolName;
0423: String oldServerName = this .serverName;
0424: this .protocolName = protocolName;
0425: this .serverName = serverName;
0426:
0427: // Both changed
0428: fireEvent("ProtocolAndServerName", new String[] {
0429: oldProtocolName, oldServerName }, new String[] {
0430: protocolName, serverName });
0431: nodeSupport.resetPropertyEnvironment(false);
0432: }
0433:
0434: /**
0435: * Returns the name of the <code>RegistryMode</code> corresponding to
0436: * this <code>RemoteRenderedOp</code>. This method overrides the
0437: * implementation in <code>RenderedOp</code> to always returns the
0438: * <code>String</code> "remoteRendered".
0439: */
0440: public String getRegistryModeName() {
0441: return RegistryMode.getMode("remoteRendered").getName();
0442: }
0443:
0444: /**
0445: * Overrides the <code>RenderedOp</code> method to allow the operation
0446: * to be performed remotely.
0447: */
0448: protected synchronized PlanarImage createInstance(
0449: boolean isNodeRendered) {
0450:
0451: ParameterBlock pb = new ParameterBlock();
0452: pb.setParameters(getParameters());
0453:
0454: int numSources = getNumSources();
0455:
0456: for (int i = 0; i < numSources; i++) {
0457:
0458: Object source = getNodeSource(i);
0459: Object ai = null;
0460: if (source instanceof RenderedOp) {
0461:
0462: RenderedOp src = (RenderedOp) source;
0463: ai = isNodeRendered ? src.getRendering() : src
0464: .createInstance();
0465:
0466: } else if ((source instanceof RenderedImage)
0467: || (source instanceof Collection)) {
0468:
0469: ai = source;
0470: } else if (source instanceof CollectionOp) {
0471: ai = ((CollectionOp) source).getCollection();
0472: } else {
0473: /* Source is some other type. Pass on (for now). */
0474: ai = source;
0475: }
0476: pb.addSource(ai);
0477: }
0478:
0479: RemoteRenderedImage instance = RemoteRIFRegistry.create(
0480: nodeSupport.getRegistry(), protocolName, serverName,
0481: nodeSupport.getOperationName(), pb, nodeSupport
0482: .getRenderingHints());
0483:
0484: // Throw an exception if the rendering is null.
0485: if (instance == null) {
0486: throw new ImagingException(JaiI18N
0487: .getString("RemoteRenderedOp2"));
0488: }
0489:
0490: // Save the state of the node.
0491: RenderingHints rh = nodeSupport.getRenderingHints();
0492: oldHints = rh == null ? null : (RenderingHints) rh.clone();
0493:
0494: // Ensure that the rendering is a PlanarImage.
0495: return PlanarImage.wrapRenderedImage(instance);
0496: }
0497:
0498: /* ----- PropertyChangeListener method. ----- */
0499:
0500: /**
0501: * Implementation of <code>PropertyChangeListener</code>.
0502: *
0503: * <p> When invoked with an event which is an instance of
0504: * <code>RenderingChangeEvent</code> the node will respond by
0505: * re-rendering itself while retaining any tiles possible.
0506: */
0507: // XXX Update javadoc both here and at class level.
0508: public synchronized void propertyChange(PropertyChangeEvent evt) {
0509:
0510: //
0511: // React if and only if the node has been rendered and
0512: // A: a non-PropertySourceChangeEvent PropertyChangeEventJAI
0513: // was received from this node, or
0514: // B: a RenderingChangeEvent was received from a source node.
0515: //
0516:
0517: // Cache event and node sources.
0518: Object evtSrc = evt.getSource();
0519: Vector nodeSources = nodeSupport.getParameterBlock()
0520: .getSources();
0521:
0522: // Get the name of the bean property and convert it to lower
0523: // case now for efficiency later.
0524: String propName = evt.getPropertyName().toLowerCase(
0525: Locale.ENGLISH);
0526:
0527: if (theImage != null
0528: && ((evt instanceof PropertyChangeEventJAI
0529: && evtSrc == this
0530: && !(evt instanceof PropertySourceChangeEvent) && nodeEventNames
0531: .contains(propName)) || ((evt instanceof RenderingChangeEvent
0532: || evt instanceof CollectionChangeEvent || (evt instanceof PropertyChangeEventJAI
0533: && evtSrc instanceof RenderedImage && propName
0534: .equals("invalidregion"))) && nodeSources
0535: .contains(evtSrc)))) {
0536:
0537: // Save the previous rendering.
0538: PlanarImage theOldImage = theImage;
0539:
0540: // Initialize the event flag.
0541: boolean shouldFireEvent = false;
0542:
0543: // Set default invalid region to null (the entire image).
0544: Shape invalidRegion = null;
0545:
0546: if (evtSrc == this
0547: && (propName.equals("operationregistry")
0548: || propName.equals("protocolname") || propName
0549: .equals("protocolandservername"))) {
0550:
0551: // invalidate the entire rendering.
0552: shouldFireEvent = true;
0553: theImage = null;
0554:
0555: } else if (evt instanceof RenderingChangeEvent
0556: || (evtSrc instanceof RenderedImage && propName
0557: .equals("invalidregion"))) {
0558:
0559: // Set the event flag.
0560: shouldFireEvent = true;
0561: Shape srcInvalidRegion = null;
0562:
0563: if (evt instanceof RenderingChangeEvent) {
0564:
0565: // RenderingChangeEvent presumably from a source
0566: // RenderedOp.
0567: RenderingChangeEvent rcEvent = (RenderingChangeEvent) evt;
0568:
0569: // Get the invalidated region of the source.
0570: srcInvalidRegion = rcEvent.getInvalidRegion();
0571:
0572: // If entire source is invalid replace with source bounds.
0573: if (srcInvalidRegion == null) {
0574: srcInvalidRegion = ((PlanarImage) rcEvent
0575: .getOldValue()).getBounds();
0576: }
0577: } else {
0578:
0579: // Get the invalidated region of the source.
0580: srcInvalidRegion = (Shape) evt.getNewValue();
0581:
0582: // If entire source is invalid replace with source bounds.
0583: if (srcInvalidRegion == null) {
0584: RenderedImage rSrc = (RenderedImage) evtSrc;
0585: srcInvalidRegion = new Rectangle(
0586: rSrc.getMinX(), rSrc.getMinY(), rSrc
0587: .getWidth(), rSrc.getHeight());
0588: }
0589: }
0590:
0591: // Only process further if the rendering is a
0592: // PlanarImageServerProxy.
0593: if (!(theImage instanceof PlanarImageServerProxy)) {
0594:
0595: // Clear the current rendering.
0596: theImage = null;
0597:
0598: } else {
0599:
0600: // Save the previous rendering as a PlanarImageServerProxy.
0601: PlanarImageServerProxy oldPISP = (PlanarImageServerProxy) theImage;
0602:
0603: // Cache source invalid bounds.
0604: Rectangle srcInvalidBounds = srcInvalidRegion
0605: .getBounds();
0606:
0607: // If bounds are empty, replace srcInvalidRegion with
0608: // the complement of the image bounds within the
0609: // bounds of all tiles.
0610: if (srcInvalidBounds.isEmpty()) {
0611: int x = oldPISP.tileXToX(oldPISP.getMinTileX());
0612: int y = oldPISP.tileYToY(oldPISP.getMinTileY());
0613: int w = oldPISP.getNumXTiles()
0614: * oldPISP.getTileWidth();
0615: int h = oldPISP.getNumYTiles()
0616: * oldPISP.getTileHeight();
0617: Rectangle tileBounds = new Rectangle(x, y, w, h);
0618: Rectangle imageBounds = oldPISP.getBounds();
0619: if (!tileBounds.equals(imageBounds)) {
0620: Area tmpArea = new Area(tileBounds);
0621: tmpArea.subtract(new Area(imageBounds));
0622: srcInvalidRegion = tmpArea;
0623: srcInvalidBounds = srcInvalidRegion
0624: .getBounds();
0625: }
0626: }
0627:
0628: // ----- Determine invalid destination region. -----
0629:
0630: boolean saveAllTiles = false;
0631: ArrayList validTiles = null;
0632: if (srcInvalidBounds.isEmpty()) {
0633: invalidRegion = srcInvalidRegion;
0634: saveAllTiles = true;
0635:
0636: } else {
0637:
0638: // Get index of source which changed.
0639: int idx = nodeSources.indexOf(evtSrc);
0640:
0641: // Determine bounds of invalid destination region.
0642: Rectangle dstRegionBounds = oldPISP
0643: .mapSourceRect(srcInvalidBounds, idx);
0644:
0645: if (dstRegionBounds == null) {
0646: dstRegionBounds = oldPISP.getBounds();
0647: }
0648:
0649: // Determine invalid destination region.
0650: Point[] indices = getTileIndices(dstRegionBounds);
0651: int numIndices = indices != null ? indices.length
0652: : 0;
0653: GeneralPath gp = null;
0654:
0655: for (int i = 0; i < numIndices; i++) {
0656: if (i % 1000 == 0 && gp != null)
0657: gp = new GeneralPath(new Area(gp));
0658:
0659: Rectangle dstRect = getTileRect(
0660: indices[i].x, indices[i].y);
0661: Rectangle srcRect = oldPISP.mapDestRect(
0662: dstRect, idx);
0663: if (srcRect == null) {
0664: gp = null;
0665: break;
0666: }
0667: if (srcInvalidRegion.intersects(srcRect)) {
0668: if (gp == null) {
0669: gp = new GeneralPath(dstRect);
0670: } else {
0671: gp.append(dstRect, false);
0672: }
0673: } else {
0674: if (validTiles == null) {
0675: validTiles = new ArrayList();
0676: }
0677: validTiles.add(indices[i]);
0678: }
0679: }
0680:
0681: invalidRegion = (gp == null) ? null : new Area(
0682: gp);
0683: }
0684:
0685: // Retrieve the old TileCache.
0686: TileCache oldCache = oldPISP.getTileCache();
0687: theImage = null;
0688:
0689: // Only perform further processing if there is a cache
0690: // and there are tiles to save.
0691: if (oldCache != null
0692: && (saveAllTiles || validTiles != null)) {
0693:
0694: // Create new rendering
0695: newEventRendering(protocolName, oldPISP,
0696: (PropertyChangeEventJAI) evt);
0697:
0698: // Only perform further processing if the new
0699: // rendering is an OpImage with a non-null TileCache.
0700: if (theImage instanceof PlanarImageServerProxy
0701: && ((PlanarImageServerProxy) theImage)
0702: .getTileCache() != null) {
0703: PlanarImageServerProxy newPISP = (PlanarImageServerProxy) theImage;
0704: TileCache newCache = newPISP.getTileCache();
0705:
0706: Object tileCacheMetric = newPISP
0707: .getTileCacheMetric();
0708:
0709: if (saveAllTiles) {
0710: Raster[] tiles = oldCache
0711: .getTiles(oldPISP);
0712: int numTiles = tiles == null ? 0
0713: : tiles.length;
0714: for (int i = 0; i < numTiles; i++) {
0715: Raster tile = tiles[i];
0716: int tx = newPISP.XToTileX(tile
0717: .getMinX());
0718: int ty = newPISP.YToTileY(tile
0719: .getMinY());
0720: newCache.add(newPISP, tx, ty, tile,
0721: tileCacheMetric);
0722: }
0723: } else { // save some, but not all, tiles
0724: int numValidTiles = validTiles.size();
0725: for (int i = 0; i < numValidTiles; i++) {
0726: Point tileIndex = (Point) validTiles
0727: .get(i);
0728: Raster tile = oldCache.getTile(
0729: oldPISP, tileIndex.x,
0730: tileIndex.y);
0731: if (tile != null) {
0732: newCache.add(newPISP,
0733: tileIndex.x,
0734: tileIndex.y, tile,
0735: tileCacheMetric);
0736: }
0737: }
0738: }
0739: }
0740: }
0741: }
0742: } else { // not op name or registry change nor RenderingChangeEvent
0743: ParameterBlock oldPB = null;
0744: ParameterBlock newPB = null;
0745: String oldServerName = serverName;
0746: String newServerName = serverName;
0747:
0748: boolean checkInvalidRegion = false;
0749:
0750: if (propName.equals("operationname")) {
0751:
0752: if (theImage instanceof PlanarImageServerProxy) {
0753: newEventRendering(protocolName,
0754: (PlanarImageServerProxy) theImage,
0755: (PropertyChangeEventJAI) evt);
0756: } else {
0757: theImage = null;
0758: createRendering();
0759: }
0760:
0761: // Do not set checkInvalidRegion to true, since there
0762: // are no tiles to save for this case.
0763:
0764: shouldFireEvent = true;
0765:
0766: // XXX Do we need to do any evaluation of any
0767: // DeferredData parameters.
0768:
0769: } else if (propName.equals("parameterblock")) {
0770: oldPB = (ParameterBlock) evt.getOldValue();
0771: newPB = (ParameterBlock) evt.getNewValue();
0772: checkInvalidRegion = true;
0773: } else if (propName.equals("sources")) {
0774: // Replace source(s)
0775: Vector params = nodeSupport.getParameterBlock()
0776: .getParameters();
0777: oldPB = new ParameterBlock((Vector) evt
0778: .getOldValue(), params);
0779: newPB = new ParameterBlock((Vector) evt
0780: .getNewValue(), params);
0781: checkInvalidRegion = true;
0782: } else if (propName.equals("parameters")) {
0783: // Replace parameter(s)
0784: oldPB = new ParameterBlock(nodeSources,
0785: (Vector) evt.getOldValue());
0786: newPB = new ParameterBlock(nodeSources,
0787: (Vector) evt.getNewValue());
0788: checkInvalidRegion = true;
0789: } else if (propName.equals("renderinghints")) {
0790: oldPB = newPB = nodeSupport.getParameterBlock();
0791: checkInvalidRegion = true;
0792: } else if (propName.equals("servername")) {
0793: oldPB = newPB = nodeSupport.getParameterBlock();
0794: oldServerName = (String) evt.getOldValue();
0795: newServerName = (String) evt.getNewValue();
0796: checkInvalidRegion = true;
0797: } else if (evt instanceof CollectionChangeEvent) {
0798: // Event from a CollectionOp source.
0799: // Replace appropriate source.
0800: int collectionIndex = nodeSources.indexOf(evtSrc);
0801: Vector oldSources = (Vector) nodeSources.clone();
0802: Vector newSources = (Vector) nodeSources.clone();
0803: oldSources.set(collectionIndex, evt.getOldValue());
0804: newSources.set(collectionIndex, evt.getNewValue());
0805:
0806: Vector params = nodeSupport.getParameterBlock()
0807: .getParameters();
0808:
0809: oldPB = new ParameterBlock(oldSources, params);
0810: newPB = new ParameterBlock(newSources, params);
0811:
0812: checkInvalidRegion = true;
0813: }
0814:
0815: if (checkInvalidRegion) {
0816: // Set event flag.
0817: shouldFireEvent = true;
0818:
0819: // Get the associated RemoteDescriptor.
0820: OperationRegistry registry = nodeSupport
0821: .getRegistry();
0822: RemoteDescriptor odesc = (RemoteDescriptor) registry
0823: .getDescriptor(RemoteDescriptor.class,
0824: protocolName);
0825:
0826: // XXX
0827: // Evaluate any DeferredData parameters.
0828: oldPB = ImageUtil.evaluateParameters(oldPB);
0829: newPB = ImageUtil.evaluateParameters(newPB);
0830:
0831: // Determine the invalid region.
0832: invalidRegion = (Shape) odesc.getInvalidRegion(
0833: "rendered", oldServerName, oldPB, oldHints,
0834: newServerName, newPB, nodeSupport
0835: .getRenderingHints(), this );
0836:
0837: if (invalidRegion == null
0838: || !(theImage instanceof PlanarImageServerProxy)) {
0839: // Can't save any tiles; clear the rendering.
0840: theImage = null;
0841:
0842: } else {
0843:
0844: // Create a new rendering.
0845: PlanarImageServerProxy oldRendering = (PlanarImageServerProxy) theImage;
0846:
0847: newEventRendering(protocolName, oldRendering,
0848: (PropertyChangeEventJAI) evt);
0849:
0850: // If the new rendering is also a
0851: // PlanarImageServerProxy, save some tiles.
0852: if (theImage instanceof PlanarImageServerProxy
0853: && oldRendering.getTileCache() != null
0854: && ((PlanarImageServerProxy) theImage)
0855: .getTileCache() != null) {
0856: PlanarImageServerProxy newRendering = (PlanarImageServerProxy) theImage;
0857:
0858: TileCache oldCache = oldRendering
0859: .getTileCache();
0860: TileCache newCache = newRendering
0861: .getTileCache();
0862:
0863: Object tileCacheMetric = newRendering
0864: .getTileCacheMetric();
0865:
0866: // If bounds are empty, replace invalidRegion with
0867: // the complement of the image bounds within the
0868: // bounds of all tiles.
0869: if (invalidRegion.getBounds().isEmpty()) {
0870: int x = oldRendering
0871: .tileXToX(oldRendering
0872: .getMinTileX());
0873: int y = oldRendering
0874: .tileYToY(oldRendering
0875: .getMinTileY());
0876: int w = oldRendering.getNumXTiles()
0877: * oldRendering.getTileWidth();
0878: int h = oldRendering.getNumYTiles()
0879: * oldRendering.getTileHeight();
0880: Rectangle tileBounds = new Rectangle(x,
0881: y, w, h);
0882: Rectangle imageBounds = oldRendering
0883: .getBounds();
0884: if (!tileBounds.equals(imageBounds)) {
0885: Area tmpArea = new Area(tileBounds);
0886: tmpArea.subtract(new Area(
0887: imageBounds));
0888: invalidRegion = tmpArea;
0889: }
0890: }
0891:
0892: if (invalidRegion.getBounds().isEmpty()) {
0893:
0894: // Save all tiles.
0895: Raster[] tiles = oldCache
0896: .getTiles(oldRendering);
0897: int numTiles = tiles == null ? 0
0898: : tiles.length;
0899: for (int i = 0; i < numTiles; i++) {
0900: Raster tile = tiles[i];
0901: int tx = newRendering.XToTileX(tile
0902: .getMinX());
0903: int ty = newRendering.YToTileY(tile
0904: .getMinY());
0905: newCache.add(newRendering, tx, ty,
0906: tile, tileCacheMetric);
0907: }
0908: } else {
0909: // Copy tiles not in invalid region from old
0910: // TileCache to new TileCache.
0911: Raster[] tiles = oldCache
0912: .getTiles(oldRendering);
0913: int numTiles = tiles == null ? 0
0914: : tiles.length;
0915: for (int i = 0; i < numTiles; i++) {
0916: Raster tile = tiles[i];
0917: Rectangle bounds = tile.getBounds();
0918: if (!invalidRegion
0919: .intersects(bounds)) {
0920: newCache
0921: .add(
0922: newRendering,
0923: newRendering
0924: .XToTileX(bounds.x),
0925: newRendering
0926: .YToTileY(bounds.y),
0927: tile,
0928: tileCacheMetric);
0929: }
0930: }
0931: }
0932: }
0933: }
0934: }
0935: }
0936:
0937: // Re-render the node. This will only occur if theImage
0938: // has been set to null above.
0939: if (theOldImage instanceof PlanarImageServerProxy
0940: && theImage == null) {
0941: newEventRendering(protocolName,
0942: (PlanarImageServerProxy) theOldImage,
0943: (PropertyChangeEventJAI) evt);
0944: } else {
0945: createRendering();
0946: }
0947:
0948: // Fire an event if the flag was set.
0949: if (shouldFireEvent) {
0950:
0951: // Clear the synthetic and cached properties and reset the
0952: // property source.
0953: resetProperties(true);
0954:
0955: // Create the event object.
0956: RenderingChangeEvent rcEvent = new RenderingChangeEvent(
0957: this , theOldImage, theImage, invalidRegion);
0958:
0959: // Fire to all registered listeners.
0960: eventManager.firePropertyChange(rcEvent);
0961:
0962: // Fire an event to all PropertyChangeListener sinks.
0963: Vector sinks = getSinks();
0964: if (sinks != null) {
0965: int numSinks = sinks.size();
0966: for (int i = 0; i < numSinks; i++) {
0967: Object sink = sinks.get(i);
0968: if (sink instanceof PropertyChangeListener) {
0969: ((PropertyChangeListener) sink)
0970: .propertyChange(rcEvent);
0971: }
0972: }
0973: }
0974: }
0975: }
0976: }
0977:
0978: /**
0979: * Creates a new rendering in response to the provided event, and
0980: * assigns the new rendering to "theImage" variable.
0981: */
0982: private void newEventRendering(String protocolName,
0983: PlanarImageServerProxy oldPISP, PropertyChangeEventJAI event) {
0984: RemoteRIF rrif = (RemoteRIF) nodeSupport.getRegistry()
0985: .getFactory("remoterendered", protocolName);
0986: theImage = (PlanarImage) rrif.create(oldPISP, this , event);
0987: }
0988:
0989: /**
0990: * Fire an events to all registered listeners.
0991: */
0992: private void fireEvent(String propName, Object oldVal, Object newVal) {
0993: if (eventManager != null) {
0994: Object eventSource = eventManager
0995: .getPropertyChangeEventSource();
0996: PropertyChangeEventJAI evt = new PropertyChangeEventJAI(
0997: eventSource, propName, oldVal, newVal);
0998: eventManager.firePropertyChange(evt);
0999: }
1000: }
1001:
1002: /**
1003: * Returns the amount of time between retries in milliseconds.
1004: *
1005: * <p> If this <code>RemoteRenderedOp</code> has been rendered, then its
1006: * <code>getRetryInterval()</code> method will be called to return
1007: * the current retry interval. If no rendering has been created, and
1008: * a value was set using the <code>setRetryInterval()</code> method), that
1009: * value will be returned, else the default retry interval as defined by
1010: * <code>RemoteJAI.DEFAULT_RETRY_INTERVAL</code> is returned.
1011: */
1012: public int getRetryInterval() {
1013: if (theImage != null) {
1014: return ((RemoteRenderedImage) theImage).getRetryInterval();
1015: } else {
1016: RenderingHints rh = nodeSupport.getRenderingHints();
1017: if (rh == null) {
1018: return RemoteJAI.DEFAULT_RETRY_INTERVAL;
1019: } else {
1020: Integer i = (Integer) rh.get(JAI.KEY_RETRY_INTERVAL);
1021: if (i == null)
1022: return RemoteJAI.DEFAULT_RETRY_INTERVAL;
1023: else
1024: return i.intValue();
1025: }
1026: }
1027: }
1028:
1029: /**
1030: * Sets the amount of time between retries in milliseconds. If this
1031: * <code>RemoteRenderedOp</code> has already been rendered, the
1032: * <code>setRetryInterval()</code> method is called on the rendering
1033: * to inform it of the new retry interval. The rendering can choose to
1034: * ignore this new setting, in which case <code>getRetryInterval()</code>
1035: * will still return the old value, or the rendering can honor these
1036: * settings, in which case <code>getRetryInterval()</code> will return
1037: * the new settings. If this <code>RemoteRenderedOp</code> has not been
1038: * rendered, the new retry interval specified will be stored.
1039: * These new stored retry interval will be passed as
1040: * part of the <code>RenderingHints</code> using the
1041: * <code>KEY_RETRY_INTERVAL</code> key, to the new rendering
1042: * when it is created.
1043: *
1044: * @param retryInterval The amount of time (in milliseconds) to wait
1045: * between retries.
1046: * @throws IllegalArgumentException if retryInterval is negative.
1047: */
1048: public void setRetryInterval(int retryInterval) {
1049:
1050: if (retryInterval < 0)
1051: throw new IllegalArgumentException(JaiI18N
1052: .getString("Generic3"));
1053:
1054: if (theImage != null) {
1055: ((RemoteRenderedImage) theImage)
1056: .setRetryInterval(retryInterval);
1057: }
1058:
1059: RenderingHints rh = nodeSupport.getRenderingHints();
1060: if (rh == null) {
1061: nodeSupport.setRenderingHints(new RenderingHints(null));
1062: rh = nodeSupport.getRenderingHints();
1063: }
1064:
1065: rh.put(JAI.KEY_RETRY_INTERVAL, new Integer(retryInterval));
1066: }
1067:
1068: /**
1069: * Returns the number of retries.
1070: *
1071: * <p> If this <code>RemoteRenderedOp</code> has been rendered, then its
1072: * <code>getNumRetries()</code> method will be called to return
1073: * the current number of retries. If no rendering has been created, and
1074: * a value was set using the <code>setNumRetries()</code> method), that
1075: * value will be returned, else the default retry interval as defined by
1076: * <code>RemoteJAI.DEFAULT_NUM_RETRIES</code> is returned.
1077: */
1078: public int getNumRetries() {
1079: if (theImage != null) {
1080: return ((RemoteRenderedImage) theImage).getNumRetries();
1081: } else {
1082: RenderingHints rh = nodeSupport.getRenderingHints();
1083: if (rh == null) {
1084: return RemoteJAI.DEFAULT_NUM_RETRIES;
1085: } else {
1086: Integer i = (Integer) rh.get(JAI.KEY_NUM_RETRIES);
1087: if (i == null)
1088: return RemoteJAI.DEFAULT_NUM_RETRIES;
1089: else
1090: return i.intValue();
1091: }
1092: }
1093: }
1094:
1095: /**
1096: * Sets the number of retries. If this <code>RemoteRenderedOp</code>
1097: * has already been rendered, the <code>setNumRetries()</code> method
1098: * is called on the rendering to inform it of the new number of retries.
1099: * The rendering can choose to ignore these new settings, in which case
1100: * <code>getNunRetries()</code> will still return the old values, or
1101: * the rendering can honor these new settings in which
1102: * case <code>getNumRetries()</code> will return the new value.
1103: * If this <code>RemoteRenderedOp</code> has not been rendered,
1104: * the new setting specified will be stored.
1105: * These new settings which have been stored will be passed as
1106: * part of the <code>RenderingHints</code> using the
1107: * <code>KEY_NUM_RETRIES</code> key, to the new rendering
1108: * when it is created.
1109: *
1110: * @param numRetries The number of times an operation should be retried
1111: * in case of a network error.
1112: * @throws IllegalArgumentException if numRetries is negative.
1113: */
1114: public void setNumRetries(int numRetries) {
1115:
1116: if (numRetries < 0)
1117: throw new IllegalArgumentException(JaiI18N
1118: .getString("Generic4"));
1119:
1120: if (theImage != null) {
1121: ((RemoteRenderedImage) theImage).setNumRetries(numRetries);
1122: }
1123:
1124: RenderingHints rh = nodeSupport.getRenderingHints();
1125: if (rh == null) {
1126: nodeSupport.setRenderingHints(new RenderingHints(null));
1127: rh = nodeSupport.getRenderingHints();
1128: }
1129:
1130: rh.put(JAI.KEY_NUM_RETRIES, new Integer(numRetries));
1131: }
1132:
1133: /**
1134: * Sets the preferences to be used in the client-server
1135: * communication. These preferences are utilized in the negotiation
1136: * process. Note that preferences for more than one category can be
1137: * specified using this method. Also each preference can be a list
1138: * of values in decreasing order of preference, each value specified
1139: * as a <code>NegotiableCapability</code>. The
1140: * <code>NegotiableCapability</code> first (for a particular category)
1141: * in this list is given highest priority in the negotiation process
1142: * (for that category).
1143: *
1144: * <p> It may be noted that this method allows for multiple negotiation
1145: * cycles by allowing negotiation preferences to be set
1146: * multiple times. If this <code>RemoteRenderedOp</code> has already
1147: * been rendered, the <code>setNegotiationPreferences()</code> method
1148: * is called on the rendering to inform it of the new preferences. The
1149: * rendering can choose to ignore these new preferences, in which case
1150: * <code>getNegotiatedValues()</code> will still return the results of
1151: * the old negotiation, or the rendering can re-perform the negotiation,
1152: * (using the <code>RemoteJAI.negotiate</code>, for example) in which
1153: * case <code>getNegotiatedValues()</code> will return the new
1154: * negotiated values. If this <code>RemoteRenderedOp</code> has not been
1155: * rendered, the new preferences specified will be stored, a negotiation
1156: * with these new preferences will be initiated and the results stored.
1157: * These new preferences which have been stored will be passed as
1158: * part of the <code>RenderingHints</code> using the
1159: * <code>KEY_NEGOTIATION_PREFERENCES</code> key, to the new rendering
1160: * when it is created.
1161: *
1162: * @param preferences The preferences to be used in the negotiation
1163: * process.
1164: */
1165: public void setNegotiationPreferences(
1166: NegotiableCapabilitySet preferences) {
1167: if (theImage != null) {
1168: ((RemoteRenderedImage) theImage)
1169: .setNegotiationPreferences(preferences);
1170: }
1171:
1172: RenderingHints rh = nodeSupport.getRenderingHints();
1173:
1174: if (preferences != null) {
1175: if (rh == null) {
1176: nodeSupport.setRenderingHints(new RenderingHints(null));
1177: rh = nodeSupport.getRenderingHints();
1178: }
1179:
1180: rh.put(JAI.KEY_NEGOTIATION_PREFERENCES, preferences);
1181: } else {
1182: // Remove any previous values set for negotiation preferences
1183: if (rh != null) {
1184: rh.remove(JAI.KEY_NEGOTIATION_PREFERENCES);
1185: }
1186: }
1187:
1188: negotiated = negotiate(preferences);
1189: }
1190:
1191: /**
1192: * Returns the current negotiation preferences or null, if none were
1193: * set previously.
1194: */
1195: public NegotiableCapabilitySet getNegotiationPreferences() {
1196:
1197: RenderingHints rh = nodeSupport.getRenderingHints();
1198: return rh == null ? null : (NegotiableCapabilitySet) rh
1199: .get(JAI.KEY_NEGOTIATION_PREFERENCES);
1200: }
1201:
1202: // do the negotiation
1203: private NegotiableCapabilitySet negotiate(
1204: NegotiableCapabilitySet prefs) {
1205:
1206: OperationRegistry registry = nodeSupport.getRegistry();
1207:
1208: NegotiableCapabilitySet serverCap = null;
1209:
1210: // Get the RemoteDescriptor for protocolName
1211: RemoteDescriptor descriptor = (RemoteDescriptor) registry
1212: .getDescriptor(RemoteDescriptor.class, protocolName);
1213:
1214: if (descriptor == null) {
1215: Object[] msgArg0 = { new String(protocolName) };
1216: MessageFormat formatter = new MessageFormat("");
1217: formatter.setLocale(Locale.getDefault());
1218: formatter.applyPattern(JaiI18N.getString("RemoteJAI16"));
1219: throw new ImagingException(formatter.format(msgArg0));
1220: }
1221:
1222: int count = 0;
1223: int numRetries = getNumRetries();
1224: int retryInterval = getRetryInterval();
1225:
1226: Exception rieSave = null;
1227: while (count++ < numRetries) {
1228: try {
1229: serverCap = descriptor
1230: .getServerCapabilities(serverName);
1231: break;
1232: } catch (RemoteImagingException rie) {
1233: // Print that an Exception occured
1234: System.err.println(JaiI18N.getString("RemoteJAI24"));
1235: rieSave = rie;
1236: // Sleep for retryInterval milliseconds
1237: try {
1238: Thread.sleep(retryInterval);
1239: } catch (InterruptedException ie) {
1240: // throw new RuntimeException(ie.toString());
1241: sendExceptionToListener(JaiI18N
1242: .getString("Generic5"),
1243: new ImagingException(JaiI18N
1244: .getString("Generic5"), ie));
1245: }
1246: }
1247: }
1248:
1249: if (serverCap == null && count > numRetries) {
1250: sendExceptionToListener(JaiI18N.getString("RemoteJAI18"),
1251: rieSave);
1252: // throw new RemoteImagingException(JaiI18N.getString("RemoteJAI18")+"\n"+rieSave.getMessage());
1253: }
1254:
1255: RemoteRIF rrif = (RemoteRIF) registry.getFactory(
1256: "remoteRendered", protocolName);
1257:
1258: return RemoteJAI.negotiate(prefs, serverCap, rrif
1259: .getClientCapabilities());
1260: }
1261:
1262: /**
1263: * Returns the results of the negotiation between the client and server
1264: * capabilities according to the preferences set via the
1265: * <code>setNegotiationPreferences()</code> method. This will return
1266: * null if no negotiation preferences were set, and no negotiation
1267: * was performed, or if the negotiation failed.
1268: *
1269: * <p> If this <code>RemoteRenderedOp</code> has been rendered, then its
1270: * <code>getNegotiatedValues()</code> method will be called to return
1271: * the current negotiated values. If no rendering has been created, then
1272: * the internally stored negotiated value (calculated when the new
1273: * preferences were set using the <code>setNegotiationPreferences()</code>
1274: * method) will be returned.
1275: */
1276: public NegotiableCapabilitySet getNegotiatedValues()
1277: throws RemoteImagingException {
1278: if (theImage != null) {
1279: return ((RemoteRenderedImage) theImage)
1280: .getNegotiatedValues();
1281: } else {
1282: return negotiated;
1283: }
1284: }
1285:
1286: /**
1287: * Returns the results of the negotiation between the client and server
1288: * capabilities for the given category according to the preferences
1289: * set via the <code>setNegotiationPreferences()</code> method. This
1290: * will return null if no negotiation preferences were set, and no
1291: * negotiation was performed, or if the negotiation failed.
1292: *
1293: * <p> If this <code>RemoteRenderedOp</code> has been rendered, then its
1294: * <code>getNegotiatedValues()</code> method will be called to return
1295: * the current negotiated values. If no rendering has been created, then
1296: * the internally stored negotiated value (calculated when the new
1297: * preferences were set using the <code>setNegotiationPreferences()</code>
1298: * method) will be returned.
1299: *
1300: * @param category The category to return the negotiated results for.
1301: */
1302: public NegotiableCapability getNegotiatedValue(String category)
1303: throws RemoteImagingException {
1304: if (theImage != null) {
1305: return ((RemoteRenderedImage) theImage)
1306: .getNegotiatedValue(category);
1307: } else {
1308: return negotiated == null ? null : negotiated
1309: .getNegotiatedValue(category);
1310: }
1311: }
1312:
1313: /**
1314: * Informs the server of the negotiated values that are the result of
1315: * a successful negotiation. If this <code>RemoteRenderedOp</code> has
1316: * been rendered, then the rendering's
1317: * <code>setServerNegotiatedValues</code> method will be called to
1318: * inform the server of the negotiated results. If no rendering has
1319: * been created, this method will do nothing.
1320: *
1321: * @param negotiatedValues The result of the negotiation.
1322: */
1323: public void setServerNegotiatedValues(
1324: NegotiableCapabilitySet negotiatedValues)
1325: throws RemoteImagingException {
1326:
1327: if (theImage != null) {
1328: ((RemoteRenderedImage) theImage)
1329: .setServerNegotiatedValues(negotiatedValues);
1330: }
1331: }
1332:
1333: void sendExceptionToListener(String message, Exception e) {
1334: ImagingListener listener = (ImagingListener) getRenderingHints()
1335: .get(JAI.KEY_IMAGING_LISTENER);
1336:
1337: listener.errorOccurred(message, e, this , false);
1338: }
1339: }
|