package org.h3270.web; /* * Copyright (C) 2003-2008 akquinet tech@spree * * This file is part of h3270. * * h3270 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 3 of the License, or * (at your option) any later version. * * h3270 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 h3270; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA */ import java.io.*; import java.util.*; import javax.servlet.ServletContext; import javax.servlet.http.*; import org.apache.commons.codec.*; import org.apache.commons.codec.net.URLCodec; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.h3270.host.*; import org.h3270.logicalunit.LogicalUnitPool; import org.h3270.render.*; /** * @author Andre Spiegel spiegel@gnu.org * @version $Id: SessionState.java,v 1.23 2008/11/21 14:50:33 spiegel Exp $ */ public class SessionState implements HttpSessionBindingListener { private static final StringEncoder ENCODER; private static final StringDecoder DECODER; static { URLCodec codec = new URLCodec(); ENCODER = codec; DECODER = codec; } private static final Log logger = LogFactory.getLog(SessionState.class); public static final String TERMINAL = "org.h3270.Terminal.id"; public static final String COOKIE_NAME = "org.h3270.Settings"; private static final String COLORSCHEME = "colorscheme"; private static final String RENDERER = "renderer"; private static final String KEYPAD = "keypad"; private static final String FONTNAME = "fontname"; private final static String[] KEYS = { COLORSCHEME, RENDERER, KEYPAD, FONTNAME }; private final H3270Configuration h3270Config; private final Properties properties_ = new Properties(); private List terminalStates = null; /** * although it is never called tomcat seems to insist to have a no-args * constructor. */ public SessionState() { throw new IllegalStateException(); } public SessionState(H3270Configuration config, String savedState) throws IOException { h3270Config = config; String decoded; try { decoded = DECODER.decode(savedState); } catch (DecoderException e) { throw new IOException(); } // for safety do not use the restored properties directly. it could // contain unknown key/value mappings. Properties props = new Properties(); props.load(new ByteArrayInputStream(decoded.getBytes())); if (logger.isDebugEnabled()) { logger.debug("trying to restore SessionState " + props); } for (int x = 0; x < KEYS.length; ++x) { String key = KEYS[x]; if (props.containsKey(key)) { properties_.put(key, props.get(key)); } } } public String toString() { return "<SessionState: " + properties_.toString() + ">"; } public Terminal getTerminal (HttpServletRequest request) { return getTerminalState(request).getTerminal(); } public void setTerminal (HttpServletRequest request, Terminal terminal) { getTerminalState(request).setTerminal(terminal); } public String getHostname (HttpServletRequest request) { if (getTerminalState(request).getTerminal() != null) { return getTerminalState(request).getTerminal().getHostname(); } return null; } public String getLogicalUnitName(HttpServletRequest request) { if (getTerminalState(request).getTerminal() != null) { return getTerminalState(request).getTerminal().getLogicalUnit(); } return null; } public boolean isConnected (HttpServletRequest request) { return getTerminalState(request).getTerminal() != null; } /** * Returns the HTML code for the terminal area, or an informational * message if there is no active session. */ public String getScreen (HttpServletRequest request) { TerminalState tState = getTerminalState(request); Throwable exception = tState.getException(); if (exception != null) { StringBuffer b = new StringBuffer(); b.append ("<font color=\"red\"><b>Error</b></font><br/><br/>"); if (exception instanceof UnknownHostException) { String host = ((UnknownHostException)exception).getHost(); b.append ("Host <b>" + host + "</b> is unknown"); } else if (exception instanceof HostUnreachableException) { HostUnreachableException ex = (HostUnreachableException)exception; b.append ("Host <b>" + ex.getHost() + "</b> is not reachable from the h3270 server machine<br/>"); b.append ("(" + ex.getReason() + ")"); } else { b.append (exception.toString()); } return b.toString(); } else if (tState.getTerminal() != null) { return tState.getScreen().toString(); } else { StringBuffer b = new StringBuffer(); b.append("<b>h3270 version "); b.append(org.h3270.Version.value); b.append("</b>\n<br /><br />not connected\n"); return b.toString(); } } void setScreen (HttpServletRequest request, String screen) { getTerminalState(request).setScreen(screen); } public String getSavedState() throws IOException { ByteArrayOutputStream stream = new ByteArrayOutputStream(); properties_.store(stream, ""); String asString = stream.toString(); String state; try { state = ENCODER.encode(asString); } catch (EncoderException e) { throw new IOException(); } return state; } public String getFontName() { return getStringProperty(FONTNAME, h3270Config.getFontnameDefault()); } public Iterator getColorschemeSelectOptions (HttpServletRequest request) { List list = new ArrayList(); List colorSchemes = getColorSchemes(); ColorScheme acs = getActiveColorScheme (request); Iterator i = colorSchemes.iterator(); while (i.hasNext()) { ColorScheme scheme = (ColorScheme) i.next(); boolean selected = scheme.equals(acs); list.add(new SelectOptionBean(scheme.getName(), selected)); } return list.iterator(); } public Iterator getFontSelectOptions() { List list = new ArrayList(); Map validFonts = h3270Config.getValidFonts(); Iterator i = validFonts.keySet().iterator(); while (i.hasNext()) { String font = (String) i.next(); String fontName = (String) validFonts.get(font); boolean selected = font.equals(getFontName()); list.add(new SelectOptionBean(fontName, font, selected)); } return list.iterator(); } public void setFontName(String fontName) { put(FONTNAME, fontName); } private void put(String name, String value) { properties_.put(name, value); } private void put(String name, boolean value) { put(name, Boolean.toString(value)); } public ColorScheme getActiveColorScheme (HttpServletRequest request) { return getTerminalState(request).getActiveColorScheme(); } public List getColorSchemes() { return h3270Config.getColorSchemes(); } public boolean setActiveColorScheme (HttpServletRequest request, String schemeName) { ColorScheme scheme = h3270Config.getColorScheme(schemeName); if (logger.isDebugEnabled()) { logger.debug("setActiveColorScheme: " + scheme); } if (scheme != null) { logger.debug("OK"); getTerminalState(request).setActiveColorScheme (scheme); put(COLORSCHEME, schemeName); return true; } return false; } public void useKeypad (HttpServletRequest request, boolean useKeypad) { getTerminalState(request).useKeypad (useKeypad); put(KEYPAD, useKeypad); } public boolean useKeypad (HttpServletRequest request) { return getTerminalState(request).useKeypad(); } public void useRenderers (boolean useRenderers) { put(RENDERER, useRenderers); } public boolean useRenderers() { return getBooleanProperty(RENDERER, true); } public void setException (HttpServletRequest request, Throwable exception) { getTerminalState(request).setException(exception); } public Throwable getException (HttpServletRequest request) { return getTerminalState(request).getException(); } public void valueBound(HttpSessionBindingEvent arg0) { // nothing } public void valueUnbound(HttpSessionBindingEvent event) { // disconnect all s3270 sessions when the HttpSession times out and release // associated logical units in the pool for (Iterator i = terminalStates.iterator(); i.hasNext();) { TerminalState ts = (TerminalState) i.next(); if (ts.getTerminal() != null && ts.getTerminal().isConnected()) { if (logger.isInfoEnabled()) logger.info("Session unbound, disconnecting terminal " + ts.getTerminal()); ts.getTerminal().disconnect(); String logicalUnit = ts.getTerminal().getLogicalUnit(); if (logicalUnit != null) { ServletContext servletContext = event.getSession() .getServletContext(); LogicalUnitPool pool = (LogicalUnitPool) servletContext .getAttribute(LogicalUnitPool.SERVLET_CONTEXT_KEY); pool.releaseLogicalUnit(logicalUnit); } } } } private boolean parseBooleanString(String s) { return (s != null) && Boolean.valueOf(s).booleanValue(); } private boolean getBooleanProperty(String name) { return parseBooleanString((String) properties_.get(name)); } private boolean getBooleanProperty(String name, boolean defaultValue) { if (isPropertyDefined(name)) { return getBooleanProperty(name); } return defaultValue; } private boolean isPropertyDefined(String name) { return properties_.containsKey(name); } private String getStringProperty(String name) { String prop = (String) properties_.get(name); if (prop != null) { prop = prop.trim(); } return prop; } private String getStringProperty(String name, String defaultValue) { String prop = getStringProperty(name); if (prop == null) { prop = defaultValue; } return prop; } /** * Returns the identifier of the terminal that is associated * with a given request. */ public String getTerminalId (HttpServletRequest request) { String id = request.getParameter (TERMINAL); if (id == null) { id = (String)request.getAttribute (TERMINAL); } return id; } /** * Returns HTML code for a hidden parameter that stores the * identifier of the terminal that is associated with a request. */ public String getTerminalParam (HttpServletRequest request) { String id = request.getParameter (TERMINAL); if (id == null) { id = (String)request.getAttribute (TERMINAL); } if (id != null) { return "<input type=hidden name=" + TERMINAL + " value=" + id + ">"; } else { return ""; } } /** * Returns the TerminalState object that belongs to the terminal * that is associated with a particular request. */ private TerminalState getTerminalState (HttpServletRequest request) { if (terminalStates == null) { terminalStates = new ArrayList(); } String id = request.getParameter(TERMINAL); if (id == null) { id = (String)request.getAttribute(TERMINAL); } if (id == null) { request.setAttribute (TERMINAL, Integer.toString(terminalStates.size())); TerminalState result = createTerminalState(); terminalStates.add (result); return result; } else { try { int num = Integer.parseInt(id); return (TerminalState)terminalStates.get(num); } catch (IndexOutOfBoundsException e) { throw new RuntimeException("The session was disconnected, possibly due to a timeout."); } } } /** * Creates a new TerminalState object, initialized from the default * preferences for a terminal in this session. */ private TerminalState createTerminalState() { boolean useKeypad = false; if (isPropertyDefined (KEYPAD)) { useKeypad = getBooleanProperty(KEYPAD); } String schemeName = getStringProperty(COLORSCHEME, h3270Config.getColorSchemeDefault()); ColorScheme scheme = h3270Config.getColorScheme (schemeName); return new TerminalState (useKeypad, scheme); } }