package org.jboss.fresh.remoteshell.ejb.impl;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.List;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
import javax.transaction.UserTransaction;
import org.jboss.fresh.vfs.UserCtx;
import org.jboss.fresh.registry.RegistryContext;
import org.jboss.fresh.shell.ProcessInfo;
import org.jboss.fresh.shell.Shell;
import org.jboss.fresh.shell.ShellException;
import org.jboss.fresh.shell.ShellIOException;
import org.jboss.fresh.shell.ShellInitializationException;
import org.jboss.fresh.shell.SystemShell;
/**
* Shell session can timeout. Using a timed-out session is like having a ghost around. Even the system is not aware of it, so
* it can easily happen that resources are allocated and not released as long as remote object impl has a reference to shell impl
* The logical thing to do would be to throw an exception to notify a client of a timeout. Client can then reconnect and try to repeat the action.
* Automatic reinitalisation is very problematic since many sessions have their environment initialized at the beginning. And only client knows exactly
* what's involved.
*
* @author mare
* @version $Revision: 1.8 $
* @modified $Author: cerar $
*/
public class RemoteShellImpl implements SessionBean {
private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(RemoteShellImpl.class);
private static final org.apache.log4j.Logger log_cmd = org.apache.log4j.Logger.getLogger(RemoteShellImpl.class.getName() + ".commands");
private static final org.apache.log4j.Logger log_treplacer = org.apache.log4j.Logger.getLogger(RemoteShellImpl.class.getName() + ".throwable");
private static final String SYSTEM_SHELL_JNDI = "java:/SystemShell";
String sessid;
boolean interactive;
transient Shell shell;
UserCtx uctx;
public static final String KEEP_EXCEPTIONS_ENV_NAME = "KEEP_EXCEPTIONS";
private SessionContext sctx;
public void ejbCreate() throws ShellException {
ejbCreate(null, false, null);
}
public void ejbCreate(String sessid) throws ShellException {
ejbCreate(sessid, false, null);
}
public void ejbCreate(boolean interactive) throws ShellException {
ejbCreate(null, interactive, null);
}
public void ejbCreate(boolean interactive, Properties roProps) throws ShellException {
ejbCreate(null, interactive, roProps);
}
public void ejbCreate(String sessid, boolean interactive) throws ShellException {
ejbCreate(sessid, interactive, null);
}
public void ejbCreate(String sessid, boolean interactive, Properties roProps) throws ShellException {
this.interactive = interactive;
this.sessid = sessid;
try {
/*if (sctx != null) {
TxSupport.assignUTForThread(sctx.getUserTransaction());
}*/
reinitShell();
if (roProps != null) {
Iterator it = roProps.entrySet().iterator();
while (it.hasNext()) {
Map.Entry ent = (Map.Entry) it.next();
shell.setROEnvProperty((String) ent.getKey(), (String) ent.getValue());
}
}
} catch (Exception ex) {
throw new ShellInitializationException(ex);
}/* finally {
TxSupport.clearUTForThread();
}*/
}
// public void ejbPostCreate(String sessid) {
// }
public void ejbRemove() {
try {
/*if (sctx != null) {
TxSupport.assignUTForThread(sctx.getUserTransaction());
}*/
if (shell == null) {
ejbActivate();
}
if (shell != null) {
shell.close(); // means all processes
}
shell = null;
sessid = null;
} catch (Exception ex) {
if (ex instanceof org.jboss.fresh.shell.SessionTimeoutException) {
log.info("Shell session has timedout.");
} else {
log.error("Error while removing EJB!", ex);
}
}/* finally {
TxSupport.clearUTForThread();
}*/
}
public void setSessionContext(SessionContext ctx) {
sctx = ctx;
//log.debug("[RemoteShell] Session ctx set.");
/*
try {
// this method never returns null
Principal principal=ctx.getCallerPrincipal();
InitialContext naming=new InitialContext();
UserDirectory udir=(UserDirectory)naming.lookup("UserDirectory");
uctx=udir.getUserCtx(principal);
} catch(Exception ex) {
ex.printStackTrace();
}
*/
}
public void unsetSessionContext() {
uctx = null;
}
public void ejbActivate() {
//log.debug("ejbActivate called");
try {
if (sctx != null) {
//TxSupport.assignUTForThread(sctx.getUserTransaction());
}
reinitShell();
} catch (Exception ex) {
if (ex instanceof org.jboss.fresh.shell.SessionTimeoutException) {
log.info("Shell session has timedout.");
} else {
log.error("Error while activating EJB!", ex);
}
} finally {
//TxSupport.clearUTForThread();
}
}
public void ejbPassivate() {
}
private void reinitShell() throws NamingException, ShellException {
if (shell == null) {
InitialContext ctx = null;
SystemShell sysshell;
String sslookup = SYSTEM_SHELL_JNDI;
try {
ctx = new InitialContext();
Context env = (Context) ctx.lookup("java:comp/env");
sslookup = (String) env.lookup("SystemShellJNDI");
} catch (NameNotFoundException ex) {
log.info("SystemShellJNDI ENV not set. Using defaults.");
} finally {
if (ctx != null) {
try {
ctx.close();
} catch (Exception e) {
log.error("Could not close " + ctx + '!', e);
}
}
}
try {
sysshell = (SystemShell) new RegistryContext().lookup(sslookup);
// } catch(NameNotFoundException ex) {
// ex.printStackTrace();
// throw new RuntimeException(ex.getMessage());
} catch (NamingException ex) {
log.error("Error while reiniting shell!", ex);
throw new RuntimeException(ex);
}
if (sessid == null) {
shell = sysshell.startSession(uctx, interactive);
sessid = shell.getSessionID();
} else {
shell = sysshell.continueSession(sessid);
}
if (shell == null) {
throw new org.jboss.fresh.shell.SessionTimeoutException("Shell session has timed out.");
}
}
}
public String startNewSession() throws ShellException {
shell = null;
ejbActivate();
return sessid;
}
public ProcessInfo execute(String cmdline) throws ShellException {
return execute(cmdline, null);
}
public ProcessInfo execute(String cmdline, List input) throws ShellException {
try {
//TxSupport.assignUTForThread(sctx.getUserTransaction());
reinitShell();
log_cmd.debug(cmdline);
return shell.execute(cmdline, input, true);
} catch (RuntimeException e) {
log.error("Error while executing " + cmdline + "!", e);
throw (RuntimeException) processThrowable(e);
} catch (org.jboss.fresh.shell.SessionTimeoutException e) {
log.info("Session has timed out");
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw (org.jboss.fresh.shell.SessionTimeoutException) th;
}
} catch (ShellException e) {
log.error("Error while executing " + cmdline + "!", e);
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw (ShellException) th;
}
} catch (Throwable e) {
log.error("Error while executing " + cmdline + "!", e);
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw new ShellException(th);
}
} /*finally {
TxSupport.clearUTForThread();
} */
}
/*
public String executeAsString(String cmdline) throws ShellException {
try {
reinitShell();
log_cmd.debug(cmdline);
return shell.executeAsString(cmdline, true);
} catch (RuntimeException e) {
log.error("Error while executing " + cmdline + "!", e);
throw (RuntimeException) processThrowable(e);
} catch (org.jboss.fresh.shell.SessionTimeoutException e) {
log.info("Session has timed out");
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw (org.jboss.fresh.shell.SessionTimeoutException) th;
}
} catch (ShellException e) {
log.error("Error while executing " + cmdline + "!", e);
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw (ShellException) th;
}
} catch (Throwable e) {
log.error("Error while executing " + cmdline + "!", e);
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw new ShellException(th);
}
}
}
*/
public Object executeAsObject(String cmdline) throws ShellException {
return executeAsObject(cmdline, null);
}
public Object executeAsObject(String cmdline, List input) throws ShellException {
try {
UserTransaction tx = sctx.getUserTransaction();
//TxSupport.assignUTForThread(tx);
reinitShell();
log_cmd.debug(cmdline);
return shell.executeAsObject(cmdline, input, true);
} catch (RuntimeException e) {
log.error("Error while executing " + cmdline + "!", e);
throw (RuntimeException) processThrowable(e);
} catch (org.jboss.fresh.shell.SessionTimeoutException e) {
log.info("Session has timed out");
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw (org.jboss.fresh.shell.SessionTimeoutException) th;
}
} catch (ShellException e) {
log.error("Error while executing " + cmdline + "!", e);
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw (ShellException) th;
}
} catch (Throwable e) {
log.error("Error while executing " + cmdline + "!", e);
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw new ShellException(th);
}
}/* finally {
TxSupport.clearUTForThread();
}*/
}
public String getEnvProperty(String propname) throws ShellException {
try {
//TxSupport.assignUTForThread(sctx.getUserTransaction());
reinitShell();
return shell.getEnvProperty(propname);
} catch (RuntimeException e) {
log.error("Error while executing getEnvProperty(" + propname + ")", e);
throw (RuntimeException) processThrowable(e);
} catch (org.jboss.fresh.shell.SessionTimeoutException e) {
log.error("Error while executing getEnvProperties(" + propname + ")", e);
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw (org.jboss.fresh.shell.SessionTimeoutException) th;
}
} catch (ShellException e) {
log.error("Error while executing getEnvProperties(" + propname + ")", e);
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw (ShellException) th;
}
} catch (Throwable e) {
log.error("Error while executing getEnvProperties(" + propname + ")", e);
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw new ShellException(th);
}
} /*finally {
TxSupport.clearUTForThread();
} */
}
public Properties getEnvProperties() throws ShellException {
try {
//TxSupport.assignUTForThread(sctx.getUserTransaction());
reinitShell();
return shell.getEnvProperties();
} catch (RuntimeException e) {
log.error("Error while executing getEnvProperties()", e);
throw (RuntimeException) processThrowable(e);
} catch (org.jboss.fresh.shell.SessionTimeoutException e) {
log.info("Session has timed out");
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw (org.jboss.fresh.shell.SessionTimeoutException) th;
}
} catch (ShellException e) {
log.error("Error while executing getEnvProperties()", e);
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw (ShellException) th;
}
} catch (Throwable e) {
log.error("Error while executing getEnvProperties()", e);
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw new ShellException(th);
}
} /*finally {
TxSupport.clearUTForThread();
}*/
}
public void setEnvProperty(String name, String value) throws ShellException {
try {
//TxSupport.assignUTForThread(sctx.getUserTransaction());
reinitShell();
shell.setEnvProperty(name, value);
} catch (RuntimeException e) {
log.error("Error while executing setEnvProperty(" + name + ", " + value + ")", e);
throw (RuntimeException) processThrowable(e);
} catch (org.jboss.fresh.shell.SessionTimeoutException e) {
log.info("Session has timed out");
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw (org.jboss.fresh.shell.SessionTimeoutException) th;
}
} catch (ShellException e) {
log.error("Error while executing setEnvProperty(" + name + ", " + value + ")", e);
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw (ShellException) th;
}
} catch (Throwable e) {
log.error("Error while executing setEnvProperty(" + name + ", " + value + ")", e);
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw new ShellException(th);
}
}/* finally {
TxSupport.clearUTForThread();
}*/
}
public Object read(String id) throws IOException {
try {
//TxSupport.assignUTForThread(sctx.getUserTransaction());
reinitShell();
Object obj = shell.read(id);
//log.debug("### obj: " + obj);
return replaceThrowable(obj);
} catch (RuntimeException e) {
log.error("Error while executing read(" + id + ")", e);
throw (RuntimeException) processThrowable(e);
} catch (IOException e) {
log.error("Error while executing read(" + id + ")", e);
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw (IOException) th;
}
} catch (Throwable e) {
log.error("Error while executing read(" + id + ")", e);
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw new ShellIOException(th);
}
}/* finally {
TxSupport.assignUTForThread(sctx.getUserTransaction());
}*/
}
public LinkedList readBuffer(String id, int maxsize) throws IOException {
try {
//TxSupport.assignUTForThread(sctx.getUserTransaction());
reinitShell();
LinkedList l = shell.readBuffer(id, maxsize);
//log.debug("### list: (" + l.size() +") " + l);
l = replaceThrowables(l);
return l;
} catch (RuntimeException e) {
log.error("Error while executing readBuffer(" + id + ", " + maxsize + ")", e);
throw (RuntimeException) processThrowable(e);
} catch (IOException e) {
log.error("Error while executing read(" + id + ")", e);
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw (IOException) th;
}
} catch (Throwable e) {
log.error("Error while executing read(" + id + ")", e);
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw new ShellIOException(th);
}
} /*finally {
TxSupport.clearUTForThread();
} */
}
public void write(String id, Object obj) throws IOException {
try {
//TxSupport.assignUTForThread(sctx.getUserTransaction());
reinitShell();
shell.write(id, obj);
} catch (RuntimeException e) {
log.error("Error while executing write(" + id + ", " + obj + ")", e);
throw (RuntimeException) processThrowable(e);
} catch (IOException e) {
log.error("Error while executing read(" + id + ")", e);
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw (IOException) th;
}
} catch (Throwable e) {
log.error("Error while executing read(" + id + ")", e);
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw new ShellIOException(th);
}
}/* finally {
TxSupport.clearUTForThread();
}*/
}
public void writeBuffer(String id, LinkedList obj) throws IOException {
try {
//TxSupport.assignUTForThread(sctx.getUserTransaction());
reinitShell();
shell.writeBuffer(id, obj);
} catch (RuntimeException e) {
log.error("Error while executing writeBuffer(" + id + ", " + obj + ")", e);
throw (RuntimeException) processThrowable(e);
} catch (IOException e) {
log.error("Error while executing read(" + id + ")", e);
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw (IOException) th;
}
} catch (Throwable e) {
log.error("Error while executing read(" + id + ")", e);
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw new ShellIOException(th);
}
} /*finally {
TxSupport.clearUTForThread();
} */
}
// means only the specified process
public void close(String id, int streamid) throws IOException {
try {
//TxSupport.assignUTForThread(sctx.getUserTransaction());
reinitShell();
shell.close(id, streamid);
} catch (RuntimeException e) {
log.error("Error while executing stream close (processId: " + id + ", streamId: " + streamid + ")", e);
throw (RuntimeException) processThrowable(e);
} catch (IOException e) {
log.error("Error while executing stream close (processId: " + id + ", streamId: " + streamid + ")", e);
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw (IOException) th;
}
} catch (Throwable e) {
log.error("Error while executing stream close (processId: " + id + ", streamId: " + streamid + ")", e);
Throwable th = processThrowable(e);
if (th instanceof RuntimeException) {
throw (RuntimeException) th;
} else {
throw new ShellIOException(th);
}
} finally {
//TxSupport.clearUTForThread();
}
}
public String getSessionID() {
return sessid;
}
public boolean isValid() {
try {
reinitShell();
shell.using();
} catch (Exception ex) {
return false;
}
return true;
}
/**
* This method is called to replace an ErrorEOF object with such that it has properly processed IOException
* which is its payload.
*/
private Object replaceThrowable(Object o) {
if (o instanceof org.jboss.fresh.io.ErrorEOF) {
o = processThrowable(((org.jboss.fresh.io.ErrorEOF) o).getIOException());
if (!(o instanceof IOException)) {
o = new org.jboss.fresh.shell.ShellIOException((Throwable) o);
}
o = new org.jboss.fresh.io.ErrorEOF((IOException) o);
}
return o;
}
/**
* This method is called to replace each ErrorEOF object with such that it has properly processed IOException
* which is its payload.
*/
private LinkedList replaceThrowables(LinkedList l) {
LinkedList l2 = new LinkedList();
while (l.size() > 0) {
Object o = l.removeFirst();
if (o instanceof org.jboss.fresh.io.ErrorEOF) {
o = processThrowable(((org.jboss.fresh.io.ErrorEOF) o).getIOException());
if (!(o instanceof IOException)) {
o = new org.jboss.fresh.shell.ShellIOException((Throwable) o);
}
o = new org.jboss.fresh.io.ErrorEOF((IOException) o);
}
l2.add(o);
}
return l2;
}
/**
* This method inspects a passed throwable for any Throwables which should not be sent to the client
* and if necessary copies the entire stack trace in terms of ThrowableProxy - an extension of RuntimeException.
*/
private Throwable processThrowable(Throwable t) {
Throwable torig = t;
Throwable tnu;
boolean needReplace = false;
java.util.LinkedList ls = new java.util.LinkedList();
while (t != null) {
if (log.isDebugEnabled()) {
log.debug("t: " + t, t);
}
if (!isPartOfClientAPI(t.getClass())) {
needReplace = true;
}
tnu = new org.jboss.fresh.util.ThrowableProxy(t);
ls.add(tnu);
t = t.getCause();
}
t = (Throwable) ls.removeLast();
while (ls.size() > 0) {
tnu = (Throwable) ls.removeLast();
if (tnu == null) {
break;
}
tnu.initCause(t);
t = tnu;
}
if (needReplace) {
return t;
}
return torig;
}
private boolean isPartOfClientAPI(Class c) {
final String className = c.getName();
if (className.startsWith("java.")) {
return true;
}
if (className.startsWith("javax.")) {
return true;
}
if (className.startsWith("org.jboss.fresh.parsek.cms.")) {
return true;
}
String pkg = "org.jboss.fresh.shell.";
if (className.startsWith(pkg) && className.indexOf(".", pkg.length()) == -1) {
return true;
}
// try to get the names of classes which also should not be replaced
String nonreplacable;
try {
reinitShell();
nonreplacable = shell.getEnvProperty(KEEP_EXCEPTIONS_ENV_NAME);
} catch (Exception e) {
log_treplacer.warn("Could not read environment variable " + KEEP_EXCEPTIONS_ENV_NAME);
nonreplacable = "";
}
log_treplacer.info(KEEP_EXCEPTIONS_ENV_NAME + " = " + nonreplacable);
if(nonreplacable == null)
return false;
final StringTokenizer st = new StringTokenizer(nonreplacable, "\r\n ;,:");
while (st.hasMoreTokens()) {
final String el = st.nextToken();
if (matches(className, el)) {
return true;
}
}
return false;
}
private boolean matches(final String value, String search) {
if (search == null) {
return false;
}
search = search.trim();
if ("".equals(search)) {
return false;
}
final String s = search
.replaceAll("\\.", "\\.")
.replaceAll("\\?", ".")
.replaceAll("\\*", ".*");
final boolean b = value.startsWith(s); // matches(s);
log_treplacer.debug(value + " matches " + s + "? " + b + ", orig: " + search);
return b;
}
}