Feature Validator Runner custom Java class extension
arcgissamples\geodatabase\FeatureValidationRunner.java
/* Copyright 2010 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.
* 
*/
package arcgissamples.geodatabase;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;

import com.esri.arcgis.datasourcesGDB.FileGDBWorkspaceFactory;
import com.esri.arcgis.geodatabase.Feature;
import com.esri.arcgis.geodatabase.FeatureClass;
import com.esri.arcgis.geodatabase.Field;
import com.esri.arcgis.geodatabase.Fields;
import com.esri.arcgis.geodatabase.GeometryDef;
import com.esri.arcgis.geodatabase.Workspace;
import com.esri.arcgis.geodatabase.esriFeatureType;
import com.esri.arcgis.geodatabase.esriFieldType;
import com.esri.arcgis.geodatabase.esriSchemaLock;
import com.esri.arcgis.geometry.GeographicCoordinateSystem;
import com.esri.arcgis.geometry.Point;
import com.esri.arcgis.geometry.Polygon;
import com.esri.arcgis.geometry.SpatialReferenceEnvironment;
import com.esri.arcgis.geometry.esriGeometryType;
import com.esri.arcgis.geometry.esriSRGeoCSType;
import com.esri.arcgis.interop.extn.ArcGISExtension;
import com.esri.arcgis.interop.extn.Bootstrapper;
import com.esri.arcgis.system.AoInitialize;
import com.esri.arcgis.system.EngineInitializer;
import com.esri.arcgis.system.UID;
import com.esri.arcgis.system.esriLicenseProductCode;
import com.esri.arcgis.system.esriLicenseStatus;

public class FeatureValidationRunner
{
    private static String outputPath = null;
    private Class ceClass = null;

