001    /*
002    // $Id: XmlaOlap4jHttpProxy.java 278 2009-09-30 13:05:54Z lucboudreau $
003    // This software is subject to the terms of the Eclipse Public License v1.0
004    // Agreement, available at the following URL:
005    // http://www.eclipse.org/legal/epl-v10.html.
006    // Copyright (C) 2007-2008 Julian Hyde
007    // All Rights Reserved.
008    // You must accept the terms of that agreement to use this software.
009    */
010    package org.olap4j.driver.xmla.proxy;
011    
012    import java.io.*;
013    import java.net.*;
014    import java.util.concurrent.*;
015    
016    import org.olap4j.driver.xmla.XmlaOlap4jDriver;
017    import org.olap4j.impl.Base64;
018    
019    /**
020     * Extends the AbstractCachedProxy and serves as
021     * a production ready http communication class. Every SOAP request
022     * sends a POST call to the destination XMLA server and returns
023     * the response as a byte array, conforming to the Proxy interface.
024     *
025     * <p>It also takes advantage of the AbstractHttpProxy cookie
026     * managing facilities. All cookies received from the end point
027     * server will be sent back if they are not expired and they also
028     * conform to cookie domain rules.
029     *
030     * @author Luc Boudreau and Julian Hyde
031     * @version $Id: XmlaOlap4jHttpProxy.java 278 2009-09-30 13:05:54Z lucboudreau $
032     */
033    public class XmlaOlap4jHttpProxy extends XmlaOlap4jAbstractHttpProxy
034    {
035        private final XmlaOlap4jDriver driver;
036    
037        /**
038         * Creates a XmlaOlap4jHttpProxy.
039         *
040         * @param driver Driver
041         */
042        public XmlaOlap4jHttpProxy(XmlaOlap4jDriver driver) {
043            this.driver = driver;
044        }
045    
046        @Override
047        public byte[] getResponse(URL url, String request)
048            throws XmlaOlap4jProxyException
049        {
050            URLConnection urlConnection = null;
051            try {
052                // Open connection to manipulate the properties
053                urlConnection = url.openConnection();
054                urlConnection.setDoOutput(true);
055    
056                // Set headers
057                urlConnection.setRequestProperty(
058                    "content-type",
059                    "text/xml");
060                urlConnection.setRequestProperty(
061                    "User-Agent",
062                    "Olap4j("
063                        .concat(driver.getVersion())
064                        .concat(")"));
065                urlConnection.setRequestProperty(
066                    "Accept",
067                    "text/xml;q=1");
068                urlConnection.setRequestProperty(
069                    "Accept-Charset",
070                    getEncodingCharsetName()
071                        .concat(";q=1"));
072    
073                // Some servers expect a SOAPAction header.
074                // TODO There is bound to be a better way to do this.
075                if (request.contains(
076                    "<Discover xmlns=\"urn:schemas-microsoft-com:xml-analysis\""))
077                {
078                    urlConnection.setRequestProperty(
079                        "SOAPAction",
080                        "urn:schemas-microsoft-com:xml-analysis:Discover");
081                } else if (request.contains(
082                    "<Execute xmlns=\"urn:schemas-microsoft-com:xml-analysis\""))
083                {
084                    urlConnection.setRequestProperty(
085                            "SOAPAction",
086                            "urn:schemas-microsoft-com:xml-analysis:Execute");
087                }
088    
089                // Encode credentials for basic authentication
090                if (url.getUserInfo() != null) {
091                    String encoding =
092                        Base64.encodeBytes(url.getUserInfo().getBytes(), 0);
093                    urlConnection.setRequestProperty(
094                        "Authorization", "Basic " + encoding);
095                }
096    
097                // Set correct cookies
098                this.useCookies(urlConnection);
099    
100                // Send data (i.e. POST). Use same encoding as specified in the
101                // header.
102                final String encoding = getEncodingCharsetName();
103                urlConnection.getOutputStream().write(request.getBytes(encoding));
104    
105                // Get the response, again assuming default encoding.
106                InputStream is = urlConnection.getInputStream();
107                final ByteArrayOutputStream baos = new ByteArrayOutputStream();
108                byte[] buf = new byte[1024];
109                int count;
110    
111                while ((count = is.read(buf)) > 0) {
112                    baos.write(buf, 0, count);
113                }
114    
115                // Save the returned cookies for later use
116                this.saveCookies(urlConnection);
117    
118                return baos.toByteArray();
119            // All exceptions should be trapped here.
120            // The response will only be available here anyways.
121            } catch (Exception e) {
122                // In order to prevent the JDK from keeping this connection
123                // in WAIT mode, we need to empty the error stream cache.
124                try {
125                    final int espCode =
126                        ((HttpURLConnection)urlConnection).getResponseCode();
127                    InputStream errorStream =
128                        ((HttpURLConnection)urlConnection).getErrorStream();
129                    final ByteArrayOutputStream baos =
130                        new ByteArrayOutputStream();
131                    final byte[] buf = new byte[1024];
132                    int count;
133                    if (errorStream != null) {
134                        while ((count = errorStream.read(buf)) > 0) {
135                            baos.write(buf, 0, count);
136                        }
137                        errorStream.close();
138                    }
139                    baos.close();
140                } catch (IOException ex) {
141                    // Well, we tried. No point notifying the user here.
142                }
143                throw new XmlaOlap4jProxyException(
144                    "This proxy encountered an exception while processing the "
145                    + "query.",
146                    e);
147            }
148        }
149    
150        @Override
151        public Future<byte[]> getResponseViaSubmit(
152            final URL url,
153            final String request)
154        {
155            return XmlaOlap4jDriver.getFuture(this, url, request);
156        }
157    
158        // implement XmlaOlap4jProxy
159        public String getEncodingCharsetName() {
160            return "UTF-8";
161        }
162    }
163    
164    // End XmlaOlap4jHttpProxy.java