Commit efc4e0a7 authored by R.W.Majeed's avatar R.W.Majeed

li2b2 moved to separate projects hosted at github/rwm/li2b2

parent 67620baf
package de.sekmi.histream.i2b2.services;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import javax.annotation.Resource;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.ws.Endpoint;
import javax.xml.ws.Provider;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.http.HTTPBinding;
/**
* Provides REST interfaces to emulate an i2b2 server.
* @author Raphael
*
*/
//@WebServiceProvider()
//@ServiceMode(value = Service.Mode.MESSAGE)
//@BindingType(HTTPBinding.HTTP_BINDING)
public class HiveServer implements Provider<Source>{
private ScriptEngineManager scriptManager;
private ScriptEngine scriptEngine;
private Path scriptDir;
private WatchService watcher;
@Resource
protected WebServiceContext context;
public HiveServer() {
scriptManager = new ScriptEngineManager();
// set bindings for callbacks to java code
//scriptManager.setBindings(bindings);
//scriptEngine = scriptManager.getEngineByName("nashorn");
// load scripts
}
public void loadMainScript() throws IOException, ScriptException{
scriptEngine = scriptManager.getEngineByName("nashorn");
try( Reader scriptFile = new FileReader(scriptDir.resolve("main.js").toFile()) ) {
scriptEngine.eval(scriptFile);
Object o = scriptEngine.eval("typeof httpRequest === 'function'");
if( o == null || !(o instanceof Boolean) || ((Boolean)o) != true ){
throw new ScriptException("global function 'httpRequest(?,?,?,?)' needed");
}
}
}
public void reloadChangesLoop(){
while( true ){
WatchKey key;
try {
key = watcher.take();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
break;
}
System.out.println("Reloading scripts..");
try {
loadMainScript();
} catch (IOException | ScriptException e1) {
System.err.println("Error loading script");
e1.printStackTrace();
}
// pollEvents() is needed for the key to reset properly
for( WatchEvent<?> e : key.pollEvents() ){
if( e == null )continue;// noop
//System.out.println("Event: "+e.kind().toString() + ", "+e.toString());
}
if( !key.reset() ){
System.err.println("Key closed");
break;
}
}
}
@SuppressWarnings("resource") // cannot close default filesystem
public void loadScipts()throws IOException, ScriptException{
FileSystem fs = FileSystems.getDefault();
scriptDir = fs.getPath("src", "main", "scripts", "i2b2-ws");
if( !Files.isDirectory(scriptDir) )throw new FileNotFoundException("Script dir not found: "+scriptDir);
loadMainScript();
// TODO: watch directory and reload script if changes
watcher = fs.newWatchService();
scriptDir.register(watcher, StandardWatchEventKinds.ENTRY_MODIFY);
/*
try( DirectoryStream<Path> dir = Files.newDirectoryStream(scriptDir, "*.js") ){
for( Path script : dir ){
System.out.println("Script: "+script.normalize().toString());
ScriptEngine se = scriptManager.getEngineByName("nashorn");
try {
se.eval(new FileReader(script.toFile()));
System.out.println("cell.name="+se.eval("this.cell.id").toString());
} catch (ScriptException e) {
e.printStackTrace();
}
}
}*/
}
public static void main(String args[]) throws IOException, ScriptException{
HiveServer hs = new HiveServer();
hs.loadScipts();
Endpoint e = Endpoint.create(HTTPBinding.HTTP_BINDING, hs);
// use executor for more control over parallel executions
//e.setExecutor(/*...*/);
String address = "http://localhost:9000/i2b2/services";
e.publish(address);
hs.reloadChangesLoop();
}
@Override
public StreamSource invoke(Source request) {
// TODO Auto-generated method stub
MessageContext mc = context.getMessageContext();
String path = (String)mc.get(MessageContext.PATH_INFO);
String query = (String)mc.get(MessageContext.QUERY_STRING);
String httpMethod = (String)mc.get(MessageContext.HTTP_REQUEST_METHOD);
if( request != null ){
System.out.println("Source: "+request.getClass());
}
if( scriptEngine != null )try {
Object ret = ((Invocable)scriptEngine).invokeFunction("httpRequest", httpMethod, path, query, request);
return new StreamSource(new StringReader(ret.toString()));
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ScriptException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
/*
@SuppressWarnings("unchecked")
java.util.Map<java.lang.String, java.util.List<java.lang.String>> headers =
(Map<String, List<String>>) context.getMessageContext().get(MessageContext.HTTP_RESPONSE_HEADERS);
headers.put("Content-Type", Arrays.asList("text/html"));*/
return new StreamSource(new StringReader("<!DOCTYPE html><html><head></head><body>ERROR</body></html>"));
}
}
.settings/
.classpath
.project
target/
i2b2 client implementation
--------------------------
Lightweight client implementation to authenticate with
an i2b2 hive, manage users/roles and run queries.
\ No newline at end of file
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<name>HIStream : li2b2 : API</name>
<packaging>jar</packaging>
<description>
li2b2 server backend API. Implement this
to run a custom i2b2 server.
</description>
<groupId>de.sekmi.histream.li2b2</groupId>
<artifactId>li2b2-api</artifactId>
<version>0.8-SNAPSHOT</version>
<parent>
<groupId>de.sekmi.histream.li2b2</groupId>
<artifactId>pom</artifactId>
<version>0.8-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package de.sekmi.histream.i2b2.api.crc;
import java.util.List;
public interface QueryInstance {
String getId(); // instance id
QueryStatus getStatus();
List<QueryResult> getResults();
}
package de.sekmi.histream.i2b2.api.crc;
import java.util.List;
import org.w3c.dom.Element;
public interface QueryManager {
QueryMaster runQuery(Element definition, List<ResultType> results);
QueryMaster getQuery(String queryId);
/**
* List queries for user
* @param userId user id
* @return queries
*/
Iterable<QueryMaster> listQueries(String userId);
/**
* Return supported result types
* @return result types
*/
Iterable<ResultType> getResultTypes();
}
package de.sekmi.histream.i2b2.api.crc;
import java.time.Instant;
import org.w3c.dom.Element;
public interface QueryMaster {
String getId();
String getDisplayName();
String getUser();
Element getDefinition();
Instant getCreateDate();
/**
* Get the instance/execution for the query.
* XXX maybe add support for multiple executions later
* @return execution instance
*/
QueryInstance getInstance();
}
package de.sekmi.histream.i2b2.api.crc;
import java.time.Instant;
public interface QueryResult {
String getId(); // result id
String getDescription();
ResultType getResultType();
Integer getSetSize();
Instant getStartDate();
Instant getEndDate();
QueryStatus getStatus();
}
package de.sekmi.histream.i2b2.api.crc;
public enum QueryStatus {
ERROR, COMPLETED(6), FINISHED(3), INCOMPLETE, WAITTOPROCESS, PROCESSING;
int typeId;
private QueryStatus(int typeId){
this.typeId = typeId;
}
private QueryStatus(){
this.typeId = 0;
}
}
package de.sekmi.histream.i2b2.api.crc;
public interface ResultType {
Integer getId();
String getName();
String getDescription();
}
package de.sekmi.histream.i2b2.api.ont;
public interface Concept {
String getKey();
String getDisplayName();
default Integer getTotalNum(){return null;}
default Concept getSynonymTarget(){return null;}
boolean hasNarrower();
Iterable<Concept> getNarrower();
boolean hasModifiers();
Iterable<Modifier> getModifiers();
/* TODO implement <facttablecolumn>concept_cd</facttablecolumn>
<tablename>concept_dimension</tablename>
<columnname>concept_path</columnname>
<columndatatype>T</columndatatype>
<operator>LIKE</operator>
<dimcode>\i2b2\Diagnoses\Conditions in the perinatal period (760-779)\</dimcode>
<tooltip> */
}
package de.sekmi.histream.i2b2.api.ont;
public interface Modifier {
}
package de.sekmi.histream.i2b2.api.ont;
public interface Ontology {
Iterable<Concept> getCategories();
Concept getConceptByKey(String key);
}
/**
* This package and its sub packages contain interfaces
* to implement backend functionality for the i2b2 server
* emulation.
* <p>
* </P>
*/
package de.sekmi.histream.i2b2.api;
package de.sekmi.histream.i2b2.api.pm;
public interface Project {
/**
* Unique id
* @return id
*/
String getId();
/**
* Short human readable name for the project.
* @return name
*/
String getName();
void addUserRole(User user, String role);
Iterable<String> getUserRoles(User user);
// TODO removeUser (removes all roles), removeUserRole
}
package de.sekmi.histream.i2b2.api.pm;
/**
* User authorisation and association of projects
*
* @author R.W.Majeed
*
*/
public interface ProjectManager {
User getUserById(String userId);
Project getProjectById(String projectId);
}
package de.sekmi.histream.i2b2.api.pm;
public interface SessionManager {
public interface Session{
String getId();
String getUserId();
String getProjectId();
// XXX set project id? or set at creation?
long getCreationTime();
long getLastAccess();
}
/**
* Access an existing session by its session id.
* This operation will also update the {@link Session#getLastAccess()}
* time stamp.
*
* @param sessionId session id
* @return session or {@code null} if not available or expired.
*/
public Session accessSession(String sessionId);
public Session createSession(String userId);
public void deleteSession(String sessionId);
/**
* Get the timeout duration in milliseconds. Sessions
* last accessed longer than this will be discarded.
* @return timeout in milliseconds
*/
public long getTimeoutMillis();
/**
* Iterate over all sessions. The remove() operation should
* be supported by the iterators.
* @return all sessions
*/
Iterable<Session> allSessions();
}
package de.sekmi.histream.i2b2.api.pm;
import java.security.Principal;
public interface User extends Principal{
String getFullName();
boolean isAdmin();
Iterable<Project> getProjects();
// check password
boolean hasPassword(char[] password);
void setPassword(char[] newPassword);
}
.settings/
.classpath
.project
target/
i2b2 client implementation
--------------------------
Lightweight client implementation to authenticate with
an i2b2 hive, manage users/roles and run queries.
\ No newline at end of file
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<name>HIStream : li2b2 : client</name>
<packaging>jar</packaging>
<description>
Lightweight client implementation to authenticate with
an i2b2 hive, manage users/roles and run queries.
</description>
<groupId>de.sekmi.histream.li2b2</groupId>
<artifactId>li2b2-client</artifactId>
<version>0.8-SNAPSHOT</version>
<parent>
<groupId>de.sekmi.histream.li2b2</groupId>
<artifactId>pom</artifactId>
<version>0.8-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package de.sekmi.histream.li2b2.client;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Objects;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.transform.TransformerException;
import javax.xml.xpath.XPathExpressionException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class CellClient {
private static final Logger log = Logger.getLogger(CellClient.class.getName());
protected Client client;
protected URL serviceUrl;
public CellClient(Client client, URL serviceUrl){
this.client = client;
this.serviceUrl = serviceUrl;
}
protected String getOutputCharset(){
return client.getOutputEncoding();
}
protected URL createRequest(String path) throws MalformedURLException{
return new URL(serviceUrl, path);
}
protected DocumentBuilder newBuilder(){
return client.newBuilder();
}
/**
* Create a new request message. Will also set the security
* credentials.
*
* @param builder document builder
* @return request message
*/
protected Request createRequestMessage(DocumentBuilder builder){
Request req = client.createRequest(builder);
req.setSecurity(client.credentials);
req.setProjectId(client.getProjectId());
// TODO set message id
return req;
}
protected Request createRequestMessage(){
return createRequestMessage(newBuilder());
}
protected Request createRequestMessage(String bodyXML) throws SAXException, IOException{
DocumentBuilder b = newBuilder();
Request req = createRequestMessage(b);
// parse body XML
Document dom = b.parse(new InputSource(new StringReader(bodyXML)));
Element body = req.getMessageBody();
Objects.requireNonNull(body, "no message_body");
// move parsed DOM into message_body
body.appendChild(body.getOwnerDocument().adoptNode(dom.getDocumentElement()));
return req;
}
/**
* Create the HTTP connection. Will use the proxy specified by
* the client and fill the redirect URL in the message header.
* <p>
* This method will also prepare the returned connection for
* the {@link URLConnection#connect()} call. Specifically,
* the request method will be set to {@code POST}, and the
* {@code Content-Type} header will be set to {@code application/xml}
* with output charset.
* </p>
* @param request request
* @param requestUrl URL
* @throws IOException io error
*/
protected HttpURLConnection createConnection(Request request, URL requestUrl) throws IOException{
HttpURLConnection c;
if( client.getProxy() != null ){
request.setRedirectUrl(requestUrl);
c = (HttpURLConnection)client.getProxy().openConnection();
}else{
// clear redirect URL
request.setRedirectUrl(null);
c = (HttpURLConnection)requestUrl.openConnection();
}
c.setRequestMethod("POST");
c.setDoOutput(true);
c.setRequestProperty("Content-Type", "application/xml; charset="+getOutputCharset());
return c;
}
protected Response submitRequest(Request request, String method) throws MalformedURLException, IOException{
return submitRequest(newBuilder(), request, createRequest(method));
}
protected Response submitRequest(DocumentBuilder b, Request request, URL requestUrl) throws IOException{
HttpURLConnection c = createConnection(request, requestUrl);
c.connect();
OutputStream out = c.getOutputStream();
try {
log.info("Submitting to "+requestUrl);
DOMUtils.printDOM(request.dom, System.out);
DOMUtils.printDOM(request.dom, out, getOutputCharset());
} catch (TransformerException e) {
throw new IOException(e);
}
out.close();
int status = c.getResponseCode();
// check status code for failure
if( status != 200 ){
throw new IOException("Unexpected HTTP response code "+status);
}
// System.out.println("Response:"+status);
InputStream in = c.getInputStream();
Document resp;
try {
resp = b.parse(in);
DOMUtils.stripWhitespace(resp.getDocumentElement());
} catch (SAXException | XPathExpressionException e) {
throw new IOException("Unable to parse response XML",e);
}
log.info("Received response:");
DOMUtils.printDOM(resp, System.out);
return new Response(resp);
}
}
package de.sekmi.histream.li2b2.client;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathExpressionException;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import de.sekmi.histream.li2b2.client.pm.UserConfiguration;
import de.sekmi.histream.li2b2.client.ont.OntologyClient;
import de.sekmi.histream.li2b2.client.pm.Cell;
import de.sekmi.histream.li2b2.client.pm.PMClient;
public class Client {
private static final Logger log = Logger.getLogger(Client.class.getName());
// configuration for connection
private URL proxy;
// information from server
UserConfiguration info;
Credentials credentials;
String projectId;
private PMClient pm;
private OntologyClient ont;
private Document requestTemplate;
private DocumentBuilderFactory factory;
private String outputEncoding;
public Client() throws IOException{
factory = DocumentBuilderFactory.newInstance();</