001/*
002// This software is subject to the terms of the Eclipse Public License v1.0
003// Agreement, available at the following URL:
004// http://www.eclipse.org/legal/epl-v10.html.
005// You must accept the terms of that agreement to use this software.
006//
007// Copyright (C) 2005-2011 Pentaho
008// All Rights Reserved.
009*/
010package mondrian.xmla.impl;
011
012import mondrian.olap.Util;
013import mondrian.xmla.*;
014
015import org.apache.log4j.Logger;
016
017import org.w3c.dom.*;
018
019import java.util.*;
020
021import static org.olap4j.metadata.XmlaConstants.Method;
022
023/**
024 * Default implementation of {@link mondrian.xmla.XmlaRequest} by DOM API.
025 *
026 * @author Gang Chen
027 */
028public class DefaultXmlaRequest
029    implements XmlaRequest, XmlaConstants
030{
031    private static final Logger LOGGER =
032        Logger.getLogger(DefaultXmlaRequest.class);
033
034    private static final String MSG_INVALID_XMLA = "Invalid XML/A message";
035
036    /* common content */
037    private Method method;
038    private Map<String, String> properties;
039    private final String roleName;
040
041    /* EXECUTE content */
042    private String statement;
043    private boolean drillthrough;
044
045    /* DISCOVER contnet */
046    private String requestType;
047    private Map<String, Object> restrictions;
048
049    private final String username;
050    private final String password;
051    private final String sessionId;
052
053    public DefaultXmlaRequest(
054        final Element xmlaRoot,
055        final String roleName,
056        final String username,
057        final String password,
058        final String sessionId)
059        throws XmlaException
060    {
061        init(xmlaRoot);
062        this.roleName = roleName;
063        this.username = username;
064        this.password = password;
065        this.sessionId = sessionId;
066    }
067
068    public String getSessionId() {
069        return sessionId;
070    }
071
072    public String getUsername() {
073        return username;
074    }
075
076    public String getPassword() {
077        return password;
078    }
079
080    public Method getMethod() {
081        return method;
082    }
083
084    public Map<String, String> getProperties() {
085        return properties;
086    }
087
088    public Map<String, Object> getRestrictions() {
089        if (method != Method.DISCOVER) {
090            throw new IllegalStateException(
091                "Only METHOD_DISCOVER has restrictions");
092        }
093        return restrictions;
094    }
095
096    public String getStatement() {
097        if (method != Method.EXECUTE) {
098            throw new IllegalStateException(
099                "Only METHOD_EXECUTE has statement");
100        }
101        return statement;
102    }
103
104    public String getRoleName() {
105        return roleName;
106    }
107
108    public String getRequestType() {
109        if (method != Method.DISCOVER) {
110            throw new IllegalStateException(
111                "Only METHOD_DISCOVER has requestType");
112        }
113        return requestType;
114    }
115
116    public boolean isDrillThrough() {
117        if (method != Method.EXECUTE) {
118            throw new IllegalStateException(
119                "Only METHOD_EXECUTE determines drillthrough");
120        }
121        return drillthrough;
122    }
123
124
125    protected final void init(Element xmlaRoot) throws XmlaException {
126        if (NS_XMLA.equals(xmlaRoot.getNamespaceURI())) {
127            String lname = xmlaRoot.getLocalName();
128            if ("Discover".equals(lname)) {
129                method = Method.DISCOVER;
130                initDiscover(xmlaRoot);
131            } else if ("Execute".equals(lname)) {
132                method = Method.EXECUTE;
133                initExecute(xmlaRoot);
134            } else {
135                // Note that is code will never be reached because
136                // the error will be caught in
137                // DefaultXmlaServlet.handleSoapBody first
138                StringBuilder buf = new StringBuilder(100);
139                buf.append(MSG_INVALID_XMLA);
140                buf.append(": Bad method name \"");
141                buf.append(lname);
142                buf.append("\"");
143                throw new XmlaException(
144                    CLIENT_FAULT_FC,
145                    HSB_BAD_METHOD_CODE,
146                    HSB_BAD_METHOD_FAULT_FS,
147                    Util.newError(buf.toString()));
148            }
149        } else {
150            // Note that is code will never be reached because
151            // the error will be caught in
152            // DefaultXmlaServlet.handleSoapBody first
153            StringBuilder buf = new StringBuilder(100);
154            buf.append(MSG_INVALID_XMLA);
155            buf.append(": Bad namespace url \"");
156            buf.append(xmlaRoot.getNamespaceURI());
157            buf.append("\"");
158            throw new XmlaException(
159                CLIENT_FAULT_FC,
160                HSB_BAD_METHOD_NS_CODE,
161                HSB_BAD_METHOD_NS_FAULT_FS,
162                Util.newError(buf.toString()));
163        }
164    }
165
166    private void initDiscover(Element discoverRoot) throws XmlaException {
167        Element[] childElems =
168            XmlaUtil.filterChildElements(
169                discoverRoot,
170                NS_XMLA,
171                "RequestType");
172        if (childElems.length != 1) {
173            StringBuilder buf = new StringBuilder(100);
174            buf.append(MSG_INVALID_XMLA);
175            buf.append(": Wrong number of RequestType elements: ");
176            buf.append(childElems.length);
177            throw new XmlaException(
178                CLIENT_FAULT_FC,
179                HSB_BAD_REQUEST_TYPE_CODE,
180                HSB_BAD_REQUEST_TYPE_FAULT_FS,
181                Util.newError(buf.toString()));
182        }
183        requestType = XmlaUtil.textInElement(childElems[0]); // <RequestType>
184
185        childElems =
186            XmlaUtil.filterChildElements(
187                discoverRoot,
188                NS_XMLA,
189                "Properties");
190        if (childElems.length != 1) {
191            StringBuilder buf = new StringBuilder(100);
192            buf.append(MSG_INVALID_XMLA);
193            buf.append(": Wrong number of Properties elements: ");
194            buf.append(childElems.length);
195            throw new XmlaException(
196                CLIENT_FAULT_FC,
197                HSB_BAD_PROPERTIES_CODE,
198                HSB_BAD_PROPERTIES_FAULT_FS,
199                Util.newError(buf.toString()));
200        }
201        initProperties(childElems[0]); // <Properties><PropertyList>
202
203        childElems =
204            XmlaUtil.filterChildElements(
205                discoverRoot,
206                NS_XMLA,
207                "Restrictions");
208        if (childElems.length != 1) {
209            StringBuilder buf = new StringBuilder(100);
210            buf.append(MSG_INVALID_XMLA);
211            buf.append(": Wrong number of Restrictions elements: ");
212            buf.append(childElems.length);
213            throw new XmlaException(
214                CLIENT_FAULT_FC,
215                HSB_BAD_RESTRICTIONS_CODE,
216                HSB_BAD_RESTRICTIONS_FAULT_FS,
217                Util.newError(buf.toString()));
218        }
219        initRestrictions(childElems[0]); // <Restriciotns><RestrictionList>
220    }
221
222    private void initExecute(Element executeRoot) throws XmlaException {
223        Element[] childElems =
224            XmlaUtil.filterChildElements(
225                executeRoot,
226                NS_XMLA,
227                "Command");
228        if (childElems.length != 1) {
229            StringBuilder buf = new StringBuilder(100);
230            buf.append(MSG_INVALID_XMLA);
231            buf.append(": Wrong number of Command elements: ");
232            buf.append(childElems.length);
233            throw new XmlaException(
234                CLIENT_FAULT_FC,
235                HSB_BAD_COMMAND_CODE,
236                HSB_BAD_COMMAND_FAULT_FS,
237                Util.newError(buf.toString()));
238        }
239        initCommand(childElems[0]); // <Command><Statement>
240
241        childElems =
242            XmlaUtil.filterChildElements(
243                executeRoot,
244                NS_XMLA,
245                "Properties");
246        if (childElems.length != 1) {
247            StringBuilder buf = new StringBuilder(100);
248            buf.append(MSG_INVALID_XMLA);
249            buf.append(": Wrong number of Properties elements: ");
250            buf.append(childElems.length);
251            throw new XmlaException(
252                CLIENT_FAULT_FC,
253                HSB_BAD_PROPERTIES_CODE,
254                HSB_BAD_PROPERTIES_FAULT_FS,
255                Util.newError(buf.toString()));
256        }
257        initProperties(childElems[0]); // <Properties><PropertyList>
258    }
259
260    private void initRestrictions(Element restrictionsRoot)
261        throws XmlaException
262    {
263        Map<String, List<String>> restrictions =
264            new HashMap<String, List<String>>();
265        Element[] childElems =
266            XmlaUtil.filterChildElements(
267                restrictionsRoot,
268                NS_XMLA,
269                "RestrictionList");
270        if (childElems.length == 1) {
271            NodeList nlst = childElems[0].getChildNodes();
272            for (int i = 0, nlen = nlst.getLength(); i < nlen; i++) {
273                Node n = nlst.item(i);
274                if (n instanceof Element) {
275                    Element e = (Element) n;
276                    if (NS_XMLA.equals(e.getNamespaceURI())) {
277                        String key = e.getLocalName();
278                        String value = XmlaUtil.textInElement(e);
279
280                        List<String> values;
281                        if (restrictions.containsKey(key)) {
282                            values = restrictions.get(key);
283                        } else {
284                            values = new ArrayList<String>();
285                            restrictions.put(key, values);
286                        }
287
288                        if (LOGGER.isDebugEnabled()) {
289                            LOGGER.debug(
290                                "DefaultXmlaRequest.initRestrictions: "
291                                + " key=\""
292                                + key
293                                + "\", value=\""
294                                + value
295                                + "\"");
296                        }
297
298                        values.add(value);
299                    }
300                }
301            }
302        } else if (childElems.length > 1) {
303            StringBuilder buf = new StringBuilder(100);
304            buf.append(MSG_INVALID_XMLA);
305            buf.append(": Wrong number of RestrictionList elements: ");
306            buf.append(childElems.length);
307            throw new XmlaException(
308                CLIENT_FAULT_FC,
309                HSB_BAD_RESTRICTION_LIST_CODE,
310                HSB_BAD_RESTRICTION_LIST_FAULT_FS,
311                Util.newError(buf.toString()));
312        }
313
314        // If there is a Catalog property,
315        // we have to consider it a constraint as well.
316        String key =
317            org.olap4j.metadata.XmlaConstants
318                .Literal.CATALOG_NAME.name();
319
320        if (this.properties.containsKey(key)
321            && !restrictions.containsKey(key))
322        {
323            List<String> values;
324            values = new ArrayList<String>();
325            restrictions.put(this.properties.get(key), values);
326
327            if (LOGGER.isDebugEnabled()) {
328                LOGGER.debug(
329                    "DefaultXmlaRequest.initRestrictions: "
330                    + " key=\""
331                    + key
332                    + "\", value=\""
333                    + this.properties.get(key)
334                    + "\"");
335            }
336        }
337
338        this.restrictions = (Map) Collections.unmodifiableMap(restrictions);
339    }
340
341    private void initProperties(Element propertiesRoot) throws XmlaException {
342        Map<String, String> properties = new HashMap<String, String>();
343        Element[] childElems =
344            XmlaUtil.filterChildElements(
345                propertiesRoot,
346                NS_XMLA,
347                "PropertyList");
348        if (childElems.length == 1) {
349            NodeList nlst = childElems[0].getChildNodes();
350            for (int i = 0, nlen = nlst.getLength(); i < nlen; i++) {
351                Node n = nlst.item(i);
352                if (n instanceof Element) {
353                    Element e = (Element) n;
354                    if (NS_XMLA.equals(e.getNamespaceURI())) {
355                        String key = e.getLocalName();
356                        String value = XmlaUtil.textInElement(e);
357
358                        if (LOGGER.isDebugEnabled()) {
359                            LOGGER.debug(
360                                "DefaultXmlaRequest.initProperties: "
361                                + " key=\""
362                                + key
363                                + "\", value=\""
364                                + value
365                                + "\"");
366                        }
367
368                        properties.put(key, value);
369                    }
370                }
371            }
372        } else if (childElems.length > 1) {
373            StringBuilder buf = new StringBuilder(100);
374            buf.append(MSG_INVALID_XMLA);
375            buf.append(": Wrong number of PropertyList elements: ");
376            buf.append(childElems.length);
377            throw new XmlaException(
378                CLIENT_FAULT_FC,
379                HSB_BAD_PROPERTIES_LIST_CODE,
380                HSB_BAD_PROPERTIES_LIST_FAULT_FS,
381                Util.newError(buf.toString()));
382        } else {
383        }
384        this.properties = Collections.unmodifiableMap(properties);
385    }
386
387
388    private void initCommand(Element commandRoot) throws XmlaException {
389        Element[] childElems =
390            XmlaUtil.filterChildElements(
391                commandRoot,
392                NS_XMLA,
393                "Statement");
394        if (childElems.length != 1) {
395            StringBuilder buf = new StringBuilder(100);
396            buf.append(MSG_INVALID_XMLA);
397            buf.append(": Wrong number of Statement elements: ");
398            buf.append(childElems.length);
399            throw new XmlaException(
400                CLIENT_FAULT_FC,
401                HSB_BAD_STATEMENT_CODE,
402                HSB_BAD_STATEMENT_FAULT_FS,
403                Util.newError(buf.toString()));
404        }
405        statement = XmlaUtil.textInElement(childElems[0]).replaceAll("\\r", "");
406        drillthrough = statement.toUpperCase().indexOf("DRILLTHROUGH") != -1;
407    }
408}
409
410// End DefaultXmlaRequest.java