Java tutorial
/* * Copyright (c) 2001-2011 Convertigo SA. * * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see<http://www.gnu.org/licenses/>. * * $URL$ * $Author$ * $Revision$ * $Date$ */ package com.twinsoft.convertigo.engine; import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.lang.reflect.Method; import java.net.URL; import java.sql.Timestamp; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.TimeZone; import java.util.Vector; import javax.servlet.http.HttpServletRequest; import org.apache.commons.httpclient.Cookie; import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpState; import org.apache.http.impl.client.CloseableHttpClient; import org.mozilla.javascript.Scriptable; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.Text; import com.twinsoft.convertigo.beans.connectors.JavelinConnector; import com.twinsoft.convertigo.beans.core.Block; import com.twinsoft.convertigo.beans.core.Connector; import com.twinsoft.convertigo.beans.core.ISheetContainer; import com.twinsoft.convertigo.beans.core.Pool; import com.twinsoft.convertigo.beans.core.Project; import com.twinsoft.convertigo.beans.core.RequestableObject; import com.twinsoft.convertigo.beans.core.ScreenClass; import com.twinsoft.convertigo.beans.core.Sequence; import com.twinsoft.convertigo.beans.core.Transaction; import com.twinsoft.convertigo.beans.transactions.AbstractHttpTransaction; import com.twinsoft.convertigo.engine.cache.CacheEntry; import com.twinsoft.convertigo.engine.enums.HttpPool; import com.twinsoft.convertigo.engine.enums.Parameter; import com.twinsoft.convertigo.engine.enums.RequestAttribute; import com.twinsoft.convertigo.engine.enums.SessionAttribute; import com.twinsoft.convertigo.engine.parsers.HtmlParser; import com.twinsoft.convertigo.engine.parsers.XulRecorder; import com.twinsoft.convertigo.engine.util.CachedIntrospector; import com.twinsoft.convertigo.engine.util.Crypto2; import com.twinsoft.convertigo.engine.util.GenericUtils; import com.twinsoft.convertigo.engine.util.HttpUtils; import com.twinsoft.convertigo.engine.util.TwsCachedXPathAPI; import com.twinsoft.convertigo.engine.util.URLrewriter; import com.twinsoft.twinj.Javelin; public class Context extends AbstractContext implements Cloneable { public LogParameters logParameters = new LogParameters(); public String name; public CacheEntry cacheEntry; public boolean noCache = false; public boolean isDestroying = false; public boolean isErrorDocument = false; public boolean isXsltRequest = false; public boolean isAsync; public boolean isStubRequested; public int waitingRequests = 0; public boolean isNewSession; public boolean isRequestFromVic = false; public boolean isTrustedRequest = false; public long documentSignatureSent = 0; public long documentSignatureReceived = 0; public final EngineStatistics statistics = new EngineStatistics(); public Map<String, Block> previousFields = new HashMap<String, Block>(); public String absoluteSheetUrl; public String sheetUrl; public String contentType; public String cacheControl; public Project project; public Connector connector; public Pool pool; public int poolContextNumber; public String projectName; public String sequenceName; public String transactionName; public String connectorName; public RequestableObject requestedObject; public ISheetContainer lastDetectedObject; public boolean removeNamespaces = false; // compatibility with older versions public ScreenClass lastDetectedScreenClass = null; public Transaction transaction = null;// public String subPath = ""; public HtmlParser htmlParser = null; public HttpState httpState = null; private Header[] responseHeaders = new Header[] {}; private TwsCachedXPathAPI xpathApi = null; public boolean tasSessionKeyVerified = false; public IdToXpathManager idToXpathManager = null; public URLrewriter urlRewriter = null; private Map<String, Connector> used_connectors = new HashMap<String, Connector>(); private Set<Connector> opened_connectors = new HashSet<Connector>(); private boolean requireRemoval = false; private Map<String, List<String>> requestHeaders = null; private Scriptable sharedScope = null; private XulRecorder xulRecorder = null; private HttpClient httpClient3 = null; private CloseableHttpClient httpClient4 = null; public Context(String contextID) { this.contextID = contextID; // boolean bContextsWriteLogs = (Engine.getProperty(Engine.ConfigurationProperties.CONTEXTS_WRITE_LOGS).equalsIgnoreCase("true")); // if (Engine.isStudioMode()) { // log.setOutputStream(Engine.objectsProvider.getContextOutputStream()); // Engine.logContext.debug("Context log output stream: studio"); // } // else if (bContextsWriteLogs) { // try { // String contextLogOutputStream = Engine.LOG_DIRECTORY + "/" + getLogFilename(); // Engine.logContext.debug("Context log output stream: " + contextLogOutputStream); // log.setOutputStream(new FileOutputStream(contextLogOutputStream, true)); // } // catch(Exception e) { // Engine.log.exception(e, "Unable to set the output stream for context log"); // } // } } public void reset() { isXsltRequest = false; isErrorDocument = false; isStubRequested = false; isNewSession = false; isCacheEnabled = true; cacheEntry = null; noCache = false; sheetUrl = null; absoluteSheetUrl = null; contentType = null; cacheControl = null; connectorName = null; sequenceName = null; transactionName = null; requestedObject = null; lastDetectedObject = null; userReference = null; removeNamespaces = false; // For compatibility with older javelin projects lastDetectedScreenClass = null; transaction = null; if (steps != null) steps.clear(); // Reset last responseExpiryDate set (#4201) remove(Parameter.ResponseExpiryDate.getName()); Engine.logContext.debug("Context reset"); } public String getProjectDirectory() { String dir = null; if (projectName != null) { dir = Engine.PROJECTS_PATH + "/" + projectName; } return dir; } public String getProjectName() { String sName = null; if (projectName != null) { sName = projectName; } return sName; } private static String webinfPath = null; private static String getWebInfPath() { if (webinfPath == null) { URL engineProps = Engine.class.getResource(EnginePropertiesManager.PROPERTIES_FILE_NAME); String path = engineProps.getPath(); path = path.replaceAll("%20", " "); int start = Engine.isLinux() ? 0 : 1; int end = path.lastIndexOf(EnginePropertiesManager.PROPERTIES_FILE_NAME); webinfPath = path.substring(start, end + 1); Engine.logContext.debug("Convertigo bin path : " + webinfPath); } return webinfPath; } /** * Loads a Properties object from a file. * */ public Properties loadPropertiesFromWebInf(String fileName) { if ((fileName != null) && (fileName.length() != 0)) { String path = getWebInfPath() + fileName; return loadProperties(path); } return null; } /** * Loads a Properties object from a file. * */ public Properties loadPropertiesFromProject(String fileName) { if ((fileName != null) && (fileName.length() != 0)) { String path = getProjectDirectory() + "/" + fileName; return loadProperties(path); } return null; } private Properties loadProperties(String path) { File file = new File(path); // Loads properties from file if (file.exists()) { try { FileInputStream fin = new FileInputStream(file); Properties properties = new Properties(); properties.load(fin); return properties; } catch (FileNotFoundException e) { Engine.logContext .warn("Problems occured while loading properties : file '" + path + "' not found!"); } catch (IOException e) { Engine.logContext.error("Problems occured while loading properties from file '" + path + "'", e); } } return null; } /** * Store a Properties object to a file. */ public boolean savePropertiesToWebInf(String fileName, Properties properties) { if ((fileName != null) && (properties != null) && (fileName.length() != 0)) { String path = getWebInfPath() + fileName; return saveProperties(path, properties); } return false; } /** * Store a Properties object to a file. */ public boolean savePropertiesToProject(String fileName, Properties properties) { if ((fileName != null) && (properties != null) && (fileName.length() != 0)) { String path = getProjectDirectory() + "/" + fileName; return saveProperties(path, properties); } return false; } private boolean saveProperties(String path, Properties properties) { File file = new File(path); // Creates file if needed if (!file.exists()) { try { if (file.createNewFile()) { Engine.logContext.warn("File '" + path + "' has been created"); } else { Engine.logContext.warn("Problems occured while creating file '" + path + "'"); } } catch (Exception e) { Engine.logContext.error("Problems occured while creating file '" + path + "'", e); } } // Store properties to file if (file.exists()) { try { FileOutputStream fos = new FileOutputStream(file); properties.store(fos, ""); fos.flush(); fos.close(); return true; } catch (FileNotFoundException e) { Engine.logContext.warn( "Problems occured while saving properties '" + name + "': file '" + path + "' not found!"); } catch (IOException e) { Engine.logContext.error( "Problems occured while saving properties '" + name + "' to file '" + path + "'", e); } } return false; } public Header[] getResponseHeaders() { return responseHeaders; } public void setResponseHeaders(Header[] responseHeaders) { this.responseHeaders = responseHeaders; } public Vector<String> getCookieStrings() { // Use the HandleCookie Property of the transaction to return or not the cookies. // // We noticed a Bug in tomcat when too much cookies where set in the response to the client. This causes a // IndexOutOfBoundException: 4096 in coyote. // To overcome this situation, now you can configure HandleCookies to false in the transaction to prevent cookies to be reflected // to the client. if (requestedObject instanceof AbstractHttpTransaction) { if (!((AbstractHttpTransaction) requestedObject).isHandleCookie()) return new Vector<String>(); } Vector<String> cookies = new Vector<String>(); if (httpState != null) { Cookie[] httpCookies = httpState.getCookies(); int len = httpCookies.length; Cookie cookie = null; String sCookie; DateFormat df = new SimpleDateFormat("EEE, dd-MMM-yyyy HH:mm:ss z", Locale.US); df.setTimeZone(TimeZone.getTimeZone("GMT")); for (int i = 0; i < len; i++) { cookie = httpCookies[i]; sCookie = cookie.getName() + "=" + cookie.getValue() + ";"; sCookie += (cookie.getExpiryDate() != null) ? "expires=" + df.format(cookie.getExpiryDate()) + ";" : ""; sCookie += "path=" + cookie.getPath() + ";"; sCookie += "domain=" + cookie.getDomain() + ";"; sCookie += cookie.getSecure() ? "secure" : ""; cookies.add(sCookie); } } return cookies; } public String getSequenceTransactionSessionId() { if (requestedObject instanceof Sequence) { return ((Sequence) requestedObject).getTransactionSessionId(); } return null; } public Node addTextNodeUnderRoot(String tagName, String text) { return addTextNode(outputDocument.getDocumentElement(), tagName, text); } public Node addTextNodeUnderBlocks(String tagName, String text) { return addTextNode(outputDocument.getDocumentElement().getElementsByTagName("blocks").item(0), tagName, text); } public Node addTextNode(Node parentNode, String tagName, String text) { Document doc = parentNode.getOwnerDocument(); Element newElement = doc.createElement(tagName); if (text != null) { Text textElement = doc.createTextNode(text); newElement.appendChild(textElement); } parentNode.appendChild(newElement); return newElement; } public boolean waitNextPage(String action, int timeout, int hardDelay) throws EngineException { return ((JavelinConnector) connector).waitNextPage(this, action, timeout, hardDelay); } public boolean waitNextPage(Javelin javelin, String action, int timeout, int hardDelay) throws EngineException { return waitNextPage(action, timeout, hardDelay); } public boolean waitAtScreenClass(int timeout, int hardDelay) throws EngineException { return ((JavelinConnector) connector).waitAtScreenClass(this, timeout, hardDelay); } public boolean waitAtScreenClass(Javelin javelin, int timeout, int hardDelay) throws EngineException { return waitAtScreenClass(timeout, hardDelay); } public IdToXpathManager getIdToXpathManager() { if (idToXpathManager == null) { idToXpathManager = new IdToXpathManager(); } return idToXpathManager; } public TwsCachedXPathAPI getXpathApi() { if (xpathApi == null) { xpathApi = new TwsCachedXPathAPI(project); } return xpathApi; } public void cleanXpathApi() { xpathApi = null; } public Object getTransactionProperty(String propertyName) { if (requestedObject == null) { return null; } BeanInfo bi; try { bi = CachedIntrospector.getBeanInfo(requestedObject.getClass()); } catch (IntrospectionException e) { Engine.logContext .error("getTransactionProperty : Exception while finding the bean info for transaction class '" + requestedObject.getClass() + "'", e); return null; } PropertyDescriptor[] propertyDescriptors = bi.getPropertyDescriptors(); int len2 = propertyDescriptors.length; PropertyDescriptor propertyDescriptor = null; Object propertyValue; int j; String propertyDescriptorName = ""; for (j = 0; j < len2; j++) { propertyDescriptor = propertyDescriptors[j]; propertyDescriptorName = propertyDescriptor.getName(); if (propertyDescriptorName.equals(propertyName)) break; } if (j == len2 && !propertyDescriptorName.equals(propertyName)) { Engine.logContext.debug("getTransactionProperty : no property descriptor found for the property '" + propertyName + "'"); return null; } Method getter = propertyDescriptor.getReadMethod(); Object args[] = {}; try { propertyValue = getter.invoke(requestedObject, args); } catch (Exception e) { Engine.logContext.error("getTransactionProperty : Exception while executing the property getter '" + getter.getName() + "'", e); return null; } return propertyValue; } public boolean isSOAPRequest() { String servletPath = httpServletRequest.getServletPath(); return (servletPath.endsWith(".ws") || servletPath.endsWith(".wsl")); } public Connector getConnector() { return connector; } public Connector loadConnector(String connectorName) throws EngineException { this.connectorName = connectorName; loadConnector(); return getConnector(); } public void loadConnector() throws EngineException { if (connectorName == null) connectorName = project.getDefaultConnector().getName(); String key = project.getName() + '\n' + connectorName; if (used_connectors.containsKey(key)) { setConnector(used_connectors.get(key)); Engine.logContext.debug("Re-use connector: " + connectorName); } else { setConnector(project.getConnectorByName(connectorName)); Engine.logContext.debug("Loaded connector: " + connectorName); getConnector().checkSymbols(); used_connectors.put(key, getConnector()); } } public void loadSequence() throws EngineException { Sequence sequence = project.getSequenceByName(sequenceName); // clone sequence in studio, needed by #3188 - Order parallel step response if (sequence.isOriginal()) { try { sequence = sequence.cloneKeepParent(); } catch (CloneNotSupportedException e) { Engine.logContext.error("Clone of sequence failed ! Use the original.", e); } } requestedObject = sequence; Engine.logContext.debug("Sequence loaded: " + requestedObject.getName()); requestedObject.checkSymbols(); setConnector(null); transaction = null; transactionName = null; } public void setConnector(Connector connector) { if (this.connector != null && this.connector != connector) Engine.logContext.debug("Connector name differs from previous one; switch connector"); this.connector = connector; if (connector != null) { this.connectorName = connector.getName(); connector.context = this; isNewSession = opened_connectors.add(connector); } else { this.connectorName = null; } } public void abortRequestable() { if (requestedObject != null) { requestedObject.abort(); } } public Collection<Connector> getOpenedConnectors() { return Collections.unmodifiableCollection(opened_connectors); } public void clearConnectors() { used_connectors.clear(); opened_connectors.clear(); } public void requireRemoval(boolean remove) { this.requireRemoval = remove; } public boolean removalRequired() { return this.requireRemoval; } public String getLogFilename() { Timestamp ts = new Timestamp(System.currentTimeMillis()); String escapedContextID = contextID; escapedContextID = escapedContextID.substring(0, escapedContextID.indexOf("_")); escapedContextID = escapedContextID.replace('/', '_'); escapedContextID = escapedContextID.replace('\\', '_'); String logFilename = ts.toString().substring(0, 10) + "_context_" + escapedContextID + ".log"; return logFilename; } public String decodeFromHexString(String s) { return Crypto2.decodeFromHexString(s); } public String encodeToHexString(String s) { return Crypto2.encodeToHexString(s); } public String decodeFromHexString(String passphrase, String s) { return Crypto2.decodeFromHexString(passphrase, s); } public String encodeToHexString(String passphrase, String s) { return Crypto2.encodeToHexString(passphrase, s); } public Map<String, List<String>> getRequestHeaders() { if (requestHeaders == null) { requestHeaders = new HashMap<String, List<String>>(); if (httpServletRequest != null) { for (String name : Collections .list(GenericUtils.<Enumeration<String>>cast(httpServletRequest.getHeaderNames()))) { String lowName = name.toLowerCase(); for (String value : Collections .list(GenericUtils.<Enumeration<String>>cast(httpServletRequest.getHeaders(name)))) { List<String> values = requestHeaders.get(lowName); if (values == null) { values = new LinkedList<String>(); requestHeaders.put(lowName, values); } values.add(value); } } } requestHeaders = Collections.unmodifiableMap(requestHeaders); } return requestHeaders; } public void setRequest(HttpServletRequest request) { httpServletRequest = request; httpSession = request.getSession(); requestHeaders = null; } public void clearRequest() { httpServletRequest = null; httpSession = null; requestHeaders = null; parentContext = null; } public Scriptable getSharedScope() { return sharedScope; } public void setSharedScope(Scriptable sharedScope) { this.sharedScope = sharedScope; } protected void checkXulRecorder() { if (xulRecorder != null) { if (xulRecorder.checkExpired()) { xulRecorder = null; } } } public void newXulRecorder(String urlRegex, int entryLifetime) { this.xulRecorder = new XulRecorder(urlRegex, entryLifetime); } public XulRecorder getXulRecorder() { return xulRecorder; } public boolean isXulRecording(String url) { return xulRecorder != null ? xulRecorder.isRecording(url) : false; } public void stopXulRecording() { if (xulRecorder != null) { xulRecorder.stopRecording(); } } public Context getRootContext() { return parentContext == null ? this : parentContext.getRootContext(); } @Override public Context clone() throws CloneNotSupportedException { Context clone = (Context) super.clone(); if (logParameters != null) { clone.logParameters = (LogParameters) logParameters.clone(); } return clone; } public HttpClient getHttpClient3(HttpPool httpPool) { switch (httpPool) { case no: return HttpUtils.makeHttpClient3(false); case context: synchronized (used_connectors) { if (httpClient3 == null) { Engine.logContext.debug("Create a new context HTTP pool."); httpClient3 = HttpUtils.makeHttpClient3(true); } } return httpClient3; case session: HttpClient httpClient; synchronized (used_connectors) { httpClient = SessionAttribute.httpClient3.get(httpSession); if (httpClient == null) { Engine.logContext.debug("Create a new session HTTP pool."); httpClient = HttpUtils.makeHttpClient3(true); SessionAttribute.httpClient3.set(httpSession, httpClient); } } return httpClient; case global: return Engine.theApp.httpClient; } return null; } public CloseableHttpClient getHttpClient4(HttpPool httpPool) { switch (httpPool) { case no: return HttpUtils.makeHttpClient4(false); case context: if (httpClient4 == null) { httpClient4 = HttpUtils.makeHttpClient4(true); } return httpClient4; case session: CloseableHttpClient httpClient = SessionAttribute.httpClient4.get(httpSession); if (httpClient == null) { httpClient = HttpUtils.makeHttpClient4(true); SessionAttribute.httpClient4.set(httpSession, httpClient); } return httpClient; case global: return Engine.theApp.httpClient4; } return null; } @Override public void setResponseHeader(String name, String value) { if (httpServletRequest != null) { Map<String, String> headers = RequestAttribute.responseHeader.get(httpServletRequest); if (headers == null) { RequestAttribute.responseHeader.set(httpServletRequest, headers = new HashMap<String, String>()); } headers.put(name, value); } } @Override public void setResponseStatus(Integer code, String text) { if (httpServletRequest != null) { Map<Integer, String> status = RequestAttribute.responseStatus.get(httpServletRequest); if (status == null) { RequestAttribute.responseStatus.set(httpServletRequest, status = new HashMap<Integer, String>()); } status.clear(); status.put(code, text); } } }