Java tutorial
/* * Copyright 2012, Digiwave Systems Ltd., Akber A. Choudhry, * and other individual contributors identified by the * @authors tag in each source artefact. * * Licensed under the Apache License, Version 2.0 (the "License"); * You may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied. * See the License for the specific language governing permissions * and limitations under the License. */ package com.dgwave.osrs; import static com.dgwave.osrs.Config.OSRSCONFIG; import static com.dgwave.osrs.Config.OSRSDIR; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.math.BigInteger; import java.security.KeyStore; import java.security.MessageDigest; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import javax.xml.XMLConstants; import javax.xml.bind.JAXBContext; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.parsers.SAXParserFactory; import javax.xml.transform.sax.SAXSource; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.scheme.PlainSocketFactory; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.PoolingClientConnectionManager; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.CoreConnectionPNames; import org.apache.http.params.HttpParams; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import com.dgwave.osrs.jaxb.Body; import com.dgwave.osrs.jaxb.DataBlock; import com.dgwave.osrs.jaxb.DtAssoc; import com.dgwave.osrs.jaxb.Header; import com.dgwave.osrs.jaxb.Item; import com.dgwave.osrs.jaxb.OPSEnvelope; import com.dgwave.osrs.jaxb.ObjectFactory; import com.dgwave.osrs.req.OsrsRequest; import com.dgwave.osrs.resp.OsrsResponse; /** * Singleton client that does all the low-level stuff for * communications. * * @author Akber Choudhry */ public class OsrsClient { /** * Singleton instance */ private static OsrsClient instance; /** * Fields */ private static Logger logger = LoggerFactory.getLogger(OsrsClient.class); private DefaultHttpClient httpClient; private String uri; private final String version = "0.9"; private final String protocol = "XCP"; private JAXBContext jc = null; // multi-threaded XMLReader xmlReader = null; private ObjectFactory oj = null; //just factory private OsrsResponseFactory orf = new OsrsResponseFactory(); /** * Get the singleton instance */ public static OsrsClient getInstance(boolean newHttpClient) { if (newHttpClient) { instance = null; } if (instance == null) { instance = new OsrsClient(); } return instance; } /** * This should be the only constructor called * It will recreate the httpClient whenever it is called, allowing */ private OsrsClient() { logger.info("New OsrsClient instance being created"); this.httpClient = new DefaultHttpClient(getConnectionManager(), getHttpParams()); this.uri = "https://" + OsrsConfig.getValue("osrs.host") + ":" + OsrsConfig.getValue("osrs.sslPort") + "/"; initJaxb(); } /** * Create HttpParams from configuration * @return HttpParams */ private HttpParams getHttpParams() { HttpParams params = new BasicHttpParams(); params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 60000); params.setParameter(CoreConnectionPNames.SO_TIMEOUT, 60000); return params; } /** * Creates a connection manager from configuration - handles test mode * @return ClientConnectionManager * @throws OsrsException */ private ClientConnectionManager getConnectionManager() throws OsrsException { // Create and initialize scheme registry SchemeRegistry schemeRegistry = new SchemeRegistry(); boolean testMode = "test".equals(OsrsConfig.getValue("osrs.environment")); int port = Integer.parseInt(OsrsConfig.getValue("osrs.port")); int sslPort = Integer.parseInt(OsrsConfig.getValue("osrs.sslPort")); schemeRegistry.register(new Scheme("http", port, PlainSocketFactory.getSocketFactory())); Scheme sslScheme; if (testMode) { try { sslScheme = new Scheme("https", sslPort, new SSLSocketFactory(getTestTrustStore())); } catch (Exception e) { throw new OsrsException("Error creating test SSL scheme", e); } } else { sslScheme = new Scheme("https", sslPort, SSLSocketFactory.getSocketFactory()); } schemeRegistry.register(sslScheme); // Create an HttpClient with the PoolingClientConnManager (thread-safe). // This connection manager must be used if more than one thread will // be using the HttpClient. ClientConnectionManager cm = new PoolingClientConnectionManager(schemeRegistry, 60, TimeUnit.SECONDS); return cm; } private KeyStore getTestTrustStore() { InputStream instream = null; try { KeyStore trustStore = KeyStore.getInstance("jks"); instream = this.getClass().getClassLoader().getResourceAsStream("opensrsCA.jks"); trustStore.load(instream, "'dgwaveopensrs'".toCharArray()); return trustStore; } catch (Exception e) { throw new OsrsException("Error with SSL root CA creation in test mode", e); } finally { try { instream.close(); } catch (Exception ignore) { } } } private String bytesToHex(byte[] bytes) { String s = new String(); BigInteger bi = new BigInteger(1, bytes); // Format to hexadecimal s = bi.toString(16); // 120ff0 if (s.length() % 2 != 0) { // Pad with 0 s = "0" + s; } return s; } private String getSignature(String xml) { String signature = new String(); try { MessageDigest md5 = MessageDigest.getInstance("MD5"); String concat = xml + OsrsConfig.getValue("osrs.key"); concat = bytesToHex(md5.digest(concat.getBytes())); signature = bytesToHex(md5.digest(concat.concat(OsrsConfig.getValue("osrs.key")).getBytes())); } catch (Exception ex) { logger.warn("Error generating signature: ", ex); } return signature; } /** * Raw send receive - one of two methods that executes network operations * * @param HttpPost * @return String * @throws OsrsException */ protected String sendReceive(HttpPost post) throws OsrsException { try { HttpResponse response = httpClient.execute(post); HttpEntity entity = response.getEntity(); String ret = EntityUtils.toString(entity); EntityUtils.consume(entity); return ret; } catch (Exception e) { throw new OsrsException("Error on base sendReceive", e); } } public OsrsResponse sendReceive(OsrsRequest request) throws OsrsException { String ret = sendReceive(getString(request)); OPSEnvelope resp = createEnvelope(ret); OsrsResponse response = orf.createResponse(request, resp); return response; } public String sendReceive(String xml) throws OsrsException { String signature = getSignature(xml); HttpPost postRequest = new HttpPost(uri); postRequest.addHeader("Content-Type", "text/xml"); postRequest.addHeader("X-Signature", signature); postRequest.addHeader("X-Username", OsrsConfig.getValue("osrs.userName")); try { postRequest.setEntity(new StringEntity(xml)); String response = sendReceive(postRequest); return response; } catch (Exception ex) { throw new OsrsException("Sending post got exception ", ex); } } protected OPSEnvelope createEnvelope(String ret) throws OsrsException { OPSEnvelope opsEnvelope = null; try { InputSource inputSource = new InputSource(new StringReader(ret)); SAXSource source = new SAXSource(xmlReader, inputSource); Unmarshaller u = jc.createUnmarshaller(); opsEnvelope = (OPSEnvelope) u.unmarshal(source); } catch (Exception e) { throw new OsrsException("Error parsing response: " + ret, e); } return opsEnvelope; } protected OPSEnvelope getEmptyEnvelope() throws OsrsException { initJaxb(); OPSEnvelope opsEnvelope = null; opsEnvelope = oj.createOPSEnvelope(); Header header = oj.createHeader(); header.setVersion(this.version); opsEnvelope.setHeader(header); DataBlock dBlock = oj.createDataBlock(); Body body = oj.createBody(); body.setDataBlock(dBlock); opsEnvelope.setBody(body); return opsEnvelope; } private void initJaxb() throws OsrsException { if (jc != null && oj != null) return; try { this.jc = JAXBContext.newInstance("com.dgwave.osrs.jaxb"); this.oj = new ObjectFactory(); SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); spf.setNamespaceAware(true); spf.setValidating(false); xmlReader = spf.newSAXParser().getXMLReader(); xmlReader.setEntityResolver(new EntityResolver() { @Override public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { logger.debug("Ignoring DTD"); return new InputSource(new StringReader("")); } }); } catch (Exception e) { throw new OsrsException("JAXB Error", e); } } protected String getString(OsrsRequest request) throws OsrsException { OPSEnvelope envelope = getEmptyEnvelope(); Map<String, Object> attrs = request.getAttributes(); Item pro = oj.createItem(); pro.setKey("protocol"); pro.setStringValue(this.protocol); Item obj = oj.createItem(); obj.setKey("object"); obj.setStringValue(request.getObject()); Item act = oj.createItem(); act.setKey("action"); act.setStringValue(request.getAction()); Item att = oj.createItem(); att.setKey("attributes"); att.addDtAssoc(processAttrs(attrs)); DtAssoc c = oj.createDtAssoc(); c.addItem(pro); c.addItem(act); c.addItem(obj); c.addItem(att); ((List<Object>) envelope.getBody().getDataBlock().getDtAass()).add(c); try { Marshaller m = jc.createMarshaller(); m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE); m.setProperty("com.sun.xml.internal.bind.xmlHeaders", "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n" + "<!DOCTYPE OPS_envelope SYSTEM '" + OSRSDIR + OSRSCONFIG + "ops.dtd'>"); //StringWriter writer = new StringWriter(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); m.marshal(envelope, baos); return baos.toString(); } catch (Exception e) { throw new OsrsException("Error creating envelope from request", e); } } private DtAssoc processAttrs(Map<String, Object> attrs) { DtAssoc dt = oj.createDtAssoc(); for (Iterator<String> iter = attrs.keySet().iterator(); iter.hasNext();) { String key = (String) iter.next(); if (attrs.get(key) instanceof String) { Item one = oj.createItem(); one.setKey(key); one.setStringValue((String) attrs.get(key)); dt.addItem(one); } } return dt; } }