/* Transit - Remote procedure calls made simple Copyright (c) 2012 Ulrich Hilger This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ package de.uhilger.transit.web; import javax.servlet.*; import javax.servlet.http.*; import java.io.*; import java.util.*; import java.util.logging.*; import de.uhilger.transit.*; /** * Das TransitServlet macht beliebige Klassen und Methoden * ueber HTTP zugaenglich. * *

Achtung:Das TransitServlet sollte nur in einem * per Authentifizierung und Autorisierung geschuetzten * Bereich einer Webanwendung bereitgestellt werden da andernfalls * durchweg alle Klassen und Methoden, die sich auf dem Server * finden, zugaenglich werden.

* *

Fuer die Bereitstellung von Funktionen ist eine Berechtigungspruefung * noetig, die am besten in Form eines Filters implementiert wird.

* * @author Copyright (c) Ulrich Hilger, http://uhilger.de * @author Published under the terms and conditions of * the GNU Affero General Public License * * @version 1, September 16, 2012 */ public class TransitServletRS extends HttpServlet { /** Proforma Konstante fuer Schnittstelle Serializable */ public static final long serialVersionUID = 42L; private static final Logger logger = Logger.getLogger(TransitServletRS.class.getName()); public static final String MIME_JSON = "application/json"; public static final String MIME_XML = "text/xml"; private static final String[] specialChars = {new String("\u00c4"), new String("\u00d6"), new String("\u00dc"), new String("\u00e4"), new String("\u00f6"), new String("\u00fc"), new String("\u00df")}; public static final String KLASSEN_TRENNER = ";"; //private String klassen; private String[] klassen; public void init(ServletConfig servletConfig) throws ServletException{ this.klassen = servletConfig.getInitParameter("klassen").split(KLASSEN_TRENNER); super.init(servletConfig); } /** * Eine Anfrage via HTTP GET verarbeiten * *

Folgende Parameter werden erwartet:

*

c: Klasse, z.B. de.uhilger.test.EineKlasse
* m: Methode
* p: Erster Parameter der Methode in der Reihenfolge der Deklaration
* p: Zweiter Parameter der Methode in der Reihenfolge der Deklaration
* .
* .
* p: Letzter Parameter der Methode in der Reihenfolge der Deklaration

* *

Das Ergebnis der Verarbeitung wird in Form einer Zeichenkette im * Format der JavaScript Object Notation (JSON) in die Antwort der * HTTP-Anfrage geschrieben.

* * @param req die Anfrage, die verarbeitet werden soll * @param resp das Objekt, mit dem das Ergebnis der Verarbeitung mitgeteilt wird */ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { anfrageAusfuehren(req, resp); } /** * Eine Anfrage via HTTP POST verarbeiten * *

Folgende Parameter werden erwartet:

*

c: Klasse, z.B. de.uhilger.test.EineKlasse
* m: Methode
* p: Erster Parameter der Methode in der Reihenfolge der Deklaration
* p: Zweiter Parameter der Methode in der Reihenfolge der Deklaration
* .
* .
* p: Letzter Parameter der Methode in der Reihenfolge der Deklaration

* *

Das Ergebnis der Verarbeitung wird in Form einer Zeichenkette im * Format der JavaScript Object Notation (JSON) in die Antwort der * HTTP-Anfrage geschrieben.

* * @param req die Anfrage, die verarbeitet werden soll * @param resp das Objekt, mit dem das Ergebnis der Verarbeitung mitgeteilt wird */ @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { anfrageAusfuehren(req, resp); } /** * Diese Methode verarbeitet die Anfragen, die via HTTP GET und HTTP POST * an dieses Servlet gestellt werden. * * @param req die Anfrage, die verarbeitet werden soll * @param resp das Objekt, mit dem das Ergebnis der Verarbeitung mitgeteilt wird */ @SuppressWarnings("unchecked") public void anfrageAusfuehren(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { JavaServer server = null; try { /* String klassenName = null; String methodenName = null; List parameterListe = new ArrayList(); String formatName = null; Enumeration en = req.getParameterNames(); while(en.hasMoreElements()) { String pName = en.nextElement().toString(); String[] pWerte = req.getParameterValues(pName); if(pName.equals(CLASS_NAME)) { klassenName = pWerte[0]; } else if(pName.equals(METHOD_NAME)) { methodenName = pWerte[0]; } else if(pName.equals(FORMAT_NAME)) { formatName = pWerte[0]; } else { for(int i = 0; i < pWerte.length; i++) { parameterListe.add(pWerte[i]); } } } */ /* http://server:port/contextpfad/servletpfad/de.irgend.eine.Klasse/methode/format/param1/param2/../paramN wird zu elemente[0] = / elemente[1] = de.irgend.eine.Klasse elemente[2] = methode elemente[3] = format elemente[4]-[n] = param1 */ String pathInfo = req.getPathInfo(); logger.info("pathInfo: " + pathInfo); String[] elemente = pathInfo.split("/"); String klassenName = elemente[1]; StringBuffer buf = new StringBuffer(); if(istErlaubt(klassenName)) { //if(this.klassen.contains(klassenName)) { //if(this.klassen.matches(klassenName)) { String methodenName = elemente[2]; String formatName = elemente[3]; List parameterListe = new ArrayList(); for(int i = 4; i < elemente.length; i++) { parameterListe.add(elemente[i]); } Object[] parameter = parameterListe.toArray(new Object[0]); server = new JavaServer(); server.wandlerHinzufuegen(new JsonWandler()); server.wandlerHinzufuegen(new JsonFlatWandler()); server.wandlerHinzufuegen(new XmlWandler()); Class cls = server.klasseFinden(klassenName); if(cls != null) { Object o = cls.newInstance(); if(o != null) { if(o instanceof NutzerKontext) { ((NutzerKontext) o).setNutzerId(Waechter.getUserName(req)); } if(o instanceof WebKontext) { ((WebKontext) o).setServletContext(getServletContext()); } if(o instanceof RequestKontext) { ((RequestKontext) o).setRequest(req); } if(o instanceof VerbindungsKontext) { ((VerbindungsKontext) o).setVerbindung(req.getSession()); } } Object resultat = null; if(formatName != null) { resultat = server.methodeAusfuehren(o, methodenName, formatName, parameter); } else { resultat = server.methodeAusfuehren(o, methodenName, JsonWandler.FORMAT_JSON, parameter); } if(resultat != null) { if(formatName == null) { resp.setContentType(MIME_JSON); } else { if(formatName.equalsIgnoreCase(JsonWandler.FORMAT_JSON) || formatName.equalsIgnoreCase(JsonFlatWandler.FORMAT_FLATJSON)) { resp.setContentType(MIME_JSON); } else if(formatName.equalsIgnoreCase(XmlWandler.FORMAT_XML)) { resp.setContentType(MIME_XML); } } buf.append(escapeHtml(resultat.toString())); } else { buf.append("

Resultat von server.methodeAusfuehren ist null.

"); } } else { buf.append("

Ergebnis von server.klasseFinden ist null (Klasse nicht gefunden?).

"); } } else { buf.append("

Klasse " + klassenName + " nicht erlaubt.

"); } Writer w = resp.getWriter(); w.write(buf.toString()); w.flush(); w.close(); } catch(Exception ex) { throw new ServletException(ex); } finally { if(server != null) { server.aufloesen(); server = null; } } } /** * Hier wird geprueft, ob der Name einer Klasse der Liste der erlaubten * Klassen bzw. Packages entspricht. Im Deployment Descriptor wird ein * Ausdruck wie z.B. * * de.uhilger.test.web;de.uhilger.test.api;de.uhilger.test.db.KlassenName * * erwartet. Diese Methode spaltet diesen Ausdruck an den ';' auf und prueft * fuer jeden Ausdruck, ob der KlassenName mit diesem Ausdruck beginnt. * Beginnt der Klassenname mit einem der Ausdruecke, wird der Zugriff erlaubt * * @param klassenName Name der Klasse, fuer die der Zugriff geprueft * werden soll * @return true, wenn die Klasse laut dem Eintrag im Deployment Desccriptor * aufgerufen werden darf, false wenn nicht */ private boolean istErlaubt(String klassenName) { boolean erlaubt = false; for(int i = 0; i < klassen.length && !erlaubt; i++) { erlaubt = klassenName.startsWith(klassen[i]); } return erlaubt; } public String escapeHtml(String text) { text = text.replace(specialChars[0], "Ä"); text = text.replace(specialChars[1], "Ö"); text = text.replace(specialChars[2], "Ü"); text = text.replace(specialChars[3], "ä"); text = text.replace(specialChars[4], "ö"); text = text.replace(specialChars[5], "ü"); text = text.replace(specialChars[6], "ß"); return text; } }