package water.api; import water.*; import water.H2O.H2OCountedCompleter; import water.exceptions.H2OIllegalArgumentException; import water.exceptions.H2OKeyNotFoundArgumentException; import water.exceptions.H2OKeyWrongTypeArgumentException; import water.util.Log; import water.util.PojoUtils; import water.util.ReflectionUtils; import water.util.annotations.IgnoreJRERequirement; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Properties; public class Handler extends H2OCountedCompleter<Handler> { public static Class<? extends Schema> getHandlerMethodInputSchema(Method method) { return (Class<? extends Schema>)ReflectionUtils.findMethodParameterClass(method, 1); } public static Class<? extends Schema> getHandlerMethodOutputSchema(Method method) { return (Class<? extends Schema>)ReflectionUtils.findMethodOutputClass(method); } // Invoke the handler with parameters. Can throw any exception the called handler can throw. public Schema handle(int version, Route route, Properties parms, String post_body) throws Exception { Class<? extends Schema> handler_schema_class = getHandlerMethodInputSchema(route._handler_method); Schema schema = Schema.newInstance(handler_schema_class); // If the schema has a real backing class fill from it to get the default field values: Class<? extends Iced> iced_class = schema.getImplClass(); if (iced_class != Iced.class) { Iced defaults = schema.createImpl(); schema.fillFromImpl(defaults); } boolean is_post_of_json = (null != post_body); // Fill from http request params: schema = schema.fillFromParms(parms, !is_post_of_json); if (schema == null) throw H2O.fail("fillFromParms returned a null schema for version: " + version + " in: " + this.getClass() + " with params: " + parms); // Fill from JSON body, if there is one. NOTE: there should *either* be a JSON body *or* parms, // with the exception of control-type query parameters. // // We use PojoUtils.fillFromJson() rather than just using "schema = Gson.fromJson(post_body)" // so that we have defaults: we only overwrite fields that the client has specified. if (is_post_of_json) { PojoUtils.fillFromJson(schema, post_body); } // NOTE! The handler method is free to modify the input schema and hand it back. Schema result = null; try { route._handler_method.setAccessible(true); result = (Schema)route._handler_method.invoke(this, version, schema); } // Exception thrown out of the invoked method turn into InvocationTargetException // rather uselessly. Peel out the original exception & throw it. catch( InvocationTargetException ite ) { Throwable t = ite.getCause(); if( t instanceof RuntimeException ) throw (RuntimeException)t; if( t instanceof Error ) throw (Error)t; throw new RuntimeException(t); } // Version-specific unwind from the Iced back into the Schema return result; } @IgnoreJRERequirement protected StringBuffer markdown(Handler handler, int version, StringBuffer docs, String filename) { // TODO: version handling StringBuffer sb = new StringBuffer(); Path path = Paths.get(filename); try { sb.append(Files.readAllBytes(path)); } catch (IOException e) { Log.warn("Caught IOException trying to read doc file: ", path); } if (docs != null) docs.append(sb); return sb; } public static <T extends Keyed> T getFromDKV(String param_name, String key, Class<T> klazz) { return getFromDKV(param_name, Key.make(key), klazz); } public static <T extends Keyed> T getFromDKV(String param_name, Key key, Class<T> klazz) { if (key == null) throw new H2OIllegalArgumentException(param_name, "Handler.getFromDKV()", "null"); Value v = DKV.get(key); if (v == null) throw new H2OKeyNotFoundArgumentException(param_name, key.toString()); try { return klazz.cast(v.get()); } catch (ClassCastException e) { throw new H2OKeyWrongTypeArgumentException(param_name, key.toString(), klazz, v.get().getClass()); } } }