package water.api;
import water.H2O;
import water.Iced;
import water.util.MarkdownBuilder;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* Routing of an http request to a handler method, with path parameter parsing.
*/
public final class Route extends Iced {
static final int MIN_VERSION = 1;
// TODO: handlers are now stateless, so create a single instance and stash it here
// TODO: all fields should be final!
// TODO: remove no-args ctor, since it is not used
public String _http_method;
public String _url;
public String _summary;
public String _api_name;
public Class<? extends Handler> _handler_class;
public Method _handler_method;
// NOTE: Java 7 captures and lets you look up subpatterns by name but won't give you the list of names, so we need this redundant list:
public String[] _path_params; // list of params we capture from the url pattern, e.g. for /17/MyComplexObj/(.*)/(.*)
public Handler _handler;
private RequestUri _uri;
/** Handler factory configures a way how handler is instantiated.
*
* PLEASE: do not remove it even H2O is not using it. It is used by Sparkling Water, since
* it needs to pass a Spark context to a new handler
*/
final HandlerFactory _handler_factory;
public Route() {
_handler_factory = null;
}
public Route(RequestUri uri,
String api_name,
String summary,
Class<? extends Handler> handler_class,
String handler_method,
HandlerFactory handler_factory) {
assert uri != null && handler_class != null;
assert handler_factory != null : "handler_factory should not be null, caller has to pass it!";
_uri = uri;
_http_method = uri.getMethod();
_url = uri.getUrl();
_summary = summary;
_api_name = api_name;
_handler_class = handler_class;
_handler_method = resolveMethod(handler_class, handler_method == null? "exec" : handler_method);
_path_params = uri.getParamsList();
_handler_factory = handler_factory;
try {
_handler = _handler_factory.create(_handler_class);
} catch (Exception ie) {
throw H2O.fail("failed to register handler " + handler_class.getSimpleName() + "." + handler_method, ie);
}
}
public RequestUri getUri() { return _uri; }
public int getVersion() { return _uri.getVersion(); }
/**
* Generate Markdown documentation for this Route.
*/
public StringBuffer markdown(Schema sinput, Schema soutput) {
MarkdownBuilder builder = new MarkdownBuilder();
builder.comment("Preview with http://jbt.github.io/markdown-editor");
builder.heading1(_http_method, _url);
builder.hline();
builder.paragraph(_summary);
// parameters and output tables
builder.heading1("Input schema: ");
builder.append(sinput .markdown(true ,false));
builder.heading1("Output schema: ");
builder.append(soutput.markdown(false, true));
return builder.stringBuffer();
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Route)) return false;
Route route = (Route) o;
return _api_name.equals(route._api_name) &&
_handler_class .equals(route._handler_class) &&
_handler_method.equals(route._handler_method) &&
_http_method.equals(route._http_method) &&
_url.equals(route._url) &&
Arrays.equals(_path_params, route._path_params);
}
@Override
public int hashCode() {
return _api_name.hashCode();
}
@Override
public String toString() {
return "Route{" +
"_http_method='" + _http_method + '\'' +
", _url_pattern=" + _url +
", _summary='" + _summary + '\'' +
", _api_name='" + _api_name + "'" +
", _handler_class=" + _handler_class +
", _handler_method=" + _handler_method +
", _input_schema=" + Handler.getHandlerMethodInputSchema(_handler_method) +
", _output_schema=" + Handler.getHandlerMethodOutputSchema(_handler_method) +
", _path_params=" + Arrays.toString(_path_params) +
'}';
}
/**
* Search the provided class (and all its superclasses) for the requested method.
* @param handler_class Class to be searched
* @param handler_method Name of the method to look for. The method must have signature (int, Schema).
* @return The callable Method object.
*/
private static Method resolveMethod(Class<? extends Handler> handler_class, String handler_method) {
for (Method method : handler_class.getMethods())
if (method.getName().equals(handler_method)) {
Class[] pt = method.getParameterTypes();
if (pt != null && pt.length == 2 && pt[0] == Integer.TYPE && Schema.class.isAssignableFrom(pt[1]))
return method;
}
throw H2O.fail("Failed to find handler method: " + handler_method + " in class: " + handler_class.getSimpleName());
}
}