arcgissamples\soi\ApplyWatermarkSOI.java
/* Copyright 2015 ESRI * * All rights reserved under the copyright laws of the United States * and applicable international laws, treaties, and conventions. * * You may freely redistribute and use this sample code, with or * without modification, provided you include the original copyright * notice and use restrictions. * * See the use restrictions at <your ArcGIS install location>/DeveloperKit10.4/userestrictions.txt. * */ package arcgissamples.soi; /* * COPYRIGHT 1995-2014 ESRI TRADE SECRETS: ESRI PROPRIETARY AND CONFIDENTIAL Unpublished material - * all rights reserved under the Copyright Laws of the United States and applicable international * laws, treaties, and conventions. * * For additional information, contact: Environmental Systems Research Institute, Inc. Attn: * Contracts and Legal Services Department 380 New York Street Redlands, California, 92373 USA * * email: contracts@esri.com */ import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.imageio.ImageIO; import com.esri.arcgis.addinframework.TypeChecker; import com.esri.arcgis.interop.AutomationException; import com.esri.arcgis.interop.extn.ArcGISExtension; import com.esri.arcgis.interop.extn.ServerObjectExtProperties; import com.esri.arcgis.server.IServerObject; import com.esri.arcgis.server.IServerObjectExtension; import com.esri.arcgis.server.IServerObjectExtensionManager; import com.esri.arcgis.server.IServerObjectExtensionManagerProxy; import com.esri.arcgis.server.IServerObjectHelper; import com.esri.arcgis.server.SOIHelper; import com.esri.arcgis.server.json.JSONObject; import com.esri.arcgis.system.EnvironmentManager; import com.esri.arcgis.system.ILog; import com.esri.arcgis.system.IPropertySet; import com.esri.arcgis.system.IRESTRequestHandler; import com.esri.arcgis.system.IRESTRequestHandlerProxy; import com.esri.arcgis.system.IRequestHandler; import com.esri.arcgis.system.IRequestHandler2; import com.esri.arcgis.system.IRequestHandler2Proxy; import com.esri.arcgis.system.IRequestHandlerProxy; import com.esri.arcgis.system.IServerEnvironment2; import com.esri.arcgis.system.IServerEnvironment2Proxy; import com.esri.arcgis.system.IWebRequestHandler; import com.esri.arcgis.system.IWebRequestHandlerProxy; import com.esri.arcgis.system.ServerUtilities; import com.esri.arcgis.system.UID; /* * For an SOE to act as in intercepter, it needs to implement all request handler interfaces * IRESTRequestHandler, IWebRequestHandler, IRequestHandler2, IRequestHandler now the SOE/SOI can * intercept all types of calls to ArcObjects or custom SOEs. * * In this sample we demonstrate how to add watermark to output image generated by the export image * operation. */ @ArcGISExtension @ServerObjectExtProperties(displayName = "Watermark SOI", description = "Overlays a watermark on the export image operation", interceptor = true, servicetype = "MapService") public class ApplyWatermarkSOI implements IServerObjectExtension, IRESTRequestHandler, IWebRequestHandler, IRequestHandler2, IRequestHandler { private static final long serialVersionUID = 1L; private static final String ARCGISHOME_ENV = "AGSSERVER"; private ILog serverLog; private IServerObject so; private SOIHelper soiHelper; // Text used as watermark private static final String WATERMARK_STRING = "� ESRI Inc."; // Path to arcgis output directory private static final String OUTPUT_DIRECTORY = "C:/arcgisserver/directories/arcgisoutput"; /** * Default constructor. * * @throws Exception */ public ApplyWatermarkSOI() throws Exception { super(); } /** * init() is called once, when the instance of the SOE/SOI is created. * * @param soh the IServerObjectHelper * @throws IOException Signals that an I/O exception has occurred. * @throws AutomationException the automation exception */ public void init(IServerObjectHelper soh) throws IOException, AutomationException { /* * An SOE should retrieve a weak reference to the Server Object from the Server Object Helper in * order to make any method calls on the Server Object and release the reference after making * the method calls. */ // Get reference to server logger utility this.serverLog = ServerUtilities.getServerLogger(); // Log message with server this.serverLog.addMessage(3, 200, "Initialized " + this.getClass().getName() + " SOI."); this.so = soh.getServerObject(); String arcgisHome = getArcGISHomeDir(); /* If null, throw an exception */ if(arcgisHome == null) { serverLog.addMessage(1, 200, "Could not get ArcGIS home directory. Check if environment variable " + ARCGISHOME_ENV + " is set."); throw new IOException( "Could not get ArcGIS home directory. Check if environment variable " + ARCGISHOME_ENV + " is set."); } if(arcgisHome != null && !arcgisHome.endsWith(File.separator)) arcgisHome += File.separator; // Load the SOI helper. this.soiHelper = new SOIHelper(arcgisHome + "XmlSchema" + File.separator + "MapServer.wsdl"); } /** * This method is called to handle REST requests. * * @param capabilities the capabilities * @param resourceName the resource name * @param operationName the operation name * @param operationInput the operation input * @param outputFormat the output format * @param requestProperties the request properties * @param responseProperties the response properties * @return the response as byte[] * @throws IOException Signals that an I/O exception has occurred. * @throws AutomationException the automation exception */ @Override public byte[] handleRESTRequest(String capabilities, String resourceName, String operationName, String operationInput, String outputFormat, String requestProperties, String[] responseProperties) throws IOException, AutomationException { // Log message with server serverLog.addMessage(3, 200, "Request received in Watermark SOI for handleRESTRequest"); /* * Add code to manipulate REST requests here */ // Find the correct delegate to forward the request too IRESTRequestHandler restRequestHandler = soiHelper.findRestRequestHandlerDelegate(so); if (restRequestHandler != null) { /* * Get the response. */ byte[] response = restRequestHandler.handleRESTRequest(capabilities, resourceName, operationName, operationInput, outputFormat, requestProperties, responseProperties); /* * Manipulate the response. * * Add watermark */ if (operationName.equalsIgnoreCase("export")) { try { BufferedImage sourceImage = null; if (outputFormat.equalsIgnoreCase("image")) { sourceImage = byteArrayToBufferedImage(response); byte[] watermarkedImage = addTextWatermark(WATERMARK_STRING, sourceImage, new JSONObject(operationInput).getString("format"), outputFormat, null); // return the watermarked image if (watermarkedImage != null) return watermarkedImage; } else if (outputFormat.equalsIgnoreCase("json")) { // Generate output file location File outputImageFileLocation = getOutputImageFileLocation(new JSONObject(new String(response)).getString("href")); sourceImage = fileToBufferedImage(outputImageFileLocation); byte[] watermarkedImage = addTextWatermark(WATERMARK_STRING, sourceImage, new JSONObject(operationInput).getString("format"), outputFormat, outputImageFileLocation); // return response as is because we have modified the file its pointing to return response; } else if (outputFormat.equalsIgnoreCase("kmz")) { // Note: Watermark can be added for the kmz format too. In this example we didn't // implement it. return response; } else { return response; } } catch (Exception ignore) { // Note: If there is an error adding the watermark // don't return anything. return new JSONObject() .put("error", new JSONObject().put("code", 404).put("message", "Not Found")).toString() .getBytes(); } } return response; } return new JSONObject() .put("error", new JSONObject().put("code", 404).put("message", "Not Found")).toString() .getBytes(); } /** * Generate physical file path from virtual path * * @param virtualPath Path returned by the MapServer SO * @return */ private File getOutputImageFileLocation(String virtualPath) { /* * Sample output returned by MapServer SO * * example : /rest/directories/arcgisoutput/SampleWorldCities_MapServer/ * _ags_map26c62f8c2c0c4965b53e87e300e1912f.png */ String[] virtualPathParts = virtualPath.split("/"); String imageFileLocation = OUTPUT_DIRECTORY; // build the physical path to the image file boolean buildPath = false; for (String virtualPathPart : virtualPathParts) { if (buildPath) { imageFileLocation += "/" + virtualPathPart; } if (virtualPathPart.equalsIgnoreCase("arcgisoutput")) { buildPath = true; } } return new File(imageFileLocation); } /** * Convert byte array to BufferedImage * * @param sourceImageBytes * @return * @throws IOException */ private BufferedImage byteArrayToBufferedImage(byte[] sourceImageBytes) throws IOException { return ImageIO.read(new ByteArrayInputStream(sourceImageBytes)); } /** * Convert BufferedImage to byte array * * @param sourceImage * @param imageType png or jpeg * @return * @throws IOException */ private byte[] bufferedImagetoByteArray(BufferedImage sourceImage, String imageType) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(sourceImage, (imageType.startsWith("png")) ? "PNG" : "JPG", baos); baos.flush(); byte[] imageInBA = baos.toByteArray(); baos.close(); return imageInBA; } /** * Read an Image file and convert it into a BufferedImage * * @param sourceImageFile * @return * @throws IOException */ private BufferedImage fileToBufferedImage(File sourceImageFile) throws IOException { return ImageIO.read(sourceImageFile); } /** * Write image to disk * * @param sourceImage * @param imageType * @param outputImageFile * @throws IOException */ private void writeImageToDisk(BufferedImage sourceImage, String imageType, File outputImageFile) throws IOException { ImageIO.write(sourceImage, (imageType.startsWith("png")) ? "PNG" : "JPG", outputImageFile); } /** * Add watermark to an image * * @param watermarkText Text to be added as watermark. * @param sourceImageBA Image data in a byte array * @param imageType Type of image (png, jpeg etc.) * @param outputFormat json or image or kmz * @param outputImageFile * @return * @throws IOException */ private byte[] addTextWatermark(String watermarkText, BufferedImage sourceImage, String imageType, String outputFormat, File outputImageFile) throws IOException { // Create BufferedImage and Graphics2D objects Graphics2D g2d = (Graphics2D) sourceImage.getGraphics(); // Initializes necessary graphic properties AlphaComposite alphaChannel = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.1f); g2d.setComposite(alphaChannel); g2d.setColor(Color.BLUE); g2d.setFont(new Font("Arial", Font.BOLD, 64)); FontMetrics fontMetrics = g2d.getFontMetrics(); Rectangle2D rect = fontMetrics.getStringBounds(watermarkText, g2d); // Calculate the center int centerX = (sourceImage.getWidth() - (int) rect.getWidth()) / 2; int centerY = sourceImage.getHeight() / 2; // Add watermark at the center of image g2d.drawString(watermarkText, centerX, centerY); g2d.dispose(); if (outputFormat.equalsIgnoreCase("image")) { return bufferedImagetoByteArray(sourceImage, imageType); } else if (outputFormat.equalsIgnoreCase("json")) { // replace watermarked image with original image writeImageToDisk(sourceImage, imageType, outputImageFile); return null; } return null; } /** * This method is called to handle SOAP requests. * * @param capabilities the capabilities * @param request the request * @return the response as String * @throws IOException Signals that an I/O exception has occurred. * @throws AutomationException the automation exception */ @Override public String handleStringRequest(String capabilities, String request) throws IOException, AutomationException { // Log message with server serverLog.addMessage(3, 200, "Request received in Sample Object Interceptor for handleStringRequest"); /* * Add code to manipulate SOAP requests here */ // Find the correct delegate to forward the request too IRequestHandler requestHandler = soiHelper.findRequestHandlerDelegate(so); if (requestHandler != null) { // Return the response return requestHandler.handleStringRequest(capabilities, request); } return null; } /** * This method is called by SOAP handler to handle OGC requests. * * @param httpMethod * @param requestURL the request URL * @param queryString the query string * @param capabilities the capabilities * @param requestData the request data * @param responseContentType the response content type * @param respDataType the response data type * @return the response as byte[] * @throws IOException Signals that an I/O exception has occurred. * @throws AutomationException the automation exception */ @Override public byte[] handleStringWebRequest(int httpMethod, String requestURL, String queryString, String capabilities, String requestData, String[] responseContentType, int[] respDataType) throws IOException, AutomationException { serverLog.addMessage(3, 200, "Request received in Sample Object Interceptor for handleStringWebRequest"); /* * Add code to manipulate OGC (WMS, WFC, WCS etc) requests here */ IWebRequestHandler webRequestHandler = soiHelper.findWebRequestHandlerDelegate(so); if (webRequestHandler != null) { return webRequestHandler.handleStringWebRequest(httpMethod, requestURL, queryString, capabilities, requestData, responseContentType, respDataType); } return null; } /** * This method is called to handle schema requests for custom SOE's. * * @return the schema as String * @throws IOException Signals that an I/O exception has occurred. * @throws AutomationException the automation exception */ @Override public String getSchema() throws IOException, AutomationException { serverLog.addMessage(3, 200, "Request received in Sample Object Interceptor for getSchema"); /* * Add code to manipulate schema requests here */ IRESTRequestHandler restRequestHandler = soiHelper.findRestRequestHandlerDelegate(so); if (restRequestHandler != null) { return restRequestHandler.getSchema(); } return null; } /** * This method is called to handle binary requests from desktop. * * @param capabilities the capabilities * @param request * @return the response as byte[] * @throws IOException Signals that an I/O exception has occurred. * @throws AutomationException the automation exception */ @Override public byte[] handleBinaryRequest2(String capabilities, byte[] request) throws IOException, AutomationException { serverLog.addMessage(3, 200, "Request received in Sample Object Interceptor for handleBinaryRequest2"); /* * Add code to manipulate Binary requests from desktop here */ IRequestHandler2 requestHandler = soiHelper.findRequestHandler2Delegate(so); if (requestHandler != null) { return requestHandler.handleBinaryRequest2(capabilities, request); } return null; } /** * This method is called to handle binary requests from desktop. It calls the * <code>handleBinaryRequest2</code> method with capabilities equal to null. * * @param request * @return the response as the byte[] * @throws IOException Signals that an I/O exception has occurred. * @throws AutomationException the automation exception */ @Override public byte[] handleBinaryRequest(byte[] request) throws IOException, AutomationException { serverLog.addMessage(3, 200, "Request received in Sample Object Interceptor for handleBinaryRequest"); /* * Add code to manipulate Binary requests from desktop here */ IRequestHandler requestHandler = soiHelper.findRequestHandlerDelegate(so); if (requestHandler != null) { return requestHandler.handleBinaryRequest(request); } return null; } /** * shutdown() is called once when the Server Object's context is being shut down and is about to * go away. * * @throws IOException Signals that an I/O exception has occurred. * @throws AutomationException the automation exception */ public void shutdown() throws IOException, AutomationException { /* * The SOE should release its reference on the Server Object Helper. */ this.serverLog.addMessage(3, 200, "Shutting down " + this.getClass().getName() + " SOI."); this.serverLog = null; } /** * Returns the ArcGIS home directory path. * * @return * @throws Exception */ private String getArcGISHomeDir() throws IOException { String arcgisHome = null; /* Not found in env, check system property */ if (System.getProperty(ARCGISHOME_ENV) != null) { arcgisHome = System.getProperty(ARCGISHOME_ENV); } if(arcgisHome == null) { /* To make env lookup case insensitive */ Map<String, String> envs = System.getenv(); for (String envName : envs.keySet()) { if (envName.equalsIgnoreCase(ARCGISHOME_ENV)) { arcgisHome = envs.get(envName); } } } if(arcgisHome != null && !arcgisHome.endsWith(File.separator)) { arcgisHome += File.separator; } return arcgisHome; } }