001    /*
002    // $Id: XmlaOlap4jHttpProxy.java 482 2012-01-05 23:27:27Z jhyde $
003    //
004    // Licensed to Julian Hyde under one or more contributor license
005    // agreements. See the NOTICE file distributed with this work for
006    // additional information regarding copyright ownership.
007    //
008    // Julian Hyde licenses this file to you under the Apache License,
009    // Version 2.0 (the "License"); you may not use this file except in
010    // compliance with the License. You may obtain a copy of the License at:
011    //
012    // http://www.apache.org/licenses/LICENSE-2.0
013    //
014    // Unless required by applicable law or agreed to in writing, software
015    // distributed under the License is distributed on an "AS IS" BASIS,
016    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017    // See the License for the specific language governing permissions and
018    // limitations under the License.
019    */
020    package org.olap4j.driver.xmla.proxy;
021    
022    import org.olap4j.driver.xmla.XmlaOlap4jDriver;
023    import org.olap4j.driver.xmla.XmlaOlap4jServerInfos;
024    import org.olap4j.impl.Base64;
025    
026    import java.io.*;
027    import java.net.*;
028    import java.util.concurrent.Future;
029    
030    /**
031     * Extends the AbstractCachedProxy and serves as
032     * a production ready http communication class. Every SOAP request
033     * sends a POST call to the destination XMLA server and returns
034     * the response as a byte array, conforming to the Proxy interface.
035     *
036     * <p>It also takes advantage of the AbstractHttpProxy cookie
037     * managing facilities. All cookies received from the end point
038     * server will be sent back if they are not expired and they also
039     * conform to cookie domain rules.
040     *
041     * @author Luc Boudreau and Julian Hyde
042     * @version $Id: XmlaOlap4jHttpProxy.java 482 2012-01-05 23:27:27Z jhyde $
043     */
044    public class XmlaOlap4jHttpProxy extends XmlaOlap4jAbstractHttpProxy
045    {
046        private final XmlaOlap4jDriver driver;
047    
048        /**
049         * Creates a XmlaOlap4jHttpProxy.
050         *
051         * @param driver Driver
052         */
053        public XmlaOlap4jHttpProxy(
054            XmlaOlap4jDriver driver)
055        {
056            this.driver = driver;
057        }
058    
059        private static final String DISCOVER =
060            "<Discover xmlns=\"urn:schemas-microsoft-com:xml-analysis\"";
061    
062        private static final String EXECUTE =
063            "<Execute xmlns=\"urn:schemas-microsoft-com:xml-analysis\"";
064    
065        @Override
066        public byte[] getResponse(XmlaOlap4jServerInfos serverInfos, String request)
067            throws XmlaOlap4jProxyException
068        {
069            URLConnection urlConnection = null;
070            try {
071                URL url = serverInfos.getUrl();
072                // Open connection to manipulate the properties
073                urlConnection = url.openConnection();
074                urlConnection.setDoOutput(true);
075    
076                // Set headers
077                urlConnection.setRequestProperty(
078                    "content-type",
079                    "text/xml");
080                urlConnection.setRequestProperty(
081                    "User-Agent",
082                    "Olap4j("
083                        .concat(driver.getVersion())
084                        .concat(")"));
085                urlConnection.setRequestProperty(
086                    "Accept",
087                    "text/xml;q=1");
088                urlConnection.setRequestProperty(
089                    "Accept-Charset",
090                    getEncodingCharsetName()
091                        .concat(";q=1"));
092    
093                // Some servers expect a SOAPAction header.
094                // TODO There is bound to be a better way to do this.
095                if (request.contains(DISCOVER)) {
096                    urlConnection.setRequestProperty(
097                        "SOAPAction",
098                        "\"urn:schemas-microsoft-com:xml-analysis:Discover\"");
099                } else if (request.contains(EXECUTE)) {
100                    urlConnection.setRequestProperty(
101                        "SOAPAction",
102                        "\"urn:schemas-microsoft-com:xml-analysis:Execute\"");
103                }
104    
105                // Encode credentials for basic authentication
106                StringBuilder sb = new StringBuilder();
107                if (serverInfos.getUsername() != null
108                    && serverInfos.getPassword() != null)
109                {
110                    sb.append(serverInfos.getUsername());
111                    sb.append(":");
112                    sb.append(serverInfos.getPassword());
113                } else if (url.getUserInfo() != null) {
114                    sb.append(url.getUserInfo());
115                }
116                if (!sb.toString().equals("")) {
117                    String encoding =
118                        Base64.encodeBytes(
119                            sb.toString().getBytes(), 0);
120                    urlConnection.setRequestProperty(
121                        "Authorization", "Basic " + encoding);
122                }
123    
124                // Set correct cookies
125                this.useCookies(urlConnection);
126    
127                // Send data (i.e. POST). Use same encoding as specified in the
128                // header.
129                final String encoding = getEncodingCharsetName();
130                urlConnection.getOutputStream().write(request.getBytes(encoding));
131    
132                // Get the response, again assuming default encoding.
133                InputStream is = urlConnection.getInputStream();
134                final ByteArrayOutputStream baos = new ByteArrayOutputStream();
135                byte[] buf = new byte[1024];
136                int count;
137    
138                while ((count = is.read(buf)) > 0) {
139                    baos.write(buf, 0, count);
140                }
141    
142                // Save the returned cookies for later use
143                this.saveCookies(urlConnection);
144    
145                return baos.toByteArray();
146            // All exceptions should be trapped here.
147            // The response will only be available here anyways.
148            } catch (Exception e) {
149                // In order to prevent the JDK from keeping this connection
150                // in WAIT mode, we need to empty the error stream cache.
151                try {
152                    final int espCode =
153                        ((HttpURLConnection)urlConnection).getResponseCode();
154                    InputStream errorStream =
155                        ((HttpURLConnection)urlConnection).getErrorStream();
156                    final ByteArrayOutputStream baos =
157                        new ByteArrayOutputStream();
158                    final byte[] buf = new byte[1024];
159                    int count;
160                    if (errorStream != null) {
161                        while ((count = errorStream.read(buf)) > 0) {
162                            baos.write(buf, 0, count);
163                        }
164                        errorStream.close();
165                    }
166                    baos.close();
167                } catch (IOException ex) {
168                    // Well, we tried. No point notifying the user here.
169                }
170                throw new XmlaOlap4jProxyException(
171                    "This proxy encountered an exception while processing the "
172                    + "query.",
173                    e);
174            }
175        }
176    
177        @Override
178        public Future<byte[]> getResponseViaSubmit(
179            final XmlaOlap4jServerInfos serverInfos,
180            final String request)
181        {
182            return XmlaOlap4jDriver.getFuture(this, serverInfos, request);
183        }
184    
185        // implement XmlaOlap4jProxy
186        public String getEncodingCharsetName() {
187            return "UTF-8";
188        }
189    }
190    
191    // End XmlaOlap4jHttpProxy.java
192    
193    
194