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