  public static void main(String[] args)
  {
    try
    {
      //Get the ArcGIS Desktop runtime, if it is available
        String arcObjectsHome = System.getenv("AGSDESKTOPJAVA");
             
        //If the ArcGIS Desktop runtime is not available, then report appropriate error to developer
        if(arcObjectsHome == null){
          if(System.getProperty("os.name").toLowerCase().indexOf("win") > -1){
            System.err.println("The FeatureValidation sample is designed to only work with ArcGIS Desktop installed.");
            System.err.println("You must install ArcGIS Desktop and deploy the FeatureValidationExt.jar as instructed " +
                "in this sample's ReadMe.htm file.");
            System.err.println("Exiting execution of this sample...");
            System.exit(0);  
          }else{
            System.err.println("The FeatureValidation sample is designed to only work with ArcGIS Desktop installed.");
            System.err.println("Because this is a UNIX based machine and ArcGIS Desktop cannot be installed, " + 
                "this sample does not apply.");
            System.err.println("To execute this sample you will need an installation of ArcGIS Desktop and a computer with " +
                "a Windows O/S installed.");
            System.err.println("Exiting execution of this sample...");
            System.exit(0);  
          }
        }
        
        String extensionJar = arcObjectsHome + "java" + File.separator + "lib" + File.separator + "ext" + File.separator +
                    "FeatureValidationExt.jar";
      
             FeatureValidationRunner driver = new FeatureValidationRunner();
          
            //get output folder
            outputPath = getOutputDir();
                
            if(driver.isJarFileValid(extensionJar))
            {
              driver.ceClass = driver.getCEClass(extensionJar).get(0);
            }
          
            EngineInitializer.initializeEngine();
        AoInitialize aoInit = new AoInitialize();
        initializeArcGISLicenses(aoInit);
                  
        driver.driveExtension();

        aoInit.shutdown();
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }
  
  static void initializeArcGISLicenses(AoInitialize aoInit) {
    try {
      if (aoInit.isProductCodeAvailable(esriLicenseProductCode.esriLicenseProductCodeArcView) 
          == esriLicenseStatus.esriLicenseAvailable)
        aoInit.initialize(esriLicenseProductCode.esriLicenseProductCodeArcView);
      else if (aoInit.isProductCodeAvailable(esriLicenseProductCode.esriLicenseProductCodeArcEditor) 
          == esriLicenseStatus.esriLicenseAvailable)
        aoInit.initialize(esriLicenseProductCode.esriLicenseProductCodeArcEditor);
      else if (aoInit.isProductCodeAvailable(esriLicenseProductCode.esriLicenseProductCodeArcInfo) 
          == esriLicenseStatus.esriLicenseAvailable)
        aoInit.initialize(esriLicenseProductCode.esriLicenseProductCodeArcInfo);
    } catch (Exception e) {e.printStackTrace();}
  }

  /**
   * Convenience method to generate an output directory based on the operating
   * system that the sample is being executed on. 
   * 
   * @return A path to the new directory is return
   */
  private static String getOutputDir() {
    String userDir;
    
    //Get the operating systems user profile or home location depending
    //on which operating system this sample is executed on.
    if(System.getProperty("os.name").toLowerCase().indexOf("win") > -1){
      userDir = System.getenv("UserProfile");
    }else{
      userDir = System.getenv("HOME");
    }
      
    String outputDir = userDir + File.separator + "arcgis_sample_output";
    
    System.out.println("Creating output directory - " + outputDir);
    
    new File(outputDir).mkdir();
    
    return outputDir;
  }

  public void driveExtension() throws Exception
  {
    System.out.println("Starting FeatureValidationRunner...\n");

    System.out.print("Initializing data paths...");
    
    String fgdbName = "emailvalidator.gdb";
    System.out.println("Done.");

    System.out.print("Creating local File GDB...");
    FileGDBWorkspaceFactory wsFactory = new FileGDBWorkspaceFactory();
    wsFactory.create(outputPath, fgdbName, null, 0);
    System.out.println("Done.");
    System.out.println("Created a File GDB in the following location: " + outputPath + File.separator + fgdbName);

    //create a fc in above file gdb
    System.out.print("Creating a feature class in local File GDB...");
    Workspace ws = (Workspace) wsFactory.openFromFile(outputPath + File.separator + fgdbName, 0);

    Fields fields = new Fields();

    //OBJECTID field
    Field field = new Field();
    field.setName("OBJECTID");
    field.setType(esriFieldType.esriFieldTypeOID);
    fields.addField(field);
    field = null;

    //shape field
    field = new Field();

    GeometryDef gdef = new GeometryDef();
    gdef.setGeometryType(esriGeometryType.esriGeometryPolygon);
    gdef.setHasM(false);
    gdef.setHasZ(false);

        SpatialReferenceEnvironment sre = new SpatialReferenceEnvironment();
        GeographicCoordinateSystem gcs = (GeographicCoordinateSystem) sre.createGeographicCoordinateSystem(esriSRGeoCSType.esriSRGeoCS_NAD1983);
        gcs.setDomain(-400, 400, -400, 400);

        gdef.setSpatialReferenceByRef(gcs);

    field.setName("shape");
    field.setType(esriFieldType.esriFieldTypeGeometry);
    field.setGeometryDefByRef(gdef);
    fields.addField(field);
    field = null;

    //zone field
    field = new Field();
    field.setName("email");
    field.setType(esriFieldType.esriFieldTypeString);
    field.setEditable(true);
    fields.addField(field);
    field = null;

    //create and return feature class
    String fcName = "emailvalidatorfc";
    FeatureClass fc = new FeatureClass(ws.createFeatureClass(fcName, fields, null, null, esriFeatureType.esriFTSimple, "shape", "default"));
    System.out.println("Done.");

    System.out.print("Adding \"FeatureValidator\" extension to newly created feature class \"" + fcName + "\"...");
    if(fc.getEXTCLSID() == null)
    {
      fc.changeSchemaLock(esriSchemaLock.esriExclusiveSchemaLock);

      // Create a new unique identifier object (UID) and assign the GUID to it.
      UID extUID = new UID();
      extUID.setValue(this.ceClass);

      fc.alterClassExtensionCLSID(extUID, null);
      fc.changeSchemaLock(esriSchemaLock.esriSharedSchemaLock);
    }
    else
    {
      System.out.println("Cannot alter EXTCLSID for feature class. One already exists");
    }
    System.out.println("Done.");

    System.out.print("Adding a feature to " + fcName + "...");
    ws.startEditing(false);
    ws.startEditOperation();

    Feature newFeature = null;
    try
    {
      newFeature = (Feature) fc.createFeature();

      Point p1 = new Point();
      p1.setX(-134.5184);
      p1.setY(43.9588);

            Point p2 = new Point();
            p2.setX(-134.0925);
            p2.setY(39.9838);

            Point p3 = new Point();
            p3.setX(-129.8336);
            p3.setY(39.9838);

            Point p4 = new Point();
            p4.setX(-130.1175);
            p4.setY(42.1133);

      Polygon polygon = new Polygon();
      polygon.addPoint(p1, null, null);
      polygon.addPoint(p2, null, null);
      polygon.addPoint(p3, null, null);
      polygon.addPoint(p4, null, null);
      polygon.setSpatialReferenceByRef(fc.getSpatialReference());

      newFeature.setShapeByRef(polygon);

      /*
       * The correct value below is "res". "com" should cause the FeatureValidator class
       * extension to output an error message, thus validating the feature. You can add
       * more functionality to your class extension to suit your purposes, such as deleting
       * a feature if any attribute value is invalid.
       */
      newFeature.setValue(2, "myemailiddomain.com");//correct email id is myemailid@domain.com
      newFeature.store();

      ws.stopEditOperation();
      ws.stopEditing(true);
    }
    catch(Exception e)
    {
      if(ws != null && ws.isBeingEdited())
      {
        ws.stopEditing(false);
        fc.delete();
        ws.delete();
      }

      e.printStackTrace();
    }
    System.out.println("Done.");

    System.out.print("Validating recently added feature...\n\n");
    String[] errMessages = new String[1];
    newFeature.validate(errMessages);

    //if error message is blank, the feature is valid
    System.out.println("errMessages (if the \"email\" attribute is invalid): " + errMessages[0].toString());

    System.out.println("\nFeatureValidatorRunner done.");
    System.out.println("Check the results in the following location: " + outputPath);
  }

  /*
   * Returns valid jar file path
   * @param jarFileName
   * @return
   */
  private boolean isJarFileValid(String jarFilePath) throws FileNotFoundException
  {
    if(jarFilePath.length() == 0 || jarFilePath == null)
    {
      System.err.println("Error: Jar file path is empty, null or it contains invalid/unsupported characters.");
      System.exit(-1);
    }
    else
    {      
      File jarFile = new File(jarFilePath);
      if(jarFile.exists())
      {
        if(!jarFile.isFile() && !jarFilePath.endsWith(".jar"))
        {
          throw new FileNotFoundException("Jar file " + jarFile + " not found. Please verify if name of jar file containing Class Extension is correct.");
        }
      }
    }
    
    return true;
  }
  
  /*
   * Retrieve list of valid SOE classes in specified jar file
   */
  private ArrayList<Class> getCEClass(String jarFilePath)
  {
    ArrayList<Class> ceClassList = new ArrayList<Class>(5);
    
    //validate input path
    if(System.getProperty("os.name").toLowerCase().startsWith("windows"))
    {
      if(jarFilePath.contains("Program Files"))
      {
        jarFilePath = jarFilePath.replace("Program Files", "Progra~1");
      }
      else if(jarFilePath.contains("Program Files (x86)"))
      {
        jarFilePath = jarFilePath.replace("Program Files (x86)", "Progra~2");
      } 
    }    
        
    //validate file indicated by jar file name. 
    try
    {        
      //load classes present inside jar file into memory
      Bootstrapper.loadExtensionJar(jarFilePath);        
      
      //retrieve individual contents of jar file
      JarFile jarFile = new JarFile(jarFilePath);
      Enumeration<JarEntry> jarContents = jarFile.entries();
      while (jarContents.hasMoreElements())
      {
        ZipEntry zipEntry = (ZipEntry) jarContents.nextElement();
        if(!zipEntry.isDirectory())
        {
          //ignore current file if it does not end with .class
          String fileName = zipEntry.getName();
          if(fileName.endsWith(".class"))
          {
            //retrieve the Class object for current class file
            String ceFullyQualifiedName = fileName.replace('/', '.').substring(0, fileName.length() -  ".class".length());
            Class classDef = Class.forName(ceFullyQualifiedName);
            
            //consider class only if its not an interface and its annotated with ArcGISExtension
            if(!classDef.isInterface() && classDef.isAnnotationPresent(ArcGISExtension.class))
            {
              //add the class to a list
              ceClassList.add(classDef);
            }
          }
        }
      }
      
      //trim the list
      ceClassList.trimToSize();
      
      //if list is of size 0
      if(ceClassList.size() <= 0)
      {
        System.err.println("\nError: No .class files or .class files containing Class Extension found in " + jarFilePath);
      }        
      
      //return it
      return ceClassList;
    }
    catch (IOException e)
    {
      e.printStackTrace();
    }
    catch(ClassNotFoundException ce)
    {
      ce.printStackTrace();
    }
    

    return null;
  }  
  
  /**
   * Empties specified directory of all files
   * @param dirName String
   */
  public void emptyFolder(String dirName, boolean includeSubFolder) throws Exception
  {
    File src = new File(dirName);
    if (src.isDirectory() && src.exists())
    {
      File list[] = src.listFiles();
      for (int i = 0; i < list.length; i++)
      {
        if (includeSubFolder && list[i].isDirectory())
        {
          emptyFolder(list[i].getPath(), includeSubFolder);
          list[i].delete();
        }
        else
        {
          list[i].delete();
        }
      }
    }
    else
    {
      throw new Exception("FeatureValidationRunner.emptyFolder() - " + dirName + " is not a directory or does not exist.");
    }
  }

  /**
   * Empties specified directory of all files
   * @param dirName String
   */
  public void deleteFolder(String dirName) throws Exception
  {
    File src = new File(dirName);
    if (src.isDirectory() && src.exists())
    {
      File list[] = src.listFiles();
      for (int i = 0; i < list.length; i++)
      {
        if (list[i].isDirectory())
        {
          emptyFolder(list[i].getPath(), true);
          list[i].delete();
        }
        else
        {
          list[i].delete();
        }
      }
      src.delete();
    }
    else
    {
      src.delete();
    }
  }

  /**
   * Empties specified directory of all files
   * @param dirName String
   */
  public void deleteFile(String fullyQualifiedFileName) throws Exception
  {
    File file = new File(fullyQualifiedFileName);
    if(!file.isDirectory() && file.exists())
    {
      file.delete();
    }
    else
    {
      throw new Exception("FeatureValidationRunner.deleteFile() - File " + fullyQualifiedFileName + " does not exist or is a directory.");
    }
  }
}