package org.jboss.fresh.shell.impl;
import org.jboss.beans.metadata.api.annotations.Inject;
import org.jboss.fresh.deployer.RegistryNamingBinder;
import org.jboss.fresh.registry.RegistryContext;
import org.jboss.fresh.shell.*;
import org.jboss.fresh.vfs.UserCtx;
import org.jboss.fresh.vfs.VFS;
import org.jboss.util.threadpool.ThreadPool;
import javax.naming.NamingException;
import java.util.*;
/**
* // This class is a server. It has a set of Shell instances.
* // These instances have to timeout if inactive.
* // Like jboss does with sessions we could persist the inactive ones out of memory.
* // And load them in if accessed after a long time.
* // We can also time them out ... we could specify a pretty high timeout ...
* // When they timeout we kill the processes and remove the Shell objects.
* // Since we need to be able to do an id based lookup, we will contain them in HashMap
*/
//@Bean(name = "SystemShell")
//@JndiBinding(name = "java:/FRESH/SystemShell")
public class SystemShellImpl extends RegistryNamingBinder implements SystemShell {
protected static org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(SystemShellImpl.class);
HashMap shellmap = new HashMap();
HashMap procmap = new HashMap();
//private String threadPoolName;
private String vfsName;
private Date startTime;
private ExecutableRegistry registry;
@Inject(bean = "jboss.system:service=ThreadPool")
private ThreadPool threadPool;
// GCThread gc;
Timer gc;
long gcPeriod = 10000L;
public SystemShellImpl(String vfsname) throws Exception {
this.vfsName = vfsname;
log.info("calling do start!");
startTime = new Date();
doStart();
reinitGC();
}
public void setThreadPool(ThreadPool threadPool) {
this.threadPool = threadPool;
}
protected String getBindClass() {
return SystemShell.class.getName();
}
protected Object classToInstance(Class c) {
return this;
}
/*public void setThreadPoolName(String threadPoolName) {
this.threadPoolName = threadPoolName;
}*/
/*public SystemShellImpl(String tpname, String vfsname) {
threadPoolName = tpname;
vfsName = vfsname;
startTime = new Date();
reinitGC();
}*/
public void setExecutableRegistry(ExecutableRegistry registry) {
this.registry = registry;
}
public void reinitGC() {
if (gc != null) gc.cancel();
gc = new Timer(true);
TimerTask tt = new TimerTask() {
public void run() {
doGC();
}
};
gc.scheduleAtFixedRate(tt, gcPeriod, gcPeriod);
}
public void setGCInterval(long val) {
//gc.setInterval(val);
if (val == gcPeriod) return;
gcPeriod = val;
reinitGC();
}
public long getGCInterval() {
//return gc.getInterval();
return gcPeriod;
}
synchronized private String newID() {
return org.jboss.fresh.util.UniqueNumberGenerator.getGUID();
}
private String newProcessID() {
return newID();
}
public VFS getVFS() {
VFS vfs = null;
try {
RegistryContext ctx = new RegistryContext();
vfs = (VFS) ctx.lookup(vfsName);
} catch (NamingException ex) {
log.error(ex.getMessage(), ex);
throw new RuntimeException("org.jboss.fresh.shell.impl.SystemShellImpl: startSession: " + vfsName + " not bound.");
}
return vfs;
}
public Shell startSession(UserCtx uctx, boolean interactive) {
// create new shell object
// assign id to it
// assign uctx to it
String id = newID();
VFS vfs = getVFS();
ShellImpl shell = new ShellImpl(id, interactive, this, uctx, vfs);
shell.setRegistry(registry);
// put it in shellmap
shellmap.put(id, shell);
// return it
return shell;
}
public Shell continueSession(String sessid) {
// lookup shell in the shellmap
Shell shell = (Shell) shellmap.get(sessid);
// return it
return shell;
}
public void closeSession(String sessid) {
// remove shell from map
//log.debug("### removing session: " + sessid);
log.debug("Removing session: " + sessid);
ShellImpl shell = (ShellImpl) shellmap.remove(sessid);
// kill all jobs that are active on this shell
// call dispose on ShellImpl
//log.debug("### disposing... " + shell);
log.debug("Disposing: " + shell);
if (shell != null) shell.dispose();
}
public Process findProcess(String id) throws ShellException {
return (Process) procmap.get(id);
}
public Process createProcess(Shell shell) throws ShellException {
// take PoolRunner from pool
try {
if (!"SINGLE".equals(shell.getEnvProperty("TMODE"))) {
//Object ob = threadPool.
if (threadPool == null)
throw new ShellException("No thread available. Either you have a thread leak on client side (you closing resources when finished with them?) or you should increase the max size of connection pool.");
}
String id = newProcessID();
Process parent = Process.getThreadParent();
Process p = new Process(id, parent, this, shell, threadPool);
if (parent == null)
p.setEnv(new EnvProperties(shell.getEnv()));
else
p.setEnv(new EnvProperties(parent.getEnv()));
procmap.put(id, p);
return p;
} catch (Exception ex) {
throw new ShellException(ex);
}
}
public Process createProcess(Shell shell, boolean useThreadPool) throws ShellException {
// take PoolRunner from pool
if (useThreadPool) return createProcess(shell);
String id = newProcessID();
Process parent = Process.getThreadParent();
Process p = new Process(id, parent, this, shell, threadPool);
if (parent == null)
p.setEnv(new EnvProperties(shell.getEnv()));
else
p.setEnv(new EnvProperties(parent.getEnv()));
procmap.put(id, p);
return p;
}
public ProcessGroup createProcessGroup(LinkedList procs) throws ShellException {
String id = newProcessID();
Process parent = Process.getThreadParent();
ProcessGroup pc = new ProcessGroup(id, parent, procs);
if (parent == null)
pc.setEnv(new EnvProperties(((Process) procs.getFirst()).getShell().getEnv()));
else
pc.setEnv(new EnvProperties(parent.getEnv()));
Iterator it = procs.iterator();
while (it.hasNext()) {
Process p = (Process) it.next();
p.setGroup(pc);
}
procmap.put(id, pc);
return pc;
}
public synchronized void endProcess(String id) {
// remove this id - nothing else
//log.debug("### SystemShellImpl : endProcess : " + id);
Process p = (Process) procmap.remove(id);
if (p == null) return;
//log.debug("SystemShellImpl : endProcess : 1");
p.getShell().removeProcess(id);
//log.debug("SystemShellImpl : endProcess : 2");
// All the following code only makes sure the ProcessGroup is removed when
// none of the Processes it contains is active any more.
ProcessGroup p2 = (ProcessGroup) p.getGroup();
//log.debug("SystemShellImpl : endProcess : 3");
if (p2 == null) return;
//log.debug("SystemShellImpl : endProcess : 4");
Iterator it = p2.getProcessList().iterator();
//log.debug("SystemShellImpl : endProcess : 5");
boolean keep = false;
//log.debug("SystemShellImpl : endProcess : 6");
while (it.hasNext()) {
Process p3 = (Process) it.next();
if (p3 == p) {
it.remove();
} else {
Process p4 = (Process) procmap.get(p3.getID());
if (p4 != null) {
keep = true;
break;
}
}
}
//log.debug("SystemShellImpl : endProcess : 7");
if (!keep) {
//log.debug("SystemShellImpl : endProcess : 8 (" + p2.getID() + ")");
procmap.remove(p2.getID());
if (p2 != p) p.getShell().removeProcess(p2.getID());
}
//log.debug("SystemShellImpl : endProcess : 9");
}
public Map getProcMap() {
return new HashMap(procmap);
}
public Map getShellMap() {
return new HashMap(shellmap);
}
public void shutdown() {
//gc.interrupt();
gc.cancel();
}
// we simply call doGC on every session we have
public void doGC() {
Map m = new HashMap(shellmap);
Iterator it = m.values().iterator();
// Commented out by boky (it's annoying!)
//log.debug("\n####\n#### GC: ");
while (it.hasNext()) {
ShellImpl sh = (ShellImpl) it.next();
sh.doGC();
}
}
/* class GCThread extends Thread {
private long ms=10000L; // 10 sec default
private SystemShellImpl sshell;
GCThread(SystemShellImpl sshell, String name) {
super(name);
GCThread.this.sshell=sshell;
}
public void setInterval(long val) {
ms=val;
}
public long getInterval() {
return ms;
}
public void run() {
// go to sleep for 10 sec
try {
while(true) {
//log.debug("[SystemShellImpl] GC: Going to sleep for " + (ms/1000) + " secs.");
Thread.sleep(ms);
//log.debug("[SystemShellImpl] GC: doing garbage collection ...");
sshell.doGC();
}
} catch(InterruptedException ex) {
}
//log.debug("[SystemShellImpl] GC: exiting");
}
}
*/
public Date getStartTime() {
return startTime;
}
}