0001: /*
0002: * Copyright 2001-2006 C:1 Financial Services GmbH
0003: *
0004: * This software is free software; you can redistribute it and/or
0005: * modify it under the terms of the GNU Lesser General Public
0006: * License Version 2.1, as published by the Free Software Foundation.
0007: *
0008: * This software is distributed in the hope that it will be useful,
0009: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0010: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0011: * Lesser General Public License for more details.
0012: *
0013: * You should have received a copy of the GNU Lesser General Public
0014: * License along with this library; if not, write to the Free Software
0015: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
0016: */
0017:
0018: package de.finix.contelligent.client.remote;
0019:
0020: import java.io.BufferedReader;
0021: import java.io.ByteArrayInputStream;
0022: import java.io.File;
0023: import java.io.FileNotFoundException;
0024: import java.io.IOException;
0025: import java.io.InputStreamReader;
0026: import java.io.PrintWriter;
0027: import java.io.Reader;
0028: import java.io.StringReader;
0029: import java.io.StringWriter;
0030: import java.net.Authenticator;
0031: import java.net.InetAddress;
0032: import java.net.PasswordAuthentication;
0033: import java.net.URL;
0034: import java.net.URLConnection;
0035: import java.security.AccessController;
0036: import java.util.ArrayList;
0037: import java.util.HashMap;
0038: import java.util.HashSet;
0039: import java.util.Iterator;
0040: import java.util.Map;
0041: import java.util.Set;
0042: import java.util.StringTokenizer;
0043: import java.util.logging.Level;
0044: import java.util.logging.Logger;
0045: import java.util.prefs.Preferences;
0046:
0047: import javax.security.auth.Subject;
0048: import javax.security.auth.kerberos.KerberosPrincipal;
0049: import javax.xml.parsers.DocumentBuilder;
0050: import javax.xml.parsers.DocumentBuilderFactory;
0051: import javax.xml.parsers.ParserConfigurationException;
0052:
0053: import org.apache.commons.httpclient.Cookie;
0054: import org.apache.commons.httpclient.Credentials;
0055: import org.apache.commons.httpclient.Header;
0056: import org.apache.commons.httpclient.HostConfiguration;
0057: import org.apache.commons.httpclient.HttpClient;
0058: import org.apache.commons.httpclient.HttpMethod;
0059: import org.apache.commons.httpclient.HttpMethodBase;
0060: import org.apache.commons.httpclient.HttpRecoverableException;
0061: import org.apache.commons.httpclient.HttpState;
0062: import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
0063: import org.apache.commons.httpclient.NTCredentials;
0064: import org.apache.commons.httpclient.UsernamePasswordCredentials;
0065: import org.apache.commons.httpclient.auth.AuthPolicy;
0066: import org.apache.commons.httpclient.cookie.CookiePolicy;
0067: import org.apache.commons.httpclient.methods.GetMethod;
0068: import org.apache.commons.httpclient.methods.MultipartPostMethod;
0069: import org.apache.commons.httpclient.methods.PostMethod;
0070: import org.apache.commons.httpclient.methods.multipart.ByteArrayPartSource;
0071: import org.apache.commons.httpclient.methods.multipart.FilePart;
0072: import org.apache.commons.httpclient.params.HttpClientParams;
0073: import org.w3c.dom.Document;
0074: import org.w3c.dom.Element;
0075: import org.w3c.dom.Node;
0076: import org.w3c.dom.NodeList;
0077: import org.xml.sax.InputSource;
0078: import org.xml.sax.SAXException;
0079: import org.xml.sax.SAXParseException;
0080:
0081: import sun.misc.RegexpPool;
0082: import de.c1fse.spnego.httpclient.Krb5Credentials;
0083: import de.c1fse.spnego.httpclient.NegotiateScheme;
0084: import de.finix.contelligent.client.base.ContelligentConstants;
0085: import de.finix.contelligent.client.base.Errors;
0086: import de.finix.contelligent.client.base.ServerInfo;
0087: import de.finix.contelligent.client.event.TaskFinishedEvent;
0088: import de.finix.contelligent.client.i18n.Resources;
0089: import de.finix.contelligent.client.modules.preferences.PreferencesModule;
0090: import de.finix.contelligent.client.security.StdAuthenticator;
0091: import de.finix.contelligent.client.util.xml.DumpImportHandler;
0092: import de.finix.contelligent.client.util.xml.XMLUtil;
0093: import de.zeigermann.xml.simpleImporter.SimpleImportHandler;
0094: import de.zeigermann.xml.simpleImporter.SimpleImporter;
0095:
0096: public class ServerConnector {
0097: private static Logger logger = Logger
0098: .getLogger(ServerConnector.class.getName());
0099:
0100: /* for some wire logging to stdout comment in: */
0101: static {
0102: AuthPolicy.registerAuthScheme(NegotiateScheme.SCHEME,
0103: NegotiateScheme.class);
0104:
0105: // System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog");
0106: // System.setProperty("org.apache.commons.logging.simplelog.showdatetime", "true");
0107: // System.setProperty("org.apache.commons.logging.simplelog.log.httpclient.wire", "debug");
0108: // System.setProperty("org.apache.commons.logging.simplelog.log.org.apache.commons.httpclient", "debug");
0109: }
0110:
0111: /**
0112: * regexp pool of hosts for which we should connect directly, not Proxy these are intialized from a property.
0113: */
0114: private static RegexpPool nonProxyHostsPool = null;
0115:
0116: /** The string source of nonProxyHostsPool */
0117: private static String nonProxyHostsSource = null;
0118:
0119: private static DocumentBuilder builder = null;
0120:
0121: private static DocumentBuilderFactory factory = DocumentBuilderFactory
0122: .newInstance();
0123:
0124: // XXX Does not work when reconnect to different server
0125: private static HttpClient httpClient = null;
0126:
0127: private static boolean httpPolicyIdentitfied = false;
0128:
0129: private static final String HEADER_KEY_SERVER = "Server";
0130:
0131: // oder the schemes: first is preferred
0132: private static final String[] schemeOrder = new String[] {
0133: "Negotiate", "Basic" };
0134:
0135: private static StdAuthenticator stdAuth;
0136:
0137: public static Cookie[] getCookies() {
0138: if (httpClient == null) {
0139: return null;
0140: }
0141: return httpClient.getState().getCookies();
0142: }
0143:
0144: public static String getCookie(String key) {
0145: if (httpClient == null) {
0146: return null;
0147: }
0148: Cookie[] cookies = httpClient.getState().getCookies();
0149:
0150: for (int i = 0; i < cookies.length; i++) {
0151: if (cookies[i].getName().equals(key)) {
0152: return cookies[i].getValue();
0153: }
0154: }
0155: return null;
0156: }
0157:
0158: public static synchronized void reset() {
0159: httpClient = null;
0160: httpPolicyIdentitfied = false;
0161: }
0162:
0163: private static synchronized HttpClient getHttpClient(URL server)
0164: throws RemoteActionException {
0165: if (httpClient == null) {
0166: MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
0167: connectionManager.setMaxConnectionsPerHost(3);
0168: httpClient = new HttpClient(connectionManager);
0169: httpClient.setTimeout(0); // infinite
0170: httpClient.setConnectionTimeout(15000);
0171: httpClient.setStrictMode(false);
0172: httpClient.getState().setCookiePolicy(
0173: CookiePolicy.COMPATIBILITY);
0174:
0175: // set the krb5 credentials for httpclient spnego support!
0176: HttpClientParams params = httpClient.getParams();
0177: /*
0178: * params.setParameter(CredentialsProvider.PROVIDER, new CredentialsProvider() { public Credentials
0179: * getCredentials(AuthScheme scheme, String host, int port, boolean proxy) throws
0180: * CredentialsNotAvailableException { if(scheme.getSchemeName().toUpperCase().equals("NEGOTIATE")) { return
0181: * new Krb5Credentials(System.getProperty("user.name"),null, host, System.getProperty("krb.domain",null))
0182: * {}; } return null; } });
0183: */
0184: // We need to use this UA string, or category dependent images wont
0185: // be
0186: // displayed correctly.
0187: params.setParameter("http.useragent",
0188: ContelligentConstants.CONTELLIGENT_USER_AGENT);
0189: HostConfiguration config = new HostConfiguration();
0190: String host = server.getHost();
0191: config
0192: .setHost(host, server.getPort(), server
0193: .getProtocol());
0194: httpClient.setHostConfiguration(config);
0195:
0196: Subject subj = Subject.getSubject(AccessController
0197: .getContext());
0198:
0199: KerberosPrincipal princ = null;
0200: Credentials creds = null;
0201: if (subj != null) {
0202: Set set = subj.getPrincipals(KerberosPrincipal.class);
0203: if (set.size() > 0)
0204: princ = (KerberosPrincipal) set.iterator().next();
0205:
0206: set = subj.getPrivateCredentials(Krb5Credentials.class);
0207: if (set.size() > 0) {
0208: creds = (Krb5Credentials) set.iterator().next();
0209: }
0210: }
0211:
0212: if (princ != null) {
0213: String kerbNameParts[] = princ.getName().split("@");
0214: creds = new Krb5Credentials(kerbNameParts[0], null,
0215: host,
0216: kerbNameParts.length > 0 ? kerbNameParts[1]
0217: : null);
0218: Subject.getSubject(AccessController.getContext())
0219: .getPrivateCredentials().add(creds);
0220: httpClient.getState().setCredentials(null, host, creds);
0221: Subject.getSubject(AccessController.getContext())
0222: .getPrivateCredentials().add(creds);
0223: } else {
0224: // try a good lookup in cache
0225: creds = new Krb5Credentials(System
0226: .getProperty("user.name"), null, host, System
0227: .getProperty("krb.domain", null));
0228: httpClient.getState().setCredentials(null, host, creds);
0229: Subject.getSubject(AccessController.getContext())
0230: .getPrivateCredentials().add(creds);
0231: }
0232:
0233: HashSet<String> authPrio = new HashSet<String>();
0234: authPrio.add(NegotiateScheme.SCHEME);
0235: httpClient.getParams().setParameter(
0236: AuthPolicy.AUTH_SCHEME_PRIORITY, authPrio);
0237:
0238: // httpClient.getState().setAuthenticationPreemptive(true);
0239:
0240: // XXX
0241: // set proxy settings set by java web start here as defined in:
0242: // http://forum.java.sun.com/thread.jsp?thread=71068&forum=38&message=743137
0243: // -DproxyHost=localhost -DproxyPort=8080
0244: String proxy = System.getProperty("proxyHost");
0245: int proxyPort = -1;
0246: if (proxy != null && !matchNonProxyHosts(host)) {
0247: proxyPort = Integer.parseInt(System
0248: .getProperty("proxyPort"));
0249: logger.log(Level.INFO, "Using Proxy '" + proxy + ":"
0250: + proxyPort + "'");
0251: config.setProxy(proxy, proxyPort);
0252: }
0253: int oldStatus = 0;
0254: while (true) {
0255: httpClient.getHttpConnectionManager()
0256: .closeIdleConnections(0);
0257: GetMethod method = new GetMethod();
0258: method.setPath(ServerInfo.getInstance().getServer()
0259: .getPath()
0260: + "/ServerInfo/");
0261: try {
0262: int statusCode = httpClient.executeMethod(method);
0263: if (statusCode == 200) {
0264: break;
0265: } else if (statusCode == 401) {
0266: // if WWW-Authenticate is given:
0267: clearSubject(subj);
0268: httpClient.getState().setCredentials(null,
0269: host, null);
0270:
0271: Map auth2Domain = parseAuthDomainMapping(method);
0272: // second time with the same status means: login
0273: // failed.
0274: setupHttpAuthenticator(auth2Domain,
0275: oldStatus == statusCode);
0276: oldStatus = statusCode;
0277:
0278: String realm = method.getAuthenticationRealm();
0279: String hostname = host;
0280: InetAddress ina = InetAddress
0281: .getByName(hostname);
0282:
0283: PasswordAuthentication auth = null;
0284: auth = Authenticator
0285: .requestPasswordAuthentication(
0286: hostname,
0287: ina,
0288: proxyPort,
0289: "http",
0290: Resources
0291: .getLocalString("http_auth_prompt"),
0292: null);
0293:
0294: if (auth == null) {
0295: throw new RemoteActionException(
0296: Errors.LOGIN_ABORTED);
0297: }
0298:
0299: String user = auth.getUserName();
0300: String scheme = "BASIC";
0301: String domain = null;
0302:
0303: int index = user.indexOf(':');
0304: if (index > -1) {
0305: scheme = user.substring(0, index);
0306: user = user.substring(index + 1);
0307: index = -1;
0308: }
0309:
0310: index = user.indexOf('\\');
0311: if (index <= -1) {
0312: index = user.indexOf('/');
0313: }
0314: if (index != -1) {
0315: // try NT credentials
0316: domain = user.substring(0, index);
0317: user = user.substring(index + 1);
0318: }
0319:
0320: String userDomainName = user
0321: + (domain != null ? ("@" + domain) : "");
0322:
0323: authPrio = new HashSet();
0324: authPrio.add(scheme);
0325: httpClient.getParams().setParameter(
0326: AuthPolicy.AUTH_SCHEME_PRIORITY,
0327: authPrio);
0328:
0329: if ("BASIC".equals(scheme.toUpperCase())) {
0330:
0331: Set set = subj
0332: .getPrivateCredentials(UsernamePasswordCredentials.class);
0333:
0334: if (set.size() > 0) {
0335: creds = (UsernamePasswordCredentials) set
0336: .iterator().next();
0337: } else {
0338: creds = new UsernamePasswordCredentials(
0339: userDomainName, new String(auth
0340: .getPassword()));
0341: subj.getPrivateCredentials().add(creds);
0342: }
0343:
0344: httpClient.getState().setCredentials(null,
0345: host, creds);
0346: httpClient.getState()
0347: .setAuthenticationPreemptive(true);
0348:
0349: } else if ("NEGOTIATE".equals(scheme
0350: .toUpperCase())) {
0351: Set set = subj
0352: .getPrivateCredentials(Krb5Credentials.class);
0353:
0354: if (set.size() > 0) {
0355: creds = (Krb5Credentials) set
0356: .iterator().next();
0357: } else {
0358: creds = new Krb5Credentials(user,
0359: new String(auth.getPassword()),
0360: hostname, domain);
0361: subj.getPrivateCredentials().add(creds);
0362: }
0363:
0364: httpClient.getState().setCredentials(null,
0365: host, creds);
0366: httpClient.getState()
0367: .setAuthenticationPreemptive(false);
0368: } else {
0369: throw new IOException(
0370: "unsupported WWW-Authenticate Scheme: "
0371: + scheme);
0372: }
0373:
0374: } else if (statusCode == 407) {
0375: // this of credentials request over proxies
0376: StdAuthenticator stdAuth = new StdAuthenticator(
0377: "HTTP-Proxy");
0378: Authenticator.setDefault(stdAuth);
0379: if (oldStatus == statusCode) {
0380: stdAuth.setMsg("proxy_login_failed");
0381: }
0382: oldStatus = statusCode;
0383:
0384: InetAddress ina = InetAddress.getByName(proxy);
0385: logger.log(Level.INFO, "Address " + ina);
0386: String proxyRealm = method
0387: .getProxyAuthenticationRealm();
0388: Header header = method
0389: .getResponseHeader("Proxy-Authenticate");
0390: String authMethod = header.getValue();
0391: if (authMethod.toUpperCase().indexOf("NTLM") > -1) {
0392: PasswordAuthentication auth = Authenticator
0393: .requestPasswordAuthentication(
0394: // proxy,
0395: ina,
0396: proxyPort,
0397: "http",
0398: Resources
0399: .getLocalString("proxy_auth_prompt"),
0400: "NTLM");
0401: if (auth == null)
0402: continue;
0403: String user = auth.getUserName();
0404: String password = new String(auth
0405: .getPassword());
0406:
0407: int index = user.indexOf('\\');
0408: if (index <= -1) {
0409: index = user.indexOf('/');
0410: }
0411: String domain = null;
0412: if (index != -1) {
0413: // try NT credentials
0414: domain = user.substring(0, index);
0415: user = user.substring(index + 1);
0416: }
0417: String hostname = InetAddress
0418: .getLocalHost().getHostName();
0419: logger.log(Level.INFO,
0420: "User, Password, Realm "
0421: + user
0422: + ":"
0423: + password.replaceAll(".",
0424: "x") + ":"
0425: + proxyRealm);
0426: httpClient.getState().setProxyCredentials(
0427: proxyRealm,
0428: proxy,
0429: new NTCredentials(user, password,
0430: hostname, domain));
0431: // do not break but make a continue
0432: continue;
0433: } else {
0434: String scheme = "unknown";
0435: if (authMethod.toUpperCase().indexOf(
0436: "DIGEST") > -1) {
0437: scheme = "Digest";
0438: } else if (authMethod.toUpperCase()
0439: .indexOf("BASIC") > -1) {
0440: scheme = "Basic";
0441: }
0442: PasswordAuthentication auth = Authenticator
0443: .requestPasswordAuthentication(
0444: // proxy,
0445: ina, proxyPort, "http",
0446: null, scheme);
0447: String user = auth.getUserName();
0448: String password = new String(auth
0449: .getPassword());
0450:
0451: logger.log(Level.INFO,
0452: "User, Password, Realm " + user
0453: + ":" + password + ":"
0454: + proxyRealm);
0455: httpClient.getState().setProxyCredentials(
0456: proxyRealm,
0457: proxy,
0458: new UsernamePasswordCredentials(
0459: user, password));
0460: // do not break but make a continue
0461: continue;
0462: }
0463: }
0464: } catch (IOException e) {
0465: httpClient = null;
0466: throw new RemoteActionException(e);
0467: } catch (RemoteActionException e) {
0468: httpClient = null;
0469: throw e;
0470: } finally {
0471: // Make sure we dont deplete the connection pool
0472: method.releaseConnection();
0473: }
0474: }
0475: // httpClient.setHttpConnectionFactoryTimeout(300);
0476: // httpClient.setConnectionTimeout(300);
0477: // httpClient.setTimeout(15000);
0478: }
0479: return httpClient;
0480: }
0481:
0482: /*
0483: * private static void setupHttpAuthenticator(String[] authMechs, String[] authDomainArr) { StdAuthenticator stdAuth =
0484: * new StdAuthenticator("HTTP",authDomainArr); Preferences prefs = PreferencesModule.getServerPreferences(); String
0485: * defaultAuthMech = prefs.get ( PreferencesModule.AUTH_MECH, PreferencesModule.DEFAULT_AUTH_MECH );
0486: *
0487: * stdAuth.setDefaultAuthMech(defaultAuthMech);
0488: *
0489: * stdAuth.setAvailableAuthMechs(authMechs);
0490: *
0491: * Authenticator.setDefault(stdAuth); }
0492: */
0493:
0494: private static void setupHttpAuthenticator(Map auth2Domain,
0495: boolean loginFailed) {
0496: stdAuth = new StdAuthenticator("HTTP", auth2Domain);
0497: Preferences prefs = PreferencesModule.getServerPreferences();
0498: String defaultAuthMech = prefs.get(PreferencesModule.AUTH_MECH,
0499: PreferencesModule.DEFAULT_AUTH_MECH);
0500:
0501: stdAuth.setDefaultAuthMech(defaultAuthMech);
0502:
0503: if (loginFailed)
0504: stdAuth.setMsg("login_failed");
0505:
0506: Authenticator.setDefault(stdAuth);
0507: }
0508:
0509: private static String[] parseAuthDomains(GetMethod method) {
0510: Header xWWWAuthDomains = method
0511: .getResponseHeader("X-WWW-AuthDomains");
0512: String authDomains = xWWWAuthDomains.getValue();
0513: String[] authDomainArr = authDomains.split(",");
0514: return authDomainArr;
0515: }
0516:
0517: private static Map<String, String[]> parseAuthDomainMapping(
0518: GetMethod method) {
0519: Header xWWWAuthDomains = method
0520: .getResponseHeader("X-WWW-AuthDomainMapping");
0521: if (xWWWAuthDomains == null) {
0522: return new HashMap();
0523: }
0524: String authDomains = xWWWAuthDomains.getValue();
0525: String[] authMappingArr = authDomains.split(";");
0526: HashMap<String, String[]> authMech2Domain = new HashMap<String, String[]>();
0527:
0528: for (int a = 0; a < authMappingArr.length; a++) {
0529: String[] tmp = authMappingArr[a].split("=");
0530: String key = tmp[0];
0531: String[] values = tmp[1].split(",");
0532: authMech2Domain.put(key, values);
0533: }
0534: return authMech2Domain;
0535: }
0536:
0537: private static String[] parseAuthMechs(GetMethod method) {
0538: Header[] headers = method
0539: .getResponseHeaders("WWW-Authenticate");
0540: String authMechs[] = parseAuthMechsFromHeaders(headers);
0541: return authMechs;
0542: }
0543:
0544: public static void clearSubject(Subject subj) {
0545: subj.getPrivateCredentials().clear();
0546: subj.getPrincipals().clear();
0547: subj.getPublicCredentials().clear();
0548: }
0549:
0550: private static String[] parseAuthMechsFromHeaders(Header[] headers) {
0551: String ret[] = new String[headers.length];
0552: for (int a = 0; a < headers.length; a++) {
0553: ret[a] = headers[a].getValue();
0554: if (ret[a].indexOf(' ') > 0) {
0555: ret[a] = ret[a].substring(0, ret[a].indexOf(' '));
0556: }
0557: }
0558: return ret;
0559: }
0560:
0561: protected static ActionResult performAction(RemoteAction action)
0562: throws RemoteActionException {
0563: String[][] parameter = new String[0][0];
0564: return performAction(action, parameter);
0565: }
0566:
0567: protected static ActionResult performAction(RemoteAction action,
0568: String[][] parameter) throws RemoteActionException {
0569: return parseActionResult(performActionRawResult(action,
0570: parameter, ServerInfo.getInstance().getServer()));
0571: }
0572:
0573: protected static ActionResult performAction(RemoteAction action,
0574: URL server) throws RemoteActionException {
0575: return parseActionResult(performActionRawResult(action,
0576: new String[0][0], server));
0577: }
0578:
0579: protected static Reader performActionRawResult(RemoteAction action)
0580: throws RemoteActionException {
0581: String[][] parameter = new String[0][0];
0582: return performActionRawResult(action, parameter, ServerInfo
0583: .getInstance().getServer());
0584: }
0585:
0586: protected static Reader performActionRawResult(RemoteAction action,
0587: String[][] parameter) throws RemoteActionException {
0588: return performActionRawResult(action, parameter, ServerInfo
0589: .getInstance().getServer());
0590: }
0591:
0592: private static String logStackTrace() {
0593: if (logger.isLoggable(Level.FINEST)) {
0594: StringWriter writer = new StringWriter();
0595: new Throwable().printStackTrace(new PrintWriter(writer));
0596: return writer.toString();
0597: } else {
0598: return "";
0599: }
0600: }
0601:
0602: private static void logRequest(String url, String[][] parameter) {
0603: // this is slow, but who cares (Olli Z.)
0604: String ext = "?";
0605: if (parameter != null) {
0606: for (int i = 0; i < parameter.length; i++) {
0607: ext += parameter[i][0] + "=" + parameter[i][1] + "&";
0608: }
0609: logger.log(Level.FINE, logStackTrace()
0610: + "tries to invoke '" + url + ext + "'");
0611: }
0612: }
0613:
0614: protected static Reader performActionRawResult(RemoteAction action,
0615: String[][] parameter, URL server)
0616: throws RemoteActionException {
0617:
0618: PostMethod method = new PostMethod();
0619:
0620: initHttpMethod(method, server, action);
0621:
0622: if (parameter != null) {
0623: for (int i = 0; i < parameter.length; i++) {
0624: if (parameter[i][0] == null || parameter[i][1] == null) {
0625: logger.log(Level.FINEST,
0626: "Parameter is null for action "
0627: + action.getPath() + " "
0628: + parameter[i][0] + "="
0629: + parameter[i][1]);
0630: } else {
0631: method.addParameter(parameter[i][0],
0632: parameter[i][1]);
0633: }
0634: }
0635: }
0636: setRequestHeaders(method);
0637: try {
0638: executeMethod(server, method);
0639:
0640: Reader in = new BufferedReader(new InputStreamReader(method
0641: .getResponseBodyAsStream(), method
0642: .getResponseCharSet()));
0643: StringBuffer sbuffer = new StringBuffer();
0644: char[] buffer = new char[4096];
0645: int r = -1;
0646:
0647: while ((r = in.read(buffer)) != -1) {
0648: sbuffer.append(buffer, 0, r);
0649: }
0650:
0651: return new StringReader(sbuffer.toString());
0652: } catch (IOException e) {
0653: throw new RemoteActionException(e);
0654: } finally {
0655: method.releaseConnection();
0656: method.recycle();
0657: }
0658: }
0659:
0660: public static void executeMethod(URL server, HttpMethodBase method)
0661: throws IOException, RemoteActionException {
0662: HttpClient client = getHttpClient(server);
0663: HttpState state = client.getState();
0664: try {
0665: client.executeMethod(method);
0666: } catch (HttpRecoverableException e) {
0667: // in case of revoverable give him a second chance.
0668: logger.log(Level.FINE,
0669: "Recoverable Http Exception caught :", e);
0670: logger
0671: .log(Level.FINE,
0672: "retrying executeMethod because exception was recoverable!");
0673: client.executeMethod(method);
0674: }
0675: Header[] headers = method.getResponseHeaders();
0676:
0677: identifyHttpClientPolicies(headers);
0678:
0679: Cookie[] currentCookies = state.getCookies();
0680: ArrayList<String> names = new ArrayList<String>(
0681: currentCookies.length);
0682:
0683: // work around a tomcat 3 flaw: tomcat 3 sends one Set-Cookie header per
0684: // cookie instead
0685: // of one header with a list of cookies
0686: // jakarta http-client cannot handle this situation so we have to do it
0687: // ourselves
0688: synchronized (state) {
0689: for (int i = 0; i < currentCookies.length; i++) {
0690: names.add(currentCookies[i].getName());
0691: }
0692:
0693: for (int i = 0; i < headers.length; i++) {
0694: if (headers[i].getName().equals("Set-Cookie")) {
0695: Cookie[] cookies = CookiePolicy
0696: .getCompatibilitySpec()
0697: .parse(server.getHost(), server.getPort(),
0698: server.getPath(), false, headers[i]);
0699: for (int j = 0; j < cookies.length; j++) {
0700: if (!names.contains(cookies[j].getName())) {
0701: state.addCookie(cookies[j]);
0702: }
0703: }
0704: }
0705: }
0706: }
0707: }
0708:
0709: /**
0710: * @param headers
0711: */
0712: private static void identifyHttpClientPolicies(Header[] headers) {
0713:
0714: if (!httpPolicyIdentitfied) {
0715: for (int a = 0; a < headers.length; a++) {
0716:
0717: if (HEADER_KEY_SERVER.equals(headers[a].getName())) {
0718: if (headers[a].getValue().indexOf("Orion") > -1) {
0719: if (setOrionHttpClientPolicies()) {
0720: httpPolicyIdentitfied = true;
0721: }
0722: } else {
0723: if (setDefaultHttpClientPolicies()) {
0724: httpPolicyIdentitfied = true;
0725: }
0726: }
0727: }
0728: }
0729: }
0730:
0731: }
0732:
0733: /**
0734: * @return
0735: */
0736: private static boolean setDefaultHttpClientPolicies() {
0737: if (httpClient == null) {
0738: return false;
0739: }
0740: httpClient.setStrictMode(false);
0741: httpClient.getState().setCookiePolicy(
0742: CookiePolicy.COMPATIBILITY);
0743: return true;
0744: }
0745:
0746: /**
0747: *
0748: */
0749: private static boolean setOrionHttpClientPolicies() {
0750: if (httpClient == null) {
0751: return false;
0752: }
0753: httpClient.setStrictMode(true);
0754: httpClient.getState().setCookiePolicy(CookiePolicy.RFC2109);
0755: return true;
0756: }
0757:
0758: protected static ActionResult performActionWithImportHandler(
0759: RemoteAction action, String[][] parameter,
0760: SimpleImportHandler importHandler)
0761: throws RemoteActionException, ParserConfigurationException,
0762: SAXException, IOException {
0763:
0764: Reader reader = ServerConnector.performActionRawResult(action,
0765: parameter);
0766:
0767: SimpleImporter importer = new SimpleImporter();
0768:
0769: // DO NOT REMOVE THIS LINE! Without it, the client will swallow every
0770: // whitespace on a 8192 byte boundary in the server response.
0771: importer.setTrimContent(false);
0772:
0773: // add user defined handler
0774: importer.addSimpleImportHandler(importHandler);
0775:
0776: // let's see what's going on (dump both on the fly and in buffer)
0777: DumpImportHandler dumpHandler = new DumpImportHandler(
0778: Level.FINEST, true);
0779: importer.addSimpleImportHandler(dumpHandler);
0780:
0781: ImportErrorHandler errorHandler = new ImportErrorHandler();
0782: importer.addSimpleImportHandler(errorHandler);
0783:
0784: importer.parse(new InputSource(reader));
0785:
0786: // ok everything worked out alright, let's have the full logging buffer
0787: logger.log(Level.FINEST, dumpHandler.getDump());
0788:
0789: ActionResult actionResult = errorHandler.getActionResult();
0790: return actionResult;
0791: }
0792:
0793: /**
0794: * The <code>performAction</code> method sends the given action request defined in the static fields above to the
0795: * server. The reader contains data that will be posted using 'POST' to the server
0796: *
0797: * @return an <code>Element</code> value
0798: * @throws RemoteActionException
0799: * if an error occurs
0800: */
0801: protected static ActionResult performAction(RemoteAction action,
0802: byte[] data, String contentType)
0803: throws RemoteActionException {
0804: URL server = ServerInfo.getInstance().getServer();
0805: PostMethod method = new PostMethod();
0806:
0807: initHttpMethod(method, server, action);
0808:
0809: method.setRequestHeader("content-type", contentType);
0810: method.setRequestBody(new ByteArrayInputStream(data));
0811: setRequestHeaders(method);
0812: try {
0813: executeMethod(server, method);
0814:
0815: Reader in = new BufferedReader(new InputStreamReader(method
0816: .getResponseBodyAsStream(), method
0817: .getResponseCharSet()));
0818: StringBuffer sbuffer = new StringBuffer();
0819: char[] buffer = new char[4096];
0820: int r = -1;
0821:
0822: while ((r = in.read(buffer)) != -1) {
0823: sbuffer.append(buffer, 0, r);
0824: }
0825:
0826: return parseActionResult(new StringReader(sbuffer
0827: .toString()));
0828: } catch (IOException e) {
0829: throw new RemoteActionException(e);
0830: } finally {
0831: method.releaseConnection();
0832: method.recycle();
0833: }
0834: }
0835:
0836: private static void initHttpMethod(HttpMethodBase method,
0837: URL server, RemoteAction action) {
0838: method.setPath(server.getPath() + action.getPath());
0839: method.setHttp11(true);
0840: method.setDoAuthentication(true);
0841: }
0842:
0843: protected static ActionResult parseActionResult(
0844: URLConnection urlConnection) throws RemoteActionException {
0845: return parseActionResult(urlConnection, false);
0846: }
0847:
0848: protected static ActionResult parseActionResult(
0849: URLConnection urlConnection, boolean isLogout)
0850: throws RemoteActionException {
0851:
0852: // XXX bye now this is only done in login action
0853: // parseActionResultHeader(urlConnection, isLogout);
0854: try {
0855: Reader in = new BufferedReader(new InputStreamReader(
0856: urlConnection.getInputStream(),
0857: getCharSet(urlConnection)));
0858:
0859: return parseActionResult(in);
0860: } catch (IOException e) {
0861: throw new RemoteActionException(Resources
0862: .getLocalString("no_connection"), e);
0863: }
0864: }
0865:
0866: private static String getCharSet(URLConnection urlConnection) {
0867: String contentType = urlConnection.getContentType();
0868: int index = contentType.indexOf("charset=");
0869: String charset = "UTF-8";
0870:
0871: if (index != -1) {
0872: charset = contentType.substring(index + 8).trim();
0873: }
0874: return charset;
0875: }
0876:
0877: protected static ActionResult parseActionResult(Reader in)
0878: throws RemoteActionException {
0879:
0880: ActionResult actionResult = new ActionResult();
0881: Reader reader = in;
0882:
0883: try {
0884: if (logger.isLoggable(Level.FINEST)) {
0885: StringBuffer string = new StringBuffer();
0886: char[] buffer = new char[4096];
0887: int r = 0;
0888:
0889: while ((r = in.read(buffer)) != -1) {
0890: string.append(buffer, 0, r);
0891: }
0892: logger.log(Level.FINEST, "-- SERVER response:\n"
0893: + string);
0894: reader = new StringReader(string.toString());
0895: }
0896: builder = factory.newDocumentBuilder();
0897: Document document = builder.parse(new InputSource(reader));
0898: Element response = (Element) document.getElementsByTagName(
0899: "response").item(0);
0900: if (response == null) {
0901: // action failed - no valid xml-response
0902: throw new RemoteActionException(Resources
0903: .getLocalString("no_valid_response"));
0904: } else {
0905: String state = XMLUtil.getAttributeValue(response,
0906: "state");
0907: actionResult.setState(state);
0908: if (XMLUtil.tagExists(response, "content")) {
0909: Element content = (Element) response
0910: .getElementsByTagName("content").item(0);
0911: actionResult.setContent(content);
0912: if (XMLUtil.tagExists(content, "paths")) {
0913: NodeList paths = content
0914: .getElementsByTagName("path");
0915: for (int j = 0; j < paths.getLength(); j++) {
0916: String path = XMLUtil.getContent(paths
0917: .item(j));
0918: actionResult.addPath(path);
0919: }
0920: }
0921: }
0922: // check for all error entries
0923: NodeList errorNodes = response.getChildNodes();
0924: for (int i = 0; i < errorNodes.getLength(); i++) {
0925: Node errorElement = errorNodes.item(i);
0926: if (errorElement.getNodeName().equals("error")) {
0927: String errorPath = XMLUtil.getAttributeValue(
0928: errorElement, "id");
0929: String level = XMLUtil.getAttributeValue(
0930: errorElement, "level");
0931: String origin = XMLUtil.getAttributeValue(
0932: errorElement, "origin");
0933: String timestamp = XMLUtil.getAttributeValue(
0934: errorElement, "timestamp");
0935: String faultstring = XMLUtil.getElementValue(
0936: (Element) errorElement, "shortMessage");
0937: String faultmessage = XMLUtil.getElementValue(
0938: (Element) errorElement, "longMessage");
0939: String details = XMLUtil.getElementValue(
0940: (Element) errorElement, "details");
0941: String path = XMLUtil
0942: .getOptionalAttributeValue(
0943: errorElement, "path");
0944: NodeList exceptionList = ((Element) errorElement)
0945: .getElementsByTagName("exception");
0946: String exceptionClass = null;
0947: String exceptionMessage = null;
0948: String exceptionStackTrace = null;
0949: if (exceptionList != null
0950: && exceptionList.item(0) != null) {
0951: Element exceptionElement = (Element) exceptionList
0952: .item(0);
0953: exceptionClass = XMLUtil.getAttributeValue(
0954: exceptionElement, "class");
0955: exceptionMessage = XMLUtil.getElementValue(
0956: exceptionElement,
0957: "exception-message");
0958: exceptionStackTrace = XMLUtil
0959: .getElementValue(exceptionElement,
0960: "stack-trace");
0961: }
0962: actionResult.addError(errorPath, faultstring,
0963: faultmessage, details, exceptionClass,
0964: exceptionMessage, exceptionStackTrace,
0965: level, origin, timestamp, path);
0966: }
0967: }
0968: }
0969: return actionResult;
0970: } catch (ParserConfigurationException pce) {
0971: // Parser with specified options can't be built
0972: throw new RemoteActionException(Resources
0973: .getLocalString("internal_error"), pce);
0974: } catch (SAXParseException spe) {
0975: throw new RemoteActionException(Resources
0976: .getLocalString("internal_error"), spe);
0977: } catch (SAXException sxe) {
0978: throw new RemoteActionException(Resources
0979: .getLocalString("internal_error"), sxe);
0980: } catch (IOException ioe) {
0981: throw new RemoteActionException(Resources
0982: .getLocalString("no_connection"), ioe);
0983: }
0984: }
0985:
0986: protected static ActionResult performUploadAction(
0987: RemoteAction action, String[][] parameter, String filename)
0988: throws RemoteActionException {
0989: Map<String, String> parameterMap = new HashMap<String, String>();
0990: Map<String, String> pathMap = new HashMap<String, String>();
0991:
0992: for (int i = 0; i < parameter.length; i++) {
0993: parameterMap.put(parameter[i][0], parameter[i][1]);
0994: }
0995: pathMap.put("file", filename);
0996: return performMultipartAction(action, parameterMap, null,
0997: pathMap);
0998: }
0999:
1000: protected static void setRequestHeaders(HttpMethod method) {
1001: method.setRequestHeader("Accept-Language", getLocaleString());
1002: method.setRequestHeader("Accept", "text/xml");
1003: method.setRequestHeader("User-Agent",
1004: ContelligentConstants.CONTELLIGENT_USER_AGENT);
1005: }
1006:
1007: private static String getLocaleString() {
1008: return Resources.getLocale().toString().replace('_', '-');
1009: }
1010:
1011: /**
1012: * @return true if host matches a host specified via the http.nonProxyHosts property XXX taken and adapted by Olli
1013: * Z. from sun.net.www.http.HttpClient
1014: */
1015: private static boolean matchNonProxyHosts(String host) {
1016: synchronized (ServerConnector.class) {
1017: String rawList = System.getProperty("http.nonProxyHosts");
1018: if (rawList == null) {
1019: nonProxyHostsPool = null;
1020: } else {
1021: if (!rawList.equals(nonProxyHostsSource)) {
1022: RegexpPool pool = new RegexpPool();
1023: StringTokenizer st = new StringTokenizer(rawList,
1024: "|", false);
1025: try {
1026: while (st.hasMoreTokens()) {
1027: pool.add(st.nextToken().toLowerCase(),
1028: Boolean.TRUE);
1029: }
1030: } catch (sun.misc.REException ex) {
1031: logger
1032: .log(
1033: Level.WARNING,
1034: "Error in http.nonProxyHosts system property",
1035: ex);
1036: }
1037: nonProxyHostsPool = pool;
1038: }
1039: }
1040: nonProxyHostsSource = rawList;
1041: }
1042:
1043: /*
1044: * Match against non-proxy hosts
1045: */
1046: if (nonProxyHostsPool == null) {
1047: return false;
1048: }
1049: if (nonProxyHostsPool.match(host) != null) {
1050: return true;
1051: } else {
1052: return false;
1053: }
1054: }
1055:
1056: /**
1057: * Perform a multipart action.
1058: *
1059: * @param action
1060: * an action which expects a multipart request.
1061: * @param parameters
1062: * the parameters of the HTTP request.
1063: * @param dataMap
1064: * @param sourceToPathMap
1065: *
1066: * @return the result of the action.
1067: *
1068: * @throws RemoteActionException
1069: */
1070: protected static ActionResult performMultipartAction(
1071: RemoteAction action, Map<String, String> parameters,
1072: Map<String, byte[]> dataMap,
1073: Map<String, String> sourceToPathMap)
1074: throws RemoteActionException {
1075:
1076: MultipartPostMethod method = new MultipartPostMethod();
1077:
1078: initHttpMethod(method, ServerInfo.getInstance().getServer(),
1079: action);
1080:
1081: if (parameters != null) {
1082: for (Iterator i = parameters.entrySet().iterator(); i
1083: .hasNext();) {
1084: Map.Entry entry = (Map.Entry) i.next();
1085:
1086: // use deprecated encode method to be compatible with server
1087: // using JRE 1.3
1088: method.addParameter((String) entry.getKey(),
1089: (String) entry.getValue());
1090: }
1091: }
1092:
1093: if (sourceToPathMap != null) {
1094: for (Iterator i = sourceToPathMap.entrySet().iterator(); i
1095: .hasNext();) {
1096: Map.Entry sourceAndPathEntry = (Map.Entry) i.next();
1097:
1098: try {
1099: // use deprecated encode method to be compatible with server
1100: // using JRE 1.3
1101: method.addParameter((String) sourceAndPathEntry
1102: .getKey(), new File(
1103: (String) sourceAndPathEntry.getValue()));
1104: } catch (FileNotFoundException e) {
1105: throw new RemoteActionException(e);
1106: }
1107: }
1108: }
1109:
1110: if (dataMap != null) {
1111: for (Iterator i = dataMap.entrySet().iterator(); i
1112: .hasNext();) {
1113: Map.Entry entry = (Map.Entry) i.next();
1114: byte[] data = (byte[]) entry.getValue();
1115:
1116: method.addPart(new FilePart((String) entry.getKey(),
1117: new ByteArrayPartSource("file", data)));
1118: }
1119: }
1120: setRequestHeaders(method);
1121: try {
1122: executeMethod(ServerInfo.getInstance().getServer(), method);
1123:
1124: Reader in = new InputStreamReader(method
1125: .getResponseBodyAsStream(), method
1126: .getResponseCharSet());
1127: StringBuffer sbuffer = new StringBuffer();
1128: char[] buffer = new char[4096];
1129: int r = -1;
1130:
1131: while ((r = in.read(buffer)) != -1) {
1132: sbuffer.append(buffer, 0, r);
1133: }
1134:
1135: return parseActionResult(new StringReader(sbuffer
1136: .toString()));
1137: } catch (IOException e) {
1138: throw new RemoteActionException(e);
1139: } finally {
1140: method.releaseConnection();
1141: method.recycle();
1142: }
1143: }
1144:
1145: static class ActionQueueEntry {
1146: private RemoteAction action;
1147:
1148: private String[][] parameter;
1149:
1150: private TaskFinishedEvent event;
1151:
1152: public ActionQueueEntry(RemoteAction action,
1153: String[][] parameter, TaskFinishedEvent event) {
1154: this .action = action;
1155: this .parameter = parameter;
1156: this .event = event;
1157: }
1158:
1159: public RemoteAction getAction() {
1160: return action;
1161: }
1162:
1163: public String[][] getParameter() {
1164: return parameter;
1165: }
1166:
1167: public TaskFinishedEvent getEvent() {
1168: return event;
1169: }
1170: }
1171: }
|