hs.hsadmin/hsarback/src/de/hsadmin/cliClientConnector/CLIClientConnectorServlet.java

492 lines
15 KiB
Java
Raw Normal View History

2010-10-01 21:52:51 +02:00
package de.hsadmin.cliClientConnector;
import java.io.BufferedReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
2010-10-05 21:42:07 +02:00
import org.apache.commons.codec.binary.Base64;
2010-10-04 19:44:49 +02:00
import de.hsadmin.core.model.AbstractEntity;
2010-10-01 21:52:51 +02:00
import de.hsadmin.core.model.GenericModuleImpl;
import de.hsadmin.core.model.ModuleInterface;
import de.hsadmin.core.model.TicketAuthentication;
import de.hsadmin.core.model.Transaction;
import de.hsadmin.core.model.onetier.TicketValidator;
/**
* actually this is the core of the CLI-Client. The other CLI-Client is just a
* rather simple HTTP Client that calls this Servlet.
*
* @author Christof Donat
*
*/
public class CLIClientConnectorServlet extends HttpServlet {
2010-10-06 15:06:01 +02:00
2010-10-01 21:52:51 +02:00
private static final long serialVersionUID = 7150004719303750077L;
2011-05-17 17:04:06 +02:00
public static final String version = "CLI Servlet 2.0.0 (2011/May/21 09:00 MEST)";
2010-10-06 15:06:01 +02:00
2010-10-01 21:52:51 +02:00
private Map<String, Class<?>> componentmap;
private Map<String, String> componentDescriptions;
private ArgumentParser parser;
/**
* Servlet initialization
*/
public void init(ServletConfig cfg) {
// init ticket validator
String validateURL = cfg.getInitParameter("proxyValidateUrl");
String serviceURL = cfg.getInitParameter("proxyServiceUrl");
TicketValidator.getInstance().initialize(validateURL, serviceURL);
// find components
String cstring = cfg.getInitParameter("Components");
String[] components = cstring.split(",");
componentmap = new HashMap<String, Class<?>>();
componentDescriptions = new HashMap<String, String>();
for (int i = 0; i < components.length; i++) {
// get everything for this component and create an entry.
try {
// component class
String classname = cfg.getInitParameter("ComponentClass_"
+ components[i]);
if (classname == null)
throw new NullPointerException(
"no class name found for Component "
+ components[i]);
Class<?> cls = Class.forName(classname);
componentmap.put(components[i], cls);
// description
String descr = cfg.getInitParameter("ComponentDescription_"
+ components[i]);
if (descr != null)
componentDescriptions.put(components[i], descr);
else
componentDescriptions.put(components[i], "");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
// TODO: get username, password from http session
parser = new ArgumentParser(this);
}
/**
* set values to a given entity Object
*
* @param o
* the entity Object
* @param set
* Hashtable with names and values that sould be changed in o
*/
private void setValues(Object o, Map<String, String> set, ModuleInterface module) {
Iterator<String> keys = set.keySet().iterator();
while (keys.hasNext()) {
String key = keys.next();
String[] ns = key.split("[.]", 2);
Object realO = o;
for (int i = 0; i < ns.length - 1; i++) {
Method[] m = realO.getClass().getMethods();
boolean oFound = false;
for (int j = 0; j < m.length; j++) {
if (m[j].getName().toLowerCase().equals(
"get" + ns[i].toLowerCase())) {
try {
realO = m[j].invoke(realO, (Object[]) null);
oFound = (realO != null);
break;
} catch (Exception e) {
new TechnicalException(e);
}
}
}
if (!oFound)
break;
}
Method[] m = realO.getClass().getMethods();
String value = set.get(key);
for (int j = 0; j < m.length; j++) {
if (!m[j].getName().toLowerCase().equals(
"set" + ns[ns.length - 1].toLowerCase()))
continue;
String type = m[j].getParameterTypes()[0].getCanonicalName();
try {
if (type.equals("java.lang.String"))
2011-05-19 14:31:34 +02:00
m[j].invoke(realO, value);
2011-05-19 13:49:59 +02:00
else if (type.equals("java.lang.Integer") || type.equals("int"))
2011-05-19 14:31:34 +02:00
m[j].invoke(realO, Integer.parseInt(value));
2011-05-19 13:49:59 +02:00
else if (type.equals("java.lang.Long") || type.equals("long"))
2011-05-19 14:31:34 +02:00
m[j].invoke(realO, Long.parseLong(value));
2011-05-19 13:49:59 +02:00
else if (type.equals("java.lang.Boolean") || type.equals("boolean"))
2011-05-19 14:31:34 +02:00
m[j].invoke(realO, Boolean.valueOf(value));
2010-10-01 21:52:51 +02:00
else if (type.equals("java.util.Date")) {
DateFormat df = DateFormat.getInstance();
2011-05-19 14:31:34 +02:00
m[j].invoke(realO, df.parse(value));
2010-10-01 21:52:51 +02:00
} else {
Method m2 = module.getClass().getMethod(
"findByString", Class.class, String.class);
Object entity =
m2.invoke(module, m[j].getParameterTypes()[0], value);
if (entity != null)
2011-05-19 14:31:34 +02:00
m[j].invoke(realO, entity);
2010-10-01 21:52:51 +02:00
else
throw new BusinessException(
"not object found for '" + value + "'");
}
} catch (Exception e) {
throw new TechnicalException(e); // TODO: this needs to be
// more specific for
// some cases
}
}
}
}
private String hasGetter(Class<?> eType, String name) {
String rval = null;
String[] ns = name.split("[.]", 2);
String n1 = ns[0];
Method meth = null;
for (Method m : eType.getMethods()) {
String n = m.getName();
if (n.startsWith("get")) {
String fn = m.getName().substring(3).toLowerCase();
if (fn != "class" && fn.equals(n1)) {
meth = m;
rval = fn;
break;
}
}
}
if (meth != null) {
Class<?> returnType = meth.getReturnType();
if (rval != null && ns.length > 1 && meth != null)
return hasGetter(returnType, ns[1]);
if (returnType.getCanonicalName().startsWith("de.hsadmin.mods")) {
try {
if (returnType.getMethod("getName") != null) {
return rval + ".name";
}
} catch (Exception e) {
// no method found
}
}
}
return rval;
}
/**
* builds a query from a where clause and some objectIDs.
*
* @param eType
* The class of the Entity Object for which the string should be
* built. We need this to call the static method
* "createQueryFromStringKey"
* @param where
* Hashtable with where parameters. Only objects which match all
* where parameters are found
* @param oids
* Only objects with one of these object IDs are found
*
* @return queryString a query string that can be used to select the
* required Objects
*/
private String buildQuery(Class<?> eType, Map<String, String> where,
ArrayList<String> oids) {
String rval = "";
boolean first = true;
Iterator<String> wkeys = where.keySet().iterator();
while (wkeys.hasNext()) {
String k = (String) wkeys.next();
String kname = hasGetter(eType, k);
if (kname != null) {
rval += (first ? "" : " and ")
2010-10-04 19:44:49 +02:00
+ "(obj." + AbstractEntity.escapeString(kname) + " = '" + AbstractEntity.escapeString(where.get(k)) + "')";
2010-10-01 21:52:51 +02:00
first = false;
}
}
String rv = "";
if (oids != null)
try {
Method m;
m = eType.getMethod("createQueryFromStringKey", String.class);
first = true;
for (String s : oids) {
rv += (first ? "" : " or ") + "("
+ (String) m.invoke(eType, s) + ")";
first = false;
}
if (rv != "" && rval != "")
rval = rval + " and (" + rv + ")";
else if (rv != "")
rval = rv;
} catch (Exception e) {
throw new TechnicalException(e);
}
return (rval == "") ? null : rval;
}
public class FunctionNotKnownException extends Exception {
private static final long serialVersionUID = -6330015688609717838L;
}
public class UnknownModuleException extends Exception {
private static final long serialVersionUID = 696641072107896601L;
}
private Object callAdd(Class<?> cls, Map<String, String> set, ModuleInterface module) {
Transaction transaction = module.getTransaction();
transaction.beginTransaction();
try {
2010-10-04 19:44:49 +02:00
Method m = module.getClass().getMethod("add", AbstractEntity.class);
2010-10-01 21:52:51 +02:00
Object o = cls.newInstance();
setValues(o, set, module);
m.invoke(module, o);
transaction.commitTransaction();
return null;
} catch (Exception e) {
transaction.rollbackTransaction();
// TODO: this needs to be more specific, but how?
throw new TechnicalException(e);
}
}
@SuppressWarnings("unchecked")
private List<Object> callSearch(Class<?> cls, Map<String, String> where,
ArrayList<String> oids, ModuleInterface module) {
try {
Method m = module.getClass().getMethod("search", Class.class,
String.class, String.class);
return (List<Object>) m.invoke(module, cls,
buildQuery(cls, where, oids), null);
} catch (Exception e) {
throw new TechnicalException(e); // TODO: this needs to be more
// specific, but how?
}
}
// / checks wheather all 'oids' are in 'list'
2010-10-04 19:44:49 +02:00
private void checkOids(List<AbstractEntity> list, List<String> oids) {
2010-10-01 21:52:51 +02:00
List<String> oidsNotFound = new ArrayList<String>();
for (String id : oids) {
boolean found = false;
2010-10-04 19:44:49 +02:00
for (AbstractEntity e : list) {
2010-10-01 21:52:51 +02:00
String foundKey = e.createStringKey();
if (foundKey.equals(id)) {
found = true;
break;
}
}
if (!found)
oidsNotFound.add(id);
}
if (oidsNotFound.size() > 0) {
throw new OidsNotFoundException(oids);
}
}
@SuppressWarnings("unchecked")
private List<Object> callUpdate(Class<?> cls, Map<String, String> where,
ArrayList<String> oids, Map<String, String> set,
ModuleInterface module) {
// better safe than sorry - alsd hso same behavior as UNIX rm
if (where.size() == 0 && oids.size() == 0)
throw new BusinessException(
"better safe than sorry - 'update' needs a -w/--where-query or object id");
Transaction tx = module.getTransaction();
tx.beginTransaction();
try {
Method m = module.getClass().getMethod("search", Class.class,
String.class, String.class);
2010-10-04 19:44:49 +02:00
List<AbstractEntity> list = (List<AbstractEntity>) m.invoke(module, cls,
2010-10-01 21:52:51 +02:00
buildQuery(cls, where, oids), null);
checkOids(list, oids);
2010-10-04 19:44:49 +02:00
Method m2 = module.getClass().getMethod("update", AbstractEntity.class);
2010-10-01 21:52:51 +02:00
for (int i = 0; i < list.size(); i++) {
2010-10-04 19:44:49 +02:00
AbstractEntity entity = list.get(i);
2010-10-01 21:52:51 +02:00
tx.detach(entity);
setValues(entity, set, module);
m2.invoke(module, entity);
}
tx.commitTransaction();
} catch (Exception e) {
tx.rollbackTransaction();
// TODO: this needs to be more specific, but how?
throw new TechnicalException(e);
}
return null;
}
@SuppressWarnings("unchecked")
private void callDelete(Class<?> cls, Map<String, String> where,
ArrayList<String> oids, ModuleInterface module) {
// better safe than sorry - also same behavior as UNIX rm
if (where.size() == 0 && oids.size() == 0)
throw new BusinessException(
"better safe than sorry - 'update' needs a -w/--where-query or object id");
Transaction tx = module.getTransaction();
tx.beginTransaction();
try {
Method m =
module.getClass().getMethod("search", Class.class, String.class, String.class);
2010-10-04 19:44:49 +02:00
List<AbstractEntity> list =
(List<AbstractEntity>) m.invoke(module, cls, buildQuery(cls, where, oids), null);
2010-10-01 21:52:51 +02:00
checkOids(list, oids);
2010-10-04 19:44:49 +02:00
Method m2 = module.getClass().getMethod("delete", AbstractEntity.class);
2010-10-01 21:52:51 +02:00
for (int i = 0; i < list.size(); i++) {
Object o = list.get(i);
m2.invoke(module, o);
}
tx.commitTransaction();
} catch (Exception e) {
tx.rollbackTransaction();
// TODO: this needs to be more specific, but how?
throw new TechnicalException(e);
}
}
/**
* Call one of the EntitySessions methods
*
* @param moduleName
* Defines the Entity class that will be given to the
* EntitySession
* @param function
* Defines the method that will be called. The function can be
* "add", "search", "update" and "delete".
* @param where
* Reduces the set of Entity objects which the function operates
* on to those matched by the where parameters. Only for
* "search", "update" and "delete". Will be ignored otherwise.
* @param set
* Set these values on all Objects - only for "add" and "update".
* Will be ignored otherwise.
* @param oids
* Works on Objects with these IDs. Only for "search", "update"
* and "delete". Will be ignored for "add".
* @return foundObjects
* @throws FunctionNotKnownException
*/
public List<Object> callModule(String moduleName, String function,
Map<String, String> where, Map<String, String> set,
ArrayList<String> oids, ModuleInterface module)
throws FunctionNotKnownException, UnknownModuleException {
List<Object> rval = new ArrayList<Object>();
// handle calls to the virtual module "modules"
if (moduleName.equals("modules")) {
// only search, no manipulation is possible
if (function.equals("search")) {
Iterator<?> m = componentDescriptions.keySet().iterator();
while (m.hasNext()) {
String mn = (String) m.next();
rval.add(new ModuleModel(mn, componentDescriptions.get(mn)));
}
} else if (function.equals("version")) {
rval.add(new VersionModel(version));
} else {
throw new FunctionNotKnownException();
}
return rval;
}
// find the component for the named module
Class<?> cls = componentmap.get(moduleName);
if (cls == null)
throw (new UnknownModuleException());
// call the appropriate methods
if (function.equals("add"))
rval.add(callAdd(cls, set, module));
else if (function.equals("search")) {
List<Object> r = callSearch(cls, where, oids, module);
if (r != null)
return r;
} else if (function.equals("update")) {
List<Object> r = callUpdate(cls, where, oids, set, module);
if (r != null)
return callUpdate(cls, where, oids, set, module);
} else if (function.equals("delete")) {
callDelete(cls, where, oids, module);
} else
throw (new FunctionNotKnownException());
return rval;
}
/**
* handle put method
*/
public void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException {
try {
2010-10-06 15:06:01 +02:00
ModuleInterface module = null;
Transaction tx = null;
2010-10-01 21:52:51 +02:00
// check login
String auth = req.getHeader("authorization");
if (auth == null) {
// no login information at all - get it
resp.setHeader("WWW-Authenticate",
"Basic realm=\"CLIClientConnector\"");
resp.getOutputStream().println("login Failed.");
resp.sendError(HttpServletResponse.SC_UNAUTHORIZED);
return;
} else {
// parse login information
String[] a = auth.split(" ", 2);
String ticket = a[1];
2010-10-05 21:42:07 +02:00
byte[] decoded = Base64.decodeBase64(ticket.trim().getBytes());
String s = new String(decoded);
a = s.split(":", 2);
2010-10-01 21:52:51 +02:00
// try to log in
String login = a[0];
ticket = a[1];
try {
if (TicketAuthentication.getInstance().login(login, ticket)) {
// login successful
2010-10-06 15:06:01 +02:00
tx = new Transaction(login);
module = new GenericModuleImpl(tx);
2010-10-01 21:52:51 +02:00
// read arguments
BufferedReader read = req.getReader();
String tmpbuf;
ArrayList<String> arguments = new ArrayList<String>();
while ((tmpbuf = read.readLine()) != null) {
arguments.add(tmpbuf);
}
// actually handle the request and write result to output
String output = parser.parse(arguments, module);
resp.getWriter().write(output);
} else {
resp.addHeader("X-hsadmin-error", "authentication failed");
}
} catch (Exception e) {
resp.addHeader("X-hsadmin-error", "exception: " + e.getMessage());
} finally {
2010-10-06 15:06:01 +02:00
if (tx != null) {
tx.close();
2010-10-01 21:52:51 +02:00
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}