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*/
020package org.olap4j.driver.xmla.proxy;
021
022import org.olap4j.driver.xmla.XmlaOlap4jDriver;
023import org.olap4j.driver.xmla.XmlaOlap4jServerInfos;
024import org.olap4j.impl.Base64;
025
026import java.io.*;
027import java.net.*;
028import 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 */
044public 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