/*
* $Id$
*
* Authors:
* Jeff Buchbinder <jeff@freemedsoftware.org>
*
* REMITT Electronic Medical Information Translation and Transmission
* Copyright (C) 1999-2014 FreeMED Software Foundation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.remitt.server.impl;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Resource;
import javax.jws.WebService;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.handler.MessageContext;
import org.apache.cxf.headers.Header;
import org.apache.cxf.helpers.CastUtils;
import org.apache.cxf.jaxws.context.WrappedMessageContext;
import org.apache.cxf.message.Message;
import org.apache.log4j.Logger;
import org.remitt.datastore.DbEligibilityJob;
import org.remitt.datastore.DbFileStore;
import org.remitt.datastore.KeyringStore;
import org.remitt.datastore.UserManagement;
import org.remitt.prototype.ConfigurationOption;
import org.remitt.prototype.EligibilityInterface;
import org.remitt.prototype.EligibilityRequest;
import org.remitt.prototype.EligibilityResponse;
import org.remitt.prototype.FileListingItem;
import org.remitt.prototype.ParserInterface;
import org.remitt.prototype.PluginInterface;
import org.remitt.prototype.UserDTO;
import org.remitt.prototype.ValidationInterface;
import org.remitt.prototype.ValidationResponse;
import org.remitt.server.Configuration;
import org.remitt.server.DbUtil;
import org.remitt.server.Service;
@WebService(targetNamespace = "http://server.remitt.org/", endpointInterface = "org.remitt.server.Service", serviceName = "RemittService")
public class ServiceImpl implements Service {
@Resource
WebServiceContext context;
static final Logger log = Logger.getLogger(Service.class);
@GET
@Path("protocolversion")
@Produces("application/json")
public String getProtocolVersion() {
return "2.0";
}
@POST
@Path("changepassword/{pw}")
@Produces("application/json")
public Boolean changePassword(String newPassword) {
Connection c = getConnection();
String userName = getCurrentUserName();
Boolean returnValue = false;
PreparedStatement cStmt = null;
try {
cStmt = c
.prepareCall("UPDATE tUser SET passhash = MD5( ? ) WHERE username = ?;");
cStmt.setString(1, newPassword);
cStmt.setString(2, userName);
cStmt.execute();
returnValue = (cStmt.getUpdateCount() == 1);
} catch (NullPointerException npe) {
log.error("Caught NullPointerException", npe);
} catch (SQLException e) {
log.error("Caught SQLException", e);
} finally {
DbUtil.closeSafely(cStmt);
DbUtil.closeSafely(c);
}
return returnValue;
}
@GET
@Path("username")
@Produces("application/json")
public String getCurrentUserName() {
MessageContext ctx = context.getMessageContext();
if (ctx != null) {
// System.out.println("ctx != null");
// for (String k : ctx.keySet()) {
// System.out.println("Found key " + k + " in ctx");
// }
Message message = ((WrappedMessageContext) ctx).getWrappedMessage();
List<Header> headers = CastUtils.cast((List<?>) message
.get(Header.HEADER_LIST));
if (headers != null) {
for (Header h : headers) {
// System.out.println("Header " + h.getName().toString()
// + " = " + h.getObject().toString());
if (h.getName().toString().equals("X-Principal-Username")) {
log.info("Found user '" + (String) h.getObject()
+ " in X-Principal-Username header.");
return (String) h.getObject();
}
}
}
} else {
log.error("MessageContext is null!");
}
// Fall back if everything else fails!
log.error("Hack! Hack! We failed to get the username from the backend!");
return "Administrator"; // TODO: FIXME: BROKEN!!: XXX
}
@POST
@Path("submit")
@Produces("application/json")
public Integer insertPayload(String originalId, String inputPayload,
String renderPlugin, String renderOption, String transportPlugin,
String transportOption) {
Connection c = getConnection();
String userName = getCurrentUserName();
log.debug("Submit job for " + userName + " [payload length = "
+ inputPayload.length() + "]");
Integer returnValue = null;
PreparedStatement cStmt = null;
try {
cStmt = c.prepareStatement("INSERT INTO tPayload ( "
+ "user, payload, renderPlugin, renderOption, "
+ "transportPlugin, transportOption, originalId "
+ " ) VALUES ( ?, ?, ?, ?, ?, ?, ? );",
PreparedStatement.RETURN_GENERATED_KEYS);
cStmt.setString(1, userName);
cStmt.setString(2, inputPayload);
cStmt.setString(3, renderPlugin);
cStmt.setString(4, renderOption);
cStmt.setString(5, transportPlugin);
cStmt.setString(6, transportOption);
cStmt.setString(7, originalId);
@SuppressWarnings("unused")
boolean hadResults = cStmt.execute();
ResultSet newKey = cStmt.getGeneratedKeys();
newKey.next();
returnValue = newKey.getInt(1);
DbUtil.closeSafely(newKey);
} catch (NullPointerException npe) {
log.error("Caught NullPointerException", npe);
} catch (SQLException e) {
log.error("Caught SQLException", e);
} finally {
DbUtil.closeSafely(cStmt);
DbUtil.closeSafely(c);
}
return returnValue;
}
@POST
@Path("resubmit")
@Produces("application/json")
public Integer resubmitPayload(Integer originalPayloadId) {
Connection c = getConnection();
String userName = getCurrentUserName();
log.debug("Resubmit job for " + userName + " original id = "
+ originalPayloadId);
Integer returnValue = null;
PreparedStatement cStmt = null;
try {
cStmt = c.prepareStatement("INSERT INTO tPayload ( "
+ "user, payload, renderPlugin, renderOption, "
+ "transportPlugin, transportOption, originalId "
+ " ) SELECT user, payload, renderPlugin, renderOption, "
+ "transportPlugin, transportOption, originalId "
+ " FROM tPayload WHERE id = ? AND user = ?;",
PreparedStatement.RETURN_GENERATED_KEYS);
cStmt.setInt(1, originalPayloadId);
cStmt.setString(2, userName);
@SuppressWarnings("unused")
boolean hadResults = cStmt.execute();
ResultSet newKey = cStmt.getGeneratedKeys();
newKey.next();
returnValue = newKey.getInt(1);
DbUtil.closeSafely(newKey);
} catch (NullPointerException npe) {
log.error("Caught NullPointerException", npe);
} catch (SQLException e) {
log.error("Caught SQLException", e);
} finally {
DbUtil.closeSafely(cStmt);
DbUtil.closeSafely(c);
}
return returnValue;
}
@POST
@Path("getoptions")
@Produces("application/json")
public ConfigurationOption[] getConfigValues() {
String userName = getCurrentUserName();
return Configuration.getConfigValues(userName);
}
@POST
@Path("setoption/{namespace}/{option}/{value}")
@Produces("application/json")
public Boolean setConfigValue(String namespace, String option, String value) {
String userName = getCurrentUserName();
try {
Configuration.setConfigValue(userName, namespace, option, value);
} catch (Exception ex) {
log.error(ex);
return Boolean.FALSE;
}
return Boolean.TRUE;
}
@POST
@Path("getstatus/{jobId}")
@Produces("application/json")
public Integer getStatus(Integer jobId) {
String userName = getCurrentUserName();
log.info("getStatus called for user = " + userName + ", jobId = "
+ (jobId == null ? "null" : jobId.toString()));
Connection c = getConnection();
CallableStatement cStmt = null;
int returnValue = 5;
try {
cStmt = c.prepareCall("CALL p_GetStatus( ?, ? );");
cStmt.setString(1, userName);
cStmt.setInt(2, jobId);
boolean hadResults = cStmt.execute();
if (hadResults) {
ResultSet r = cStmt.getResultSet();
r.next();
Integer status = r.getInt("status");
String stage = r.getString("stage");
if (stage == null) {
// Handle null stage
} else {
if (status.equals(0)) {
if (stage.equalsIgnoreCase("validation")) {
returnValue = 1; // validation
} else if (stage.equalsIgnoreCase("render")) {
returnValue = 2; // render
} else if (stage.equalsIgnoreCase("translation")) {
returnValue = 3; // translation
} else if (stage.equalsIgnoreCase("transport")) {
returnValue = 4; // transport
} else if (stage.equalsIgnoreCase("failed")) {
returnValue = 6; // failed
}
} else {
returnValue = 0; // completed
}
}
r.close();
} else {
returnValue = 5; // unknown
}
} catch (NullPointerException npe) {
log.error("Caught NullPointerException", npe);
} catch (SQLException e) {
log.error("Caught SQLException", e);
} finally {
DbUtil.closeSafely(cStmt);
DbUtil.closeSafely(c);
}
return returnValue;
}
@POST
@Path("getbulkstatus/{jobsIds}")
@Produces("application/json")
@Override
public Integer[] getBulkStatus(Integer[] jobIds) {
List<Integer> ret = new ArrayList<Integer>();
for (Integer i : jobIds) {
ret.add(getStatus(i));
}
return (Integer[]) ret.toArray(new Integer[0]);
}
@POST
@Path("plugins/{category}")
@Produces("application/json")
@Override
public String[] getPlugins(String category) {
String userName = getCurrentUserName();
log.debug("Plugin list for " + userName + " [category = " + category
+ "]");
return Configuration.getPlugins(category).toArray(new String[0]);
}
@POST
@Path("file/{category}/{filename}")
@Produces("application/json")
@Override
public byte[] getFile(String category, String filename) {
String userName = getCurrentUserName();
log.debug("getFile for " + userName + " [category = " + category
+ ", filename = " + filename + "]");
return DbFileStore.getFile(userName, category, filename);
}
@POST
@Path("pluginoptions/{pluginclass}/{qualifyingoption}")
@Produces("application/json")
@Override
public String[] getPluginOptions(String pluginClass, String qualifyingOption) {
PluginInterface p = null;
try {
p = (PluginInterface) Class.forName(pluginClass).newInstance();
} catch (InstantiationException e) {
log.error(e);
return null;
} catch (IllegalAccessException e) {
log.error(e);
return null;
} catch (ClassNotFoundException e) {
log.error(e);
return null;
}
return p.getPluginConfigurationOptions();
}
@POST
@Path("filelist/{category}/{criteria}/{value}")
@Produces("application/json")
@Override
public FileListingItem[] getFileList(String category, String criteria,
String value) {
Connection c = getConnection();
String userName = getCurrentUserName();
log.debug("getFileList for " + userName + " [criteria = "
+ (criteria == null ? "null" : criteria) + ", value = "
+ (value == null ? "null" : value) + ", category = " + category
+ "]");
FileListingItem[] returnValue = null;
PreparedStatement cStmt = null;
String queryBase = "SELECT f.filename " + " , f.contentsize "
+ " , p.originalId " + " , p.insert_stamp "
+ " FROM tFileStore f "
+ " LEFT OUTER JOIN tPayload p ON p.id = f.payloadId "
+ " WHERE f.user = ? " + " AND f.category = ? " + " AND ";
try {
if (criteria.equalsIgnoreCase("month")) {
cStmt = c.prepareStatement(queryBase
+ " DATE_FORMAT(f.stamp, '%Y-%m') = ? " + ";");
} else if (criteria.equalsIgnoreCase("year")) {
cStmt = c.prepareStatement(queryBase
+ " DATE_FORMAT(f.stamp, '%Y') = ? " + ";");
} else if (criteria.equalsIgnoreCase("payload")) {
cStmt = c.prepareStatement(queryBase + " f.payloadId = ? "
+ ";");
} else {
DbUtil.closeSafely(cStmt);
DbUtil.closeSafely(c);
return null;
}
cStmt.setString(1, userName);
cStmt.setString(2, category);
cStmt.setString(3, value);
boolean hadResults = cStmt.execute();
List<FileListingItem> results = new ArrayList<FileListingItem>();
if (hadResults) {
ResultSet rs = cStmt.getResultSet();
while (rs.next()) {
FileListingItem i = new FileListingItem();
i.setFilename(rs.getString(1));
i.setFilesize(rs.getInt(2));
i.setOriginalId(rs.getString(3));
i.setInserted(rs.getDate(4));
results.add(i);
}
rs.close();
}
returnValue = results.toArray(new FileListingItem[0]);
} catch (NullPointerException npe) {
log.error("Caught NullPointerException", npe);
} catch (SQLException e) {
log.error("Caught SQLException", e);
} finally {
DbUtil.closeSafely(cStmt);
DbUtil.closeSafely(c);
}
return returnValue;
}
@POST
@Path("outputmonths/{targetyear}")
@Produces("application/json")
@Override
public String[] getOutputMonths(Integer targetYear) {
Connection c = getConnection();
String userName = getCurrentUserName();
log.debug("getOutputMonths for " + userName + " [targetYear = "
+ targetYear + "]");
String[] returnValue = null;
PreparedStatement cStmt = null;
try {
cStmt = c
.prepareStatement("SELECT DATE_FORMAT(stamp, '%Y-%m') AS m "
+ " FROM tFileStore "
+ " WHERE user = ? AND YEAR(stamp) = ? "
+ " GROUP BY m ;");
cStmt.setString(1, userName);
cStmt.setInt(2, targetYear);
boolean hadResults = cStmt.execute();
List<String> results = new ArrayList<String>();
if (hadResults) {
ResultSet rs = cStmt.getResultSet();
while (rs.next()) {
results.add(rs.getString("m"));
}
rs.close();
}
returnValue = results.toArray(new String[0]);
} catch (NullPointerException npe) {
log.error("Caught NullPointerException", npe);
} catch (SQLException e) {
log.error("Caught SQLException", e);
} finally {
DbUtil.closeSafely(cStmt);
DbUtil.closeSafely(c);
}
return returnValue;
}
@POST
@Path("outputyears")
@Produces("application/json")
@Override
public Integer[][] getOutputYears() {
Connection c = getConnection();
String userName = getCurrentUserName();
log.debug("getOutputYears for " + userName);
Integer[][] returnValue = null;
PreparedStatement cStmt = null;
try {
cStmt = c.prepareStatement("SELECT "
+ " DISTINCT(YEAR(stamp)) AS year "
+ ", COUNT(YEAR(stamp)) AS c " + " FROM tFileStore "
+ "WHERE user = ? " + " GROUP BY YEAR(stamp) " + ";");
cStmt.setString(1, userName);
boolean hadResults = cStmt.execute();
List<Integer[]> results = new ArrayList<Integer[]>();
if (hadResults) {
ResultSet rs = cStmt.getResultSet();
while (rs.next()) {
List<Integer> atomicResult = new ArrayList<Integer>();
atomicResult.add(rs.getInt(1));
atomicResult.add(rs.getInt(2));
results.add(atomicResult.toArray(new Integer[0]));
}
rs.close();
}
returnValue = results.toArray(new Integer[0][0]);
} catch (NullPointerException npe) {
log.error("Caught NullPointerException", npe);
} catch (SQLException e) {
log.error("Caught SQLException", e);
} finally {
DbUtil.closeSafely(cStmt);
DbUtil.closeSafely(c);
}
return returnValue;
}
@POST
@Path("eligibility")
@Produces("application/json")
@Override
public EligibilityResponse getEligibility(EligibilityRequest request) {
String userName = getCurrentUserName();
EligibilityInterface p = null;
try {
p = (EligibilityInterface) Class.forName(request.getPlugin())
.newInstance();
} catch (InstantiationException e) {
log.error(e);
return null;
} catch (IllegalAccessException e) {
log.error(e);
return null;
} catch (ClassNotFoundException e) {
log.error(e);
return null;
}
try {
return p.checkEligibility(userName, request.getRequest(), false,
(int) System.currentTimeMillis() % 1000000000);
} catch (Exception e) {
log.error(e);
return null;
}
}
@POST
@Path("eligibilitybatch")
@Produces("application/json")
@Override
public Integer batchEligibilityCheck(EligibilityRequest[] requests) {
String userName = getCurrentUserName();
for (EligibilityRequest param : requests) {
DbEligibilityJob.addEligibilityJob(userName, param.getPlugin(),
param.getRequest());
}
return 1;
}
@POST
@Path("parse")
@Produces("application/json")
@Override
public String parseData(String parserClass, String data) {
// String userName = getCurrentUserName();
ParserInterface p = null;
try {
p = (ParserInterface) Class.forName(parserClass).newInstance();
} catch (InstantiationException e) {
log.error(e);
return null;
} catch (IllegalAccessException e) {
log.error(e);
return null;
} catch (ClassNotFoundException e) {
log.error(e);
return null;
}
try {
return p.parseData(data);
} catch (Exception e) {
log.error(e);
return null;
}
}
@POST
@Path("addkey/{keyname}/{privatekey}/{publickey}")
@Produces("application/json")
@Override
public boolean addKeyToKeyring(String keyname, byte[] privatekey,
byte[] publickey) {
return KeyringStore.putKey(getCurrentUserName(), keyname, privatekey,
publickey);
}
@POST
@Path("adduser/{user}")
@Produces("application/json")
@Override
public boolean addRemittUser(UserDTO user) {
if (!context.isUserInRole("admin")) {
log.error("Attempt to add a user by a non-admin account");
return false;
}
return UserManagement.addUser(user.getUsername(), user.getPassword(),
user.getCallbackServiceUri(), user.getCallbackServiceWsdlUri(),
user.getCallbackUsername(), user.getCallbackPassword());
}
@POST
@Path("listusers")
@Produces("application/json")
@Override
public UserDTO[] listRemittUsers() {
if (!context.isUserInRole("admin")) {
log.error("Attempt to list users by a non-admin account");
return null;
}
return UserManagement.listUsers().toArray(new UserDTO[0]);
}
@POST
@Path("validate/{validatorClass}")
@Produces("application/json")
@Override
public ValidationResponse validatePayload(String validatorClass, byte[] data) {
String userName = getCurrentUserName();
ValidationInterface v = null;
try {
v = (ValidationInterface) Class.forName(validatorClass)
.newInstance();
} catch (InstantiationException e) {
log.error(e);
return null;
} catch (IllegalAccessException e) {
log.error(e);
return null;
} catch (ClassNotFoundException e) {
log.error(e);
return null;
}
try {
return v.validate(userName, data);
} catch (Exception e) {
log.error(e);
return null;
}
}
/**
* Internal method to get a database connection.
*
* @return
*/
protected Connection getConnection() {
// Connection c = (Connection) Configuration.getServletContext()
// .getServletContext().getAttribute("connection");
// return c;
return Configuration.getConnection();
}
}