/*
# Licensed Materials - Property of IBM
# Copyright IBM Corp. 2015
*/
package com.ibm.streamsx.topology.inet;
import java.util.HashMap;
import java.util.Map;
import com.ibm.json.java.JSONObject;
import com.ibm.streams.operator.Tuple;
import com.ibm.streamsx.topology.TSink;
import com.ibm.streamsx.topology.TStream;
import com.ibm.streamsx.topology.TWindow;
import com.ibm.streamsx.topology.Topology;
import com.ibm.streamsx.topology.TopologyElement;
import com.ibm.streamsx.topology.context.Placeable;
import com.ibm.streamsx.topology.json.JSONStreams;
import com.ibm.streamsx.topology.spl.SPL;
import com.ibm.streamsx.topology.spl.SPLStream;
import com.ibm.streamsx.topology.spl.SPLStreams;
import com.ibm.streamsx.topology.spl.SPLWindow;
import com.ibm.streamsx.topology.tuple.JSONAble;
/**
* Adds a HTTP REST server to a topology to provide external interaction with
* the application.
*
* @see <a
* href="http://ibmstreams.github.io/streamsx.inet/">com.ibm.streamsx.inet</a>
*/
public class RestServer {
private final Topology topology;
private final int port;
private Placeable<?> firstInvocation;
/**
* Create a REST server that will listen on port 8080.
*
* @param te
* Topology to contain the REST server.
*/
public RestServer(TopologyElement te) {
this(te, 8080);
}
/**
* Create a REST server that will listen on the specified port.
*
* @param te
* Topology to contain the REST server.
* @param port
* Port to listen on.
*/
public RestServer(TopologyElement te, int port) {
this.topology = te.topology();
this.port = port;
}
/**
* Get the topology for this REST server.
*
* @return Topology for this server.
*/
public Topology topology() {
return topology;
}
/**
* Declare a HTTP REST view of tuples in a window. A HTTP GET to the the
* server at runtime will return the current contents of the window as an
* array of JSON objects, with each tuple converted to a JSON object.
*
* <BR>
* Conversion to JSON depends on the type of stream
* that populates {@code window}.
* <UL>
* <LI>
* {@code TStream<JSONObject>} - Each tuple is the JSON object.
* </LI>
* <LI>
* {@code TStream<? extends JSONAble>} - Each tuple is converted to a JSON object
* using {@link JSONAble#toJSON()}.
* </LI>
* <LI>
* {@code TStream<String>} - Each tuple will be converted to a JSON object with a single
* attribute '{@code string}' with the tuple's value.
* </LI>
* <LI>{@link com.ibm.streamsx.topology.spl.SPLStream SPLStream} -
* Each {@code Tuple} is converted to JSON using the encoding provided
* by the Java Operator API {@code com.ibm.streams.operator.encoding.JSONEncoding}.
* </LI>
* </UL>
* Other stream types are not supported.
*
* @param window
* Window to expose through the HTTP GET
* @param context
* Context path for the URL to access the resource.
* @param name
* Relative name (to {@code context}) of the resource.
*
* @return null (future will be the path to the tuples)
*/
@SuppressWarnings("unchecked")
public String viewer(TWindow<?,?> window, String context,
String name) {
if (window.getStream() instanceof SPLStream)
return viewerSpl((TWindow<Tuple,?>) window, context, name);
if (String.class.equals(window.getTupleClass())) {
return viewerString((TWindow<String,?>) window, context, name);
}
if (JSONObject.class.equals(window.getTupleClass())) {
return viewerJSON((TWindow<JSONObject,?>) window, context, name);
}
if (JSONAble.class.isAssignableFrom(window.getTupleClass())) {
return viewerJSONable((TWindow<? extends JSONAble,?>) window, context, name);
}
throw new IllegalArgumentException("Stream type not yet supported!:"
+ window.getTupleClass());
}
private String viewerJSON(TWindow<JSONObject,?> window,
String context, String name) {
SPLStream splStream = JSONStreams.toSPL(window.getStream());
return viewerSpl(splStream.window(window), context, name);
}
private String viewerJSONable(TWindow<? extends JSONAble,?> window,
String context, String name) {
TStream<JSONObject> jsonStream = JSONStreams.toJSON(window.getStream());
SPLStream splStream = JSONStreams.toSPL(jsonStream);
return viewerSpl(splStream.window(window), context, name);
}
private String viewerString(TWindow<String,?> window,
String context, String name) {
SPLStream splStream = SPLStreams.stringToSPLStream(window.getStream());
return viewerSpl(splStream.window(window), context, name);
}
private String viewerSpl(TWindow<Tuple,?> window, String context, String name) {
assert window.getStream() instanceof SPLStream;
SPLWindow splWindow = SPLStreams.triggerCount(window, 1);
Map<String, Object> params = new HashMap<>();
params.put("port", port);
params.put("context", context);
params.put("contextResourceBase", "opt/html/" + context);
TSink tv = SPL.invokeSink(name, "com.ibm.streamsx.inet.rest::HTTPTupleView",
splWindow,
params);
if (firstInvocation == null)
firstInvocation = tv;
else
firstInvocation.colocate(tv);
// Always mapping to a single operator per window
// so currently it's always port 0.
return name + "/ports/input/0";
}
}