title: Deploying H2O-generated POJO to Jetty: /predict returns 404
body: <p>I have generated a POJO from an H2O model.</p>
  
  <p>On a separate EC2 instance, I want to deploy this model to a Jetty servelet to be used as an API scoring endpoint.  How can I do this?</p>
  
  <h3>1. Example POJO</h3>
  
  <p>Here is a minimal example, <code>demo_glm</code>, predicting Titanic passenger survival as a logistic regression on age, fare class, and sex:</p>
  
  <pre class="lang-java prettyprint-override"><code>/*
    Licensed under the Apache License, Version 2.0
      http://www.apache.org/licenses/LICENSE-2.0.html
  
    AUTOGENERATED BY H2O at 2017-09-13T17:30:17.931Z
    3.13.0.3908
  
    Standalone prediction code with sample test data for GLMModel named demo_glm
  
    How to download, compile and execute:
        mkdir tmpdir
        cd tmpdir
        curl http://XXX.XX.XX.XXX:54321/3/h2o-genmodel.jar &gt; h2o-genmodel.jar
        curl http://XXX.XX.XX.XXX:54321/3/Models.java/demo_glm &gt; demo_glm.java
        javac -cp h2o-genmodel.jar -J-Xmx2g -J-XX:MaxPermSize=128m demo_glm.java
  
       (Note:  Try java argument -XX:+PrintCompilation to show runtime JIT compiler behavior.)
  */
  import java.util.Map;
  import hex.genmodel.GenModel;
  import hex.genmodel.annotations.ModelPojo;
  
  @ModelPojo(name="demo_glm", algorithm="glm")
  public class demo_glm extends GenModel {
    public hex.ModelCategory getModelCategory() { return hex.ModelCategory.Binomial; }
  
    public boolean isSupervised() { return true; }
    public int nfeatures() { return 3; }
    public int nclasses() { return 2; }
  
    // Names of columns used by model.
    public static final String[] NAMES = NamesHolder_demo_glm.VALUES;
    // Number of output classes included in training data response column.
    public static final int NCLASSES = 2;
  
    // Column domains. The last array contains domain of response column.
    public static final String[][] DOMAINS = new String[][] {
      /* pclass */ demo_glm_ColInfo_0.VALUES,
      /* sex */ demo_glm_ColInfo_1.VALUES,
      /* age */ null,
      /* survived */ demo_glm_ColInfo_3.VALUES
    };
    // Prior class distribution
    public static final double[] PRIOR_CLASS_DISTRIB = null;
    // Class distribution used for model building
    public static final double[] MODEL_CLASS_DISTRIB = null;
  
    public demo_glm() { super(NAMES,DOMAINS); }
    public String getUUID() { return Long.toString(-1806915013443955212L); }
  
    // Pass in data in a double[], pre-aligned to the Model's requirements.
    // Jam predictions into the preds[] array; preds[0] is reserved for the
    // main prediction (class for classifiers or value for regression),
    // and remaining columns hold a probability distribution for classifiers.
    public final double[] score0( double[] data, double[] preds ) {
      final double [] b = BETA.VALUES;
      for(int i = 0; i &lt; 2; ++i) if(Double.isNaN(data[i])) data[i] = CAT_MODES.VALUES[i];
      for(int i = 0; i &lt; 1; ++i) if(Double.isNaN(data[i + 2])) data[i+2] = NUM_MEANS.VALUES[i];
      double eta = 0.0;
      for(int i = 0; i &lt; CATOFFS.length-1; ++i) if(data[i] != 0) {
        int ival = (int)data[i] - 1;
        if(ival != data[i] - 1) throw new IllegalArgumentException("categorical value out of range");
        ival += CATOFFS[i];
        if(ival &lt; CATOFFS[i + 1])
          eta += b[ival];
      }
      for(int i = 2; i &lt; b.length-1-1; ++i)
      eta += b[1+i]*data[i];
      eta += b[b.length-1]; // reduce intercept
      double mu = hex.genmodel.GenModel.GLM_logitInv(eta);
      preds[0] = (mu &gt;= 0.3701702514726391) ? 1 : 0; // threshold given by ROC
      preds[1] = 1.0 - mu; // class 0
      preds[2] =       mu; // class 1
      return preds;
    }
      public static class BETA implements java.io.Serializable {
        public static final double[] VALUES = new double[5];
        static {
          BETA_0.fill(VALUES);
        }
        static final class BETA_0 implements java.io.Serializable {
          static final void fill(double[] sa) {
            sa[0] = -1.280567936795408;
            sa[1] = -2.2896567020762353;
            sa[2] = -2.4978421167555616;
            sa[3] = -0.034393168117166584;
            sa[4] = 3.5220688949789816;
          }
        }
  }
  // Imputed numeric values
      static class NUM_MEANS implements java.io.Serializable {
        public static final double[] VALUES = new double[1];
        static {
          NUM_MEANS_0.fill(VALUES);
        }
        static final class NUM_MEANS_0 implements java.io.Serializable {
          static final void fill(double[] sa) {
            sa[0] = 29.881134512434045;
          }
        }
  }
  // Imputed categorical values.
      static class CAT_MODES implements java.io.Serializable {
        public static final int[] VALUES = new int[2];
        static {
          CAT_MODES_0.fill(VALUES);
        }
        static final class CAT_MODES_0 implements java.io.Serializable {
          static final void fill(int[] sa) {
            sa[0] = 2;
            sa[1] = 1;
          }
        }
  }
      // Categorical Offsets
      public static final int[] CATOFFS = {0,2,3};
  }
  // The class representing training column names
  class NamesHolder_demo_glm implements java.io.Serializable {
    public static final String[] VALUES = new String[3];
    static {
      NamesHolder_demo_glm_0.fill(VALUES);
    }
    static final class NamesHolder_demo_glm_0 implements java.io.Serializable {
      static final void fill(String[] sa) {
        sa[0] = "pclass";
        sa[1] = "sex";
        sa[2] = "age";
      }
    }
  }
  // The class representing column pclass
  class demo_glm_ColInfo_0 implements java.io.Serializable {
    public static final String[] VALUES = new String[3];
    static {
      demo_glm_ColInfo_0_0.fill(VALUES);
    }
    static final class demo_glm_ColInfo_0_0 implements java.io.Serializable {
      static final void fill(String[] sa) {
        sa[0] = "1st";
        sa[1] = "2nd";
        sa[2] = "3rd";
      }
    }
  }
  // The class representing column sex
  class demo_glm_ColInfo_1 implements java.io.Serializable {
    public static final String[] VALUES = new String[2];
    static {
      demo_glm_ColInfo_1_0.fill(VALUES);
    }
    static final class demo_glm_ColInfo_1_0 implements java.io.Serializable {
      static final void fill(String[] sa) {
        sa[0] = "female";
        sa[1] = "male";
      }
    }
  }
  // The class representing column survived
  class demo_glm_ColInfo_3 implements java.io.Serializable {
    public static final String[] VALUES = new String[2];
    static {
      demo_glm_ColInfo_3_0.fill(VALUES);
    }
    static final class demo_glm_ColInfo_3_0 implements java.io.Serializable {
      static final void fill(String[] sa) {
        sa[0] = "0";
        sa[1] = "1";
      }
    }
  }
  </code></pre>
  
  <h3>2. What I've tried so far</h3>
  
  <p>After installing Jetty on a separate EC2 instance, I follow the instructions in the comments above:</p>
  
  <pre><code>cd $JETTY_HOME/demo-base/webapps
  mkdir model_demo
  cd model_demo
  curl http://XXX.XX.XX.XXX:54321/3/h2o-genmodel.jar &gt; h2o-genmodel.jar
  curl http://XXX.XX.XX.XXX:54321/3/Models.java/demo_glm &gt; demo_glm.java
  javac -cp h2o-genmodel.jar -J-Xmx2g -J-XX:MaxPermSize=128m 
  </code></pre>
  
  <p>I can access the servelet at <code>http://XXX.XX.XX.YY:8080/</code> and see the "Welcome to Jetty-9" screen in browser or via curl.</p>
  
  <p>My problem is that when I try:</p>
  
  <pre><code>curl http://XXX.XX.XX.YY:8080/predict?pclass=1st&amp;age=29&amp;sex=female
  </code></pre>
  
  <p>or:</p>
  
  <pre><code>curl http://XXX.XX.XX.YY:8080/model_demo/predict?pclass=1st&amp;age=29&amp;s‌​ex=female
  </code></pre>
  
  <p>I get:</p>
  
  <blockquote>
    <p>HTTP ERROR 404</p>
    
    <p>Problem accessing /predict. Reason:</p>
  
  <pre><code>  Not Found 
  </code></pre>
  </blockquote>