├── .gitignore ├── HISTORY.md ├── NOTICE ├── README.md ├── epl-v10.html ├── pom.xml ├── src └── main │ └── java │ ├── mondrian │ └── xmla │ │ ├── Enumeration.java │ │ ├── PropertyDefinition.java │ │ ├── Rowset.java │ │ ├── RowsetDefinition.java │ │ ├── SaxWriter.java │ │ ├── XmlaConstants.java │ │ ├── XmlaException.java │ │ ├── XmlaHandler.java │ │ ├── XmlaRequest.java │ │ ├── XmlaRequestCallback.java │ │ ├── XmlaResponse.java │ │ ├── XmlaServlet.java │ │ ├── XmlaUtil.java │ │ ├── impl │ │ ├── AuthenticatingXmlaRequestCallback.java │ │ ├── DefaultSaxWriter.java │ │ ├── DefaultXmlaRequest.java │ │ ├── DefaultXmlaResponse.java │ │ ├── DefaultXmlaServlet.java │ │ ├── JsonSaxWriter.java │ │ └── Olap4jXmlaServlet.java │ │ └── package.html │ └── org │ └── olap4j │ └── xmla │ └── server │ └── impl │ ├── ArrayStack.java │ ├── Composite.java │ ├── CompositeList.java │ ├── StringEscaper.java │ └── Util.java └── xmlaserver.iml /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | target 3 | .idea/ 4 | 5 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | 2 | # olap4j-xmlaserver 3 | 4 | ############################################################################# 5 | ## Version 1.2.0 6 | 7 | This is the first release of olap4j-xmlaserver. We have numbered it 1.2.0 so 8 | it aligns with the core olap4j releases. Future releases might not maintain 9 | this alignment. 10 | 11 | This release is also the first to be compatible with OSGI. For a list 12 | of the exported packages, please refer to the maven build file. 13 | 14 | ############################################################################# 15 | 16 | ## Commit history 17 | 18 | [4c9a99f](../../commit/4c9a99f50b5cbf1eb5959d0fe230cc25e93a7bfa) 19 | Fri, 10 Jan 2014 11:40:13 -0500 - __(Kurtis Walker)__ 20 | Mondrian needs some additional packages exported in order to fully resolve in OSGI. This changes makes the exported xmlaserver packages explicit. 21 | 22 | [33fa613](../../commit/33fa6135672db30de665d568521c4faf962f3760) 23 | Thu, 9 Jan 2014 10:49:05 -0500 - __(Luc Boudreau)__ 24 | Update pom.xml 25 | 26 | [2939112](../../commit/29391127e752ce457f766f9009f23bca1e411bf0) 27 | Thu, 5 Dec 2013 09:51:32 -0500 - __(Nicholas Baker)__ 28 | Changes to POM to make the artifact an OSGI bundle. Upped version to 0.0.2-SNAPSHOT due to the change. 29 | 30 | [8f0f55f](../../commit/8f0f55fa2dbb68df2ea8795a40d34ba7d830ec75) 31 | Tue, 26 Nov 2013 10:39:26 -0500 - __(Kurtis Walker)__ 32 | ANALYZER-2188 - adding new methods to XmlaExtra 33 | 34 | [dad5a17](../../commit/dad5a17cb38d2fa713e2be89c1748f4b6bf37813) 35 | Fri, 26 Jul 2013 10:12:07 -0700 - __(Julian Hyde)__ 36 | Remove Intellij files from git. 37 | 38 | [d7dfa1f](../../commit/d7dfa1fce7c1791e628d7dafe22329205aaf2ef5) 39 | Wed, 19 Jun 2013 09:18:32 -0400 - __(mkambol)__ 40 | [MONDRIAN-1581] Fixing datatypes which were incorrectly marshalled in XMLA responses: Boolean, Short, Byte, Float (were formerly treated as xsd:string, xsd:int, xsd:int, and xsd:double, respectively) merged from 534768 41 | 42 | [a0e99cd](../../commit/a0e99cd8df51d7650213af3de0c2c46ca1f7edde) 43 | Thu, 21 Feb 2013 10:34:15 -0500 - __(Luc Boudreau)__ 44 | Merge of commit 24cf3d0e2a57b88bb9550e4bccd8c8bb34c6d9db form Mondrian's master. 45 | 46 | [34e6697](../../commit/34e66971c15a947ec99e8a56c6273d0c2de1648c) 47 | Thu, 21 Feb 2013 10:20:56 -0500 - __(Luc Boudreau)__ 48 | Merge of change a5c552f74ed8068a7b40fa6f2b3e9a995d94e100 from Mondrian's master. 49 | 50 | [2f444e2](../../commit/2f444e26e905804fd13b5d53636d168b6df925ed) 51 | Thu, 21 Feb 2013 09:39:15 -0500 - __(Luc Boudreau)__ 52 | Merge of changes 8fa75e276f638b1156e0fa1c447fc49006112f88 ba93ad762db859d04d6aa2411e4587280ca06249 160dadad7af7dee209fa85ba8cbd9106fa60c799 from Mondrian's master. 53 | 54 | [d87b6ad](../../commit/d87b6ad54c3ba39448ec3bc2a31a249d75d9acf5) 55 | Wed, 2 Jan 2013 11:59:47 -0500 - __(Luc Boudreau)__ 56 | Adds getSchemaId() to XmlaExtra. 57 | 58 | [1cd132a](../../commit/1cd132ad3a5f225412ba8ed8cb9c4409c59653a4) 59 | Mon, 17 Dec 2012 14:31:27 -0500 - __(Luc Boudreau)__ 60 | Idem. 61 | 62 | [04296ff](../../commit/04296ff62f7fee2531b33d342fe2a1c8cd9b118d) 63 | Mon, 17 Dec 2012 13:30:45 -0500 - __(buildguy)__ 64 | Update pom.xml 65 | 66 | [c620eab](../../commit/c620eab7bb69916df36944963c0a94169b497eb4) 67 | Mon, 17 Dec 2012 12:56:50 -0500 - __(Luc Boudreau)__ 68 | Idem. 69 | 70 | [a6f4059](../../commit/a6f405959235692e064210b1333a7e4abe1ec038) 71 | Mon, 17 Dec 2012 12:54:58 -0500 - __(Luc Boudreau)__ 72 | Idem. 73 | 74 | [773507d](../../commit/773507d0affdba351fc50e37c91479e7f3fe9633) 75 | Mon, 17 Dec 2012 12:43:44 -0500 - __(Luc Boudreau)__ 76 | An attempt to fix CI. 77 | 78 | [fae048f](../../commit/fae048f973b8aa51d4c9abee9c707042fedf33df) 79 | Mon, 17 Dec 2012 11:29:18 -0500 - __(Luc Boudreau)__ 80 | Small change to the pom to get deployment working on CI. 81 | 82 | [ffd4216](../../commit/ffd4216b41e1e0009df0ade94d2125bf984919f4) 83 | Mon, 17 Dec 2012 10:54:36 -0500 - __(Luc Boudreau)__ 84 | Upgrades to latest olap4j. 85 | 86 | [38d065a](../../commit/38d065af43a45335758d772072066cafea9159d7) 87 | Fri, 7 Sep 2012 15:48:26 -0700 - __(Julian Hyde)__ 88 | DefaultSaxWriter now writes to any Appendable, rather than a PrintWriter. If the Appendable is a StringBuilder, this should be more efficient. 89 | 90 | [ed28a49](../../commit/ed28a4973c69fe0991c63b9e4a55b0faf87d3cb0) 91 | Fri, 7 Sep 2012 13:25:31 -0700 - __(Julian Hyde)__ 92 | Fix name of project in pom; get XmlaExtra from ConnectionFactory, rather than by using "unwrap" on the OlapConnection. 93 | 94 | [de2fed9](../../commit/de2fed93ee14d5ae4a83a34b12bda513ff6d2e63) 95 | Fri, 31 Aug 2012 21:07:17 -0700 - __(Julian Hyde)__ 96 | Slim down dependencies. 97 | 98 | [3602acf](../../commit/3602acf4133dabf48a053fdb7b5186eaee7a97de) 99 | Fri, 31 Aug 2012 20:46:51 -0700 - __(Julian Hyde)__ 100 | Remove mondrian dependencies. (Extend XmlaExtra interface, remove stub classes.) 101 | 102 | [7e53b09](../../commit/7e53b09f6820d86ad38d552b71316fd05df0da61) 103 | Fri, 31 Aug 2012 17:03:32 -0700 - __(Julian Hyde)__ 104 | Initial version, copying mondrian.xmla package and stub versions of mondrian classes it depends upon. 105 | 106 | [3600730](../../commit/3600730867ac523c8cc0cbcc346b711ff5d1c42a) 107 | Fri, 31 Aug 2012 15:20:27 -0700 - __(Julian Hyde)__ 108 | Initial commit 109 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | olap4j-xmlaserver 2 | 3 | =============================================================================== 4 | This product includes software from the Mondrian project. 5 | 6 | Copyright (C) 2001-2005 Julian Hyde 7 | Copyright (C) 2005-2012 Pentaho and others 8 | 9 | =============================================================================== 10 | The StringEscaper class comes from the eigenbase-xom project. 11 | 12 | Copyright (C) 2005 Dynamo BI Corporation 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | olap4j-xmlaserver 2 | ================= 3 | 4 | XML for Analysis (XMLA) server based upon an olap4j connection -------------------------------------------------------------------------------- /epl-v10.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 |Eclipse Public License - v 1.0
31 | 32 |THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE 33 | PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR 34 | DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS 35 | AGREEMENT.
36 | 37 |1. DEFINITIONS
38 | 39 |"Contribution" means:
40 | 41 |a) in the case of the initial Contributor, the initial 42 | code and documentation distributed under this Agreement, and
43 |b) in the case of each subsequent Contributor:
44 |i) changes to the Program, and
45 |ii) additions to the Program;
46 |where such changes and/or additions to the Program 47 | originate from and are distributed by that particular Contributor. A 48 | Contribution 'originates' from a Contributor if it was added to the 49 | Program by such Contributor itself or anyone acting on such 50 | Contributor's behalf. Contributions do not include additions to the 51 | Program which: (i) are separate modules of software distributed in 52 | conjunction with the Program under their own license agreement, and (ii) 53 | are not derivative works of the Program.
54 | 55 |"Contributor" means any person or entity that distributes 56 | the Program.
57 | 58 |"Licensed Patents" mean patent claims licensable by a 59 | Contributor which are necessarily infringed by the use or sale of its 60 | Contribution alone or when combined with the Program.
61 | 62 |"Program" means the Contributions distributed in accordance 63 | with this Agreement.
64 | 65 |"Recipient" means anyone who receives the Program under 66 | this Agreement, including all Contributors.
67 | 68 |2. GRANT OF RIGHTS
69 | 70 |a) Subject to the terms of this Agreement, each 71 | Contributor hereby grants Recipient a non-exclusive, worldwide, 72 | royalty-free copyright license to reproduce, prepare derivative works 73 | of, publicly display, publicly perform, distribute and sublicense the 74 | Contribution of such Contributor, if any, and such derivative works, in 75 | source code and object code form.
76 | 77 |b) Subject to the terms of this Agreement, each 78 | Contributor hereby grants Recipient a non-exclusive, worldwide, 79 | royalty-free patent license under Licensed Patents to make, use, sell, 80 | offer to sell, import and otherwise transfer the Contribution of such 81 | Contributor, if any, in source code and object code form. This patent 82 | license shall apply to the combination of the Contribution and the 83 | Program if, at the time the Contribution is added by the Contributor, 84 | such addition of the Contribution causes such combination to be covered 85 | by the Licensed Patents. The patent license shall not apply to any other 86 | combinations which include the Contribution. No hardware per se is 87 | licensed hereunder.
88 | 89 |c) Recipient understands that although each Contributor 90 | grants the licenses to its Contributions set forth herein, no assurances 91 | are provided by any Contributor that the Program does not infringe the 92 | patent or other intellectual property rights of any other entity. Each 93 | Contributor disclaims any liability to Recipient for claims brought by 94 | any other entity based on infringement of intellectual property rights 95 | or otherwise. As a condition to exercising the rights and licenses 96 | granted hereunder, each Recipient hereby assumes sole responsibility to 97 | secure any other intellectual property rights needed, if any. For 98 | example, if a third party patent license is required to allow Recipient 99 | to distribute the Program, it is Recipient's responsibility to acquire 100 | that license before distributing the Program.
101 | 102 |d) Each Contributor represents that to its knowledge it 103 | has sufficient copyright rights in its Contribution, if any, to grant 104 | the copyright license set forth in this Agreement.
105 | 106 |3. REQUIREMENTS
107 | 108 |A Contributor may choose to distribute the Program in object code 109 | form under its own license agreement, provided that:
110 | 111 |a) it complies with the terms and conditions of this 112 | Agreement; and
113 | 114 |b) its license agreement:
115 | 116 |i) effectively disclaims on behalf of all Contributors 117 | all warranties and conditions, express and implied, including warranties 118 | or conditions of title and non-infringement, and implied warranties or 119 | conditions of merchantability and fitness for a particular purpose;
120 | 121 |ii) effectively excludes on behalf of all Contributors 122 | all liability for damages, including direct, indirect, special, 123 | incidental and consequential damages, such as lost profits;
124 | 125 |iii) states that any provisions which differ from this 126 | Agreement are offered by that Contributor alone and not by any other 127 | party; and
128 | 129 |iv) states that source code for the Program is available 130 | from such Contributor, and informs licensees how to obtain it in a 131 | reasonable manner on or through a medium customarily used for software 132 | exchange.
133 | 134 |When the Program is made available in source code form:
135 | 136 |a) it must be made available under this Agreement; and
137 | 138 |b) a copy of this Agreement must be included with each 139 | copy of the Program.
140 | 141 |Contributors may not remove or alter any copyright notices contained 142 | within the Program.
143 | 144 |Each Contributor must identify itself as the originator of its 145 | Contribution, if any, in a manner that reasonably allows subsequent 146 | Recipients to identify the originator of the Contribution.
147 | 148 |4. COMMERCIAL DISTRIBUTION
149 | 150 |Commercial distributors of software may accept certain 151 | responsibilities with respect to end users, business partners and the 152 | like. While this license is intended to facilitate the commercial use of 153 | the Program, the Contributor who includes the Program in a commercial 154 | product offering should do so in a manner which does not create 155 | potential liability for other Contributors. Therefore, if a Contributor 156 | includes the Program in a commercial product offering, such Contributor 157 | ("Commercial Contributor") hereby agrees to defend and 158 | indemnify every other Contributor ("Indemnified Contributor") 159 | against any losses, damages and costs (collectively "Losses") 160 | arising from claims, lawsuits and other legal actions brought by a third 161 | party against the Indemnified Contributor to the extent caused by the 162 | acts or omissions of such Commercial Contributor in connection with its 163 | distribution of the Program in a commercial product offering. The 164 | obligations in this section do not apply to any claims or Losses 165 | relating to any actual or alleged intellectual property infringement. In 166 | order to qualify, an Indemnified Contributor must: a) promptly notify 167 | the Commercial Contributor in writing of such claim, and b) allow the 168 | Commercial Contributor to control, and cooperate with the Commercial 169 | Contributor in, the defense and any related settlement negotiations. The 170 | Indemnified Contributor may participate in any such claim at its own 171 | expense.
172 | 173 |For example, a Contributor might include the Program in a commercial 174 | product offering, Product X. That Contributor is then a Commercial 175 | Contributor. If that Commercial Contributor then makes performance 176 | claims, or offers warranties related to Product X, those performance 177 | claims and warranties are such Commercial Contributor's responsibility 178 | alone. Under this section, the Commercial Contributor would have to 179 | defend claims against the other Contributors related to those 180 | performance claims and warranties, and if a court requires any other 181 | Contributor to pay any damages as a result, the Commercial Contributor 182 | must pay those damages.
183 | 184 |5. NO WARRANTY
185 | 186 |EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS 187 | PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 188 | OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, 189 | ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY 190 | OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely 191 | responsible for determining the appropriateness of using and 192 | distributing the Program and assumes all risks associated with its 193 | exercise of rights under this Agreement , including but not limited to 194 | the risks and costs of program errors, compliance with applicable laws, 195 | damage to or loss of data, programs or equipment, and unavailability or 196 | interruption of operations.
197 | 198 |6. DISCLAIMER OF LIABILITY
199 | 200 |EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT 201 | NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, 202 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING 203 | WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF 204 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 205 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR 206 | DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED 207 | HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
208 | 209 |7. GENERAL
210 | 211 |If any provision of this Agreement is invalid or unenforceable under 212 | applicable law, it shall not affect the validity or enforceability of 213 | the remainder of the terms of this Agreement, and without further action 214 | by the parties hereto, such provision shall be reformed to the minimum 215 | extent necessary to make such provision valid and enforceable.
216 | 217 |If Recipient institutes patent litigation against any entity 218 | (including a cross-claim or counterclaim in a lawsuit) alleging that the 219 | Program itself (excluding combinations of the Program with other 220 | software or hardware) infringes such Recipient's patent(s), then such 221 | Recipient's rights granted under Section 2(b) shall terminate as of the 222 | date such litigation is filed.
223 | 224 |All Recipient's rights under this Agreement shall terminate if it 225 | fails to comply with any of the material terms or conditions of this 226 | Agreement and does not cure such failure in a reasonable period of time 227 | after becoming aware of such noncompliance. If all Recipient's rights 228 | under this Agreement terminate, Recipient agrees to cease use and 229 | distribution of the Program as soon as reasonably practicable. However, 230 | Recipient's obligations under this Agreement and any licenses granted by 231 | Recipient relating to the Program shall continue and survive.
232 | 233 |Everyone is permitted to copy and distribute copies of this 234 | Agreement, but in order to avoid inconsistency the Agreement is 235 | copyrighted and may only be modified in the following manner. The 236 | Agreement Steward reserves the right to publish new versions (including 237 | revisions) of this Agreement from time to time. No one other than the 238 | Agreement Steward has the right to modify this Agreement. The Eclipse 239 | Foundation is the initial Agreement Steward. The Eclipse Foundation may 240 | assign the responsibility to serve as the Agreement Steward to a 241 | suitable separate entity. Each new version of the Agreement will be 242 | given a distinguishing version number. The Program (including 243 | Contributions) may always be distributed subject to the version of the 244 | Agreement under which it was received. In addition, after a new version 245 | of the Agreement is published, Contributor may elect to distribute the 246 | Program (including its Contributions) under the new version. Except as 247 | expressly stated in Sections 2(a) and 2(b) above, Recipient receives no 248 | rights or licenses to the intellectual property of any Contributor under 249 | this Agreement, whether expressly, by implication, estoppel or 250 | otherwise. All rights in the Program not expressly granted under this 251 | Agreement are reserved.
252 | 253 |This Agreement is governed by the laws of the State of New York and 254 | the intellectual property laws of the United States of America. No party 255 | to this Agreement will bring a legal action under this Agreement more 256 | than one year after the cause of action arose. Each party waives its 257 | rights to a jury trial in any resulting litigation.
258 | 259 | 260 | 261 | 262 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 |SaxWriter
is similar to a SAX {@link org.xml.sax.ContentHandler}
15 | * which, perversely, converts its events into an output document.
16 | *
17 | * @author jhyde
18 | * @author Gang Chen
19 | * @since 27 April, 2003
20 | */
21 | public interface SaxWriter {
22 |
23 | public void startDocument();
24 |
25 | public void endDocument();
26 |
27 | public void startElement(String name);
28 |
29 | public void startElement(String name, Object... attrs);
30 |
31 | public void endElement();
32 |
33 | public void element(String name, Object... attrs);
34 |
35 | public void characters(String data);
36 |
37 | /**
38 | * Informs the writer that a sequence of elements of the same name is
39 | * starting.
40 | *
41 | * For XML, is equivalent to {@code startElement(name)}. 42 | * 43 | *
For JSON, initiates the array construct: 44 | * 45 | *
"name" : [
46 | * { ... },
47 | * { ... }
48 | * ]
49 | *
50 | * @param name Element name
51 | * @param subName Child element name
52 | */
53 | public void startSequence(String name, String subName);
54 |
55 | /**
56 | * Informs the writer that a sequence of elements of the same name has
57 | * ended.
58 | */
59 | public void endSequence();
60 |
61 | /**
62 | * Generates a text-only element, {@code <name>data</name>}.
63 | *
64 | * For XML, this is equivalent to 65 | * 66 | *
startElement(name);
67 | * characters(data);
68 | * endElement();
69 | *
70 | * but for JSON, generates {@code "name": "data"}.
71 | *
72 | * @param name Name of element
73 | * @param data Text content of element
74 | */
75 | public void textElement(String name, Object data);
76 |
77 | public void completeBeforeElement(String tagName);
78 |
79 | /**
80 | * Sends a piece of text verbatim through the writer. It must be a piece
81 | * of well-formed XML.
82 | */
83 | public void verbatim(String text);
84 |
85 | /**
86 | * Flushes any unwritten output.
87 | */
88 | public void flush();
89 | }
90 |
91 | // End SaxWriter.java
92 |
--------------------------------------------------------------------------------
/src/main/java/mondrian/xmla/XmlaConstants.java:
--------------------------------------------------------------------------------
1 | /*
2 | // This software is subject to the terms of the Eclipse Public License v1.0
3 | // Agreement, available at the following URL:
4 | // http://www.eclipse.org/legal/epl-v10.html.
5 | // You must accept the terms of that agreement to use this software.
6 | //
7 | // Copyright (C) 2005-2011 Pentaho
8 | // All Rights Reserved.
9 | */
10 | package mondrian.xmla;
11 |
12 | /**
13 | * Constants for XML/A.
14 | *
15 | * @author Gang Chen
16 | */
17 | public interface XmlaConstants {
18 |
19 | /* SOAP 1.1 */
20 | public static final String NS_SOAP_ENV_1_1 =
21 | "http://schemas.xmlsoap.org/soap/envelope/";
22 | public static final String NS_SOAP_ENC_1_1 =
23 | "http://schemas.xmlsoap.org/soap/encoding/";
24 |
25 | /* SOAP 1.2 - currently not supported */
26 | public static final String NS_SOAP_ENV_1_2 =
27 | "http://www.w3.org/2003/05/soap-envelope";
28 | public static final String NS_SOAP_ENC_1_2 =
29 | "http://www.w3.org/2003/05/soap-encoding";
30 |
31 | /* Namespaces for XML */
32 | public static final String NS_XSD = "http://www.w3.org/2001/XMLSchema";
33 | public static final String NS_XSI = "http://www.w3.org/2001/XMLSchema-instance";
34 |
35 | /* Namespaces for XML/A */
36 | public static final String NS_XMLA =
37 | "urn:schemas-microsoft-com:xml-analysis";
38 | public static final String NS_XMLA_MDDATASET =
39 | "urn:schemas-microsoft-com:xml-analysis:mddataset";
40 | public static final String NS_XMLA_EMPTY =
41 | "urn:schemas-microsoft-com:xml-analysis:empty";
42 | public static final String NS_XMLA_ROWSET =
43 | "urn:schemas-microsoft-com:xml-analysis:rowset";
44 | public static final String NS_SQL = "urn:schemas-microsoft-com:xml-sql";
45 | public static final String NS_XMLA_EX =
46 | "urn:schemas-microsoft-com:xml-analysis:exception";
47 |
48 | public static final String NS_SOAP_SECEXT =
49 | "http://schemas.xmlsoap.org/ws/2002/04/secext";
50 |
51 | public static final String SOAP_PREFIX = "SOAP-ENV";
52 |
53 | /**
54 | * Soap Header mustUnderstand attribute name.
55 | */
56 | public static final String SOAP_MUST_UNDERSTAND_ATTR = "mustUnderstand";
57 |
58 | /**
59 | * Soap XMLA Header elements and attribute names.
60 | */
61 | public static final String XMLA_BEGIN_SESSION = "BeginSession";
62 | public static final String XMLA_SESSION = "Session";
63 | public static final String XMLA_END_SESSION = "EndSession";
64 | public static final String XMLA_SESSION_ID = "SessionId";
65 | public static final String XMLA_SECURITY = "Security";
66 |
67 | // Names of context keys known by both callbacks and Mondrian code.
68 |
69 | // context key for role name storage
70 | public static final String CONTEXT_ROLE_NAME = "role_name";
71 | // context key for language (SOAP or JSON)
72 | public static final String CONTEXT_MIME_TYPE = "language";
73 | // context key for session id storage
74 | public static final String CONTEXT_XMLA_SESSION_ID = "session_id";
75 |
76 | // Username and password tokens
77 | public static final String CONTEXT_XMLA_USERNAME = "username";
78 | public static final String CONTEXT_XMLA_PASSWORD = "password";
79 |
80 | // context key for session state storage
81 | public static final String CONTEXT_XMLA_SESSION_STATE = "SessionState";
82 | public static final String CONTEXT_XMLA_SESSION_STATE_BEGIN =
83 | "SessionStateBegin";
84 | public static final String CONTEXT_XMLA_SESSION_STATE_WITHIN =
85 | "SessionStateWithin";
86 | public static final String CONTEXT_XMLA_SESSION_STATE_END =
87 | "SessionStateEnd";
88 |
89 | /*************************************************************************
90 | *
91 | * The following are XMLA exception fault codes used as faultcode entries
92 | * in the SOAP Fault element.
93 | *
94 | * If Mondrian Exceptions actually used the "id" attributes found in the
95 | * MondrianResource.xml file, then those would be used as the SOAP Fault
96 | * detail error code values, but, alas they do not show up as part of
97 | * the generated Exception Java code so, here we simply duplicate
98 | * the fault code entry.
99 | *
100 | * Currently, SOAP 1.2 errors are not supported.
101 | *
102 | *************************************************************************/
103 |
104 | /**
105 | * This is the namespace used to qualify the faultcode identifier.
106 | */
107 | public static final String MONDRIAN_NAMESPACE = "http://mondrian.sourceforge.net";
108 | public static final String FAULT_NS_PREFIX = "XA";
109 |
110 | public static final String FAULT_ACTOR = "Mondrian";
111 |
112 | // soap 1.1 default faultcodes
113 | public static final String VERSION_MISSMATCH_FAULT_FC = "VersionMismatch";
114 | public static final String MUST_UNDERSTAND_FAULT_FC = "MustUnderstand";
115 | public static final String CLIENT_FAULT_FC = "Client";
116 | public static final String SERVER_FAULT_FC = "Server";
117 |
118 | //If the value is a list of strings, the restriction passes if the
37 | * column has one of the values.
38 | */
39 | Map Not necessarily the same as the HTTP session: the SOAP request
78 | * contains its own session information. The session id is used to retrieve existing olap connections. And
81 | * username / password only need to be passed on the first request in a
82 | * session.
75 | * Note that it is upto the XMLA client to determine whether or not
76 | * there is an Expect header entry (ADOMD.NET seems to like to do this).
77 | *
78 | * @return true if XmlaServlet handling is to continue and false if
79 | * there was an Expect header "100-continue".
80 | */
81 | boolean processHttpHeader(
82 | HttpServletRequest request,
83 | HttpServletResponse response,
84 | Map XML element name:
51 | *
52 | * Char ::= #x9 | #xA | #xD | [#x20-#xD7FF]
53 | * | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
54 | * S ::= (#x20 | #x9 | #xD | #xA)+
55 | * NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar
56 | * | Extender
57 | * Name ::= (Letter | '_' | ':') (NameChar)*
58 | * Names ::= Name (#x20 Name)*
59 | * Nmtoken ::= (NameChar)+
60 | * Nmtokens ::= Nmtoken (#x20 Nmtoken)*
61 | *
62 | */
63 | private static final String[] CHAR_TABLE = new String[256];
64 | private static final Pattern LOWERCASE_PATTERN =
65 | Pattern.compile(".*[a-z].*");
66 |
67 | static {
68 | initCharTable(" \t\r\n(){}[]+/*%!,?");
69 | }
70 |
71 | private static void initCharTable(String charStr) {
72 | char[] chars = charStr.toCharArray();
73 | for (char c : chars) {
74 | CHAR_TABLE[c] = encodeChar(c);
75 | }
76 | }
77 |
78 | private static String encodeChar(char c) {
79 | StringBuilder buf = new StringBuilder();
80 | buf.append("_x");
81 | String str = Integer.toHexString(c);
82 | for (int i = 4 - str.length(); i > 0; i--) {
83 | buf.append("0");
84 | }
85 | return buf.append(str).append("_").toString();
86 | }
87 |
88 | /**
89 | * Encodes an XML element name.
90 | *
91 | * This function is mainly for encode element names in result of Drill
92 | * Through execute, because its element names come from database, we cannot
93 | * make sure they are valid XML contents.
94 | *
95 | * Quoth the XML/A specification, version
96 | * 1.1:
97 | * If it contains a decimal point, then
253 | * strip off trailing '0's. After stripping off
254 | * the '0's, if there is nothing right of the
255 | * decimal point, then strip off decimal point.
256 | *
257 | * @param numericStr Numeric string
258 | * @return Normalized string
259 | */
260 | public static String normalizeNumericString(String numericStr) {
261 | int index = numericStr.indexOf('.');
262 | if (index > 0) {
263 | // If it uses exponential notation, 1.0E4, then it could
264 | // have a trailing '0' that should not be stripped of,
265 | // e.g., 1.0E10. This would be rather bad.
266 | if (numericStr.indexOf('e') != -1) {
267 | return numericStr;
268 | } else if (numericStr.indexOf('E') != -1) {
269 | return numericStr;
270 | }
271 |
272 | boolean found = false;
273 | int p = numericStr.length();
274 | char c = numericStr.charAt(p - 1);
275 | while (c == '0') {
276 | found = true;
277 | p--;
278 | c = numericStr.charAt(p - 1);
279 | }
280 | if (c == '.') {
281 | p--;
282 | }
283 | if (found) {
284 | return numericStr.substring(0, p);
285 | }
286 | }
287 | return numericStr;
288 | }
289 |
290 | /**
291 | * Returns a set of column headings and rows for a given metadata request.
292 | *
293 | * The header can contain a list of mime types and optional qualities,
474 | * for example "text/html,application/xhtml+xml,application/xml;q=0.9"
475 | *
476 | * @param accept Accept header
477 | * @return Mime type, or null if none is acceptable
478 | */
479 | public static Enumeration.ResponseMimeType chooseResponseMimeType(
480 | String accept)
481 | {
482 | for (String s : accept.split(",")) {
483 | s = s.trim();
484 | final int semicolon = s.indexOf(";");
485 | if (semicolon >= 0) {
486 | s = s.substring(0, semicolon);
487 | }
488 | Enumeration.ResponseMimeType mimeType =
489 | Enumeration.ResponseMimeType.MAP.get(s);
490 | if (mimeType != null) {
491 | return mimeType;
492 | }
493 | }
494 | return null;
495 | }
496 |
497 | /**
498 | * Returns whether an XMLA request should return invisible members.
499 | *
500 | * According to the XMLA spec, it should not. But we allow the client to
501 | * specify different behavior. In particular, the olap4j driver for XMLA
502 | * may need to access invisible members.
503 | *
504 | * Returns true if the EmitInvisibleMembers property is specified and
505 | * equal to "true".
506 | *
507 | * @param request XMLA request
508 | * @return Whether to return invisible members
509 | */
510 | public static boolean shouldEmitInvisibleMembers(XmlaRequest request) {
511 | final String value =
512 | request.getProperties().get(
513 | PropertyDefinition.EmitInvisibleMembers.name());
514 | return Boolean.parseBoolean(value);
515 | }
516 |
517 | /**
518 | * Result of a metadata query.
519 | */
520 | public static class MetadataRowset {
521 | public final List Once implemented, you only need to register your class using the XMLA
28 | * servlet config, within your web.xml descriptor.
29 | *
30 | * @author LBoudreau
31 | */
32 | public abstract class AuthenticatingXmlaRequestCallback
33 | implements XmlaRequestCallback
34 | {
35 | public String generateSessionId(Map Should there be any problems with the credentials, subclasses
82 | * can invoke {@link #throwAuthenticationException(String)} to throw
83 | * an authentication exception back to the client.
84 | * @param username Username used for authentication, as specified
85 | * in the SOAP security header. Might be null.
86 | * @param password Password used for authentication, as specified
87 | * in the SOAP security header. Might be null.
88 | * @param sessionID A unique identifier for this client session.
89 | * Session IDs should remain the same between different queries from
90 | * a same client, although some clients do not implement the XMLA
91 | * Session header properly, resulting in a new session ID for each
92 | * request.
93 | * @return A comma separated list of roles associated to this user,
94 | * or Examples: {@code "a \"quoted\" string"} (string),
203 | * {@code 12} (int), {@code 12.345} (float), {@code null} (null value).
204 | *
205 | * @param value Value
206 | */
207 | private void value(Object value) {
208 | if (value instanceof String) {
209 | String s = (String) value;
210 | Util.quoteForMdx(buf, s);
211 | } else {
212 | buf.append(value);
213 | }
214 | }
215 |
216 | private void comma() {
217 | if (stack.peek().ordinal++ > 0) {
218 | buf.append(",\n");
219 | } else {
220 | buf.append("\n");
221 | }
222 | }
223 |
224 | private static class Frame {
225 | final String name;
226 | int ordinal;
227 |
228 | Frame(String name) {
229 | this.name = name;
230 | }
231 | }
232 | }
233 |
234 | // End JsonSaxWriter.java
235 |
--------------------------------------------------------------------------------
/src/main/java/mondrian/xmla/impl/Olap4jXmlaServlet.java:
--------------------------------------------------------------------------------
1 | /*
2 | // This software is subject to the terms of the Eclipse Public License v1.0
3 | // Agreement, available at the following URL:
4 | // http://www.eclipse.org/legal/epl-v10.html.
5 | // You must accept the terms of that agreement to use this software.
6 | //
7 | // Copyright (C) 2011-2012 Pentaho
8 | // All Rights Reserved.
9 | */
10 | package mondrian.xmla.impl;
11 |
12 | import mondrian.xmla.XmlaHandler;
13 | import mondrian.xmla.XmlaRequest;
14 |
15 | import org.olap4j.xmla.server.impl.Util;
16 |
17 | import org.apache.commons.dbcp.BasicDataSource;
18 | import org.apache.commons.dbcp.DelegatingConnection;
19 | import org.apache.log4j.Logger;
20 |
21 | import org.olap4j.OlapConnection;
22 | import org.olap4j.OlapWrapper;
23 |
24 | import java.lang.reflect.*;
25 | import java.sql.Connection;
26 | import java.sql.SQLException;
27 | import java.util.*;
28 | import javax.servlet.ServletConfig;
29 | import javax.servlet.ServletException;
30 |
31 | /**
32 | * XMLA servlet that gets its connections from an olap4j data source.
33 | *
34 | * @author Julian Hyde
35 | * @author Michele Rossi
36 | */
37 | public class Olap4jXmlaServlet extends DefaultXmlaServlet {
38 | private static final Logger LOGGER =
39 | Logger.getLogger(Olap4jXmlaServlet.class);
40 |
41 | private static final String OLAP_DRIVER_CLASS_NAME_PARAM =
42 | "OlapDriverClassName";
43 |
44 | private static final String OLAP_DRIVER_CONNECTION_STRING_PARAM =
45 | "OlapDriverConnectionString";
46 |
47 | private static final String OLAP_DRIVER_CONNECTION_PROPERTIES_PREFIX =
48 | "OlapDriverConnectionProperty.";
49 |
50 | private static final String
51 | OLAP_DRIVER_PRECONFIGURED_DISCOVER_DATASOURCES_RESPONSE =
52 | "OlapDriverUsePreConfiguredDiscoverDatasourcesResponse";
53 |
54 | private static final String OLAP_DRIVER_IDLE_CONNECTIONS_TIMEOUT_MINUTES =
55 | "OlapDriverIdleConnectionsTimeoutMinutes";
56 |
57 | private static final String
58 | OLAP_DRIVER_PRECONFIGURED_DISCOVER_DATASOURCES_PREFIX =
59 | "OlapDriverDiscoverDatasources.";
60 |
61 | /**
62 | * Name of property used by JDBC to hold user name.
63 | */
64 | private static final String JDBC_USER = "user";
65 |
66 | /**
67 | * Name of property used by JDBC to hold password.
68 | */
69 | private static final String JDBC_PASSWORD = "password";
70 |
71 | /** idle connections are cleaned out after 5 minutes by default */
72 | private static final int DEFAULT_IDLE_CONNECTIONS_TIMEOUT_MS =
73 | 5 * 60 * 1000;
74 |
75 | private static final String OLAP_DRIVER_MAX_NUM_CONNECTIONS_PER_USER =
76 | "OlapDriverMaxNumConnectionsPerUser";
77 |
78 | /**
79 | * Unwraps a given interface from a given connection.
80 | *
81 | * @param connection Connection object
82 | * @param clazz Interface to unwrap
83 | * @param The properties found in the System properties override the ones in
351 | * the ServletConfig.
352 | *
353 | * copies the values of init parameters / properties which
354 | * start with the given prefix to a target Map object stripping out the
355 | * configured prefix from the property name.
356 | *
357 | * The following example uses prefix "olapConn.":
358 | *
359 | * This will result in a connection properties object with entries
372 | * In other words we want the "close" method to play nice and do all the
418 | * pooling actions while we want all the olap methods to execute directly on
419 | * the un-wrapped OlapConnection object.
420 | */
421 | private static OlapConnection createDelegatingOlapConnection(
422 | final Connection connection,
423 | final OlapConnection olapConnection)
424 | {
425 | return (OlapConnection) Proxy.newProxyInstance(
426 | olapConnection.getClass().getClassLoader(),
427 | new Class[] {OlapConnection.class},
428 | new InvocationHandler() {
429 | public Object invoke(
430 | Object proxy,
431 | Method method,
432 | Object[] args)
433 | throws Throwable
434 | {
435 | if ("unwrap".equals(method.getName())
436 | || OlapConnection.class
437 | .isAssignableFrom(method.getDeclaringClass()))
438 | {
439 | return method.invoke(olapConnection, args);
440 | } else {
441 | return method.invoke(connection, args);
442 | }
443 | }
444 | }
445 | );
446 | }
447 | }
448 |
449 | // End Olap4jXmlaServlet.java
450 |
--------------------------------------------------------------------------------
/src/main/java/mondrian/xmla/package.html:
--------------------------------------------------------------------------------
1 |
2 | More efficient than {@link java.util.Stack}, which extends
19 | * {@link java.util.Vector} and is
20 | * therefore synchronized whether you like it or not.
21 | *
22 | * @param To create a StringEscaper, create a builder.
28 | * Initially it is the identity transform (it leaves every character unchanged).
29 | * Call {@link Builder#defineEscape} as many
30 | * times as necessary to set up mappings, and then call {@link Builder#build}
31 | * to create a StringEscaper. StringEscaper is immutable, but you can call {@link #toBuilder()} to
34 | * get a builder back. Several escapers are pre-defined: For example, {"Foo_", "Bar%BAZ"} becomes "Foo.|Bar.*BAZ".
166 | *
167 | * @param wildcards List of SQL-style wildcard expressions
168 | * @return Regular expression
169 | */
170 | public static String wildcardToRegexp(List For example, The inverse operation of {@link java.util.Locale#toString()}.
240 | *
241 | * @param localeString Locale string, e.g. "en" or "en_US"
242 | * @return Java locale object
243 | */
244 | public static Locale parseLocale(String localeString) {
245 | String[] strings = localeString.split("_");
246 | switch (strings.length) {
247 | case 1:
248 | return new Locale(strings[0]);
249 | case 2:
250 | return new Locale(strings[0], strings[1]);
251 | case 3:
252 | return new Locale(strings[0], strings[1], strings[2]);
253 | default:
254 | throw newInternal(
255 | "bad locale string '" + localeString + "'");
256 | }
257 | }
258 |
259 | /**
260 | * Applies a collection of filters to an iterable.
261 | *
262 | * @param iterable Iterable
263 | * @param conds Zero or more conditions
264 | * @param null
if they do not want
102 | * to generate a custom session ID, in which case, the default algorithm
103 | * to generate session IDs will be used.
104 | * @param context The context of this query.
105 | * @return An arbitrary session id to use, or null
.
106 | */
107 | String generateSessionId(Map
98 | * XML does not allow certain characters as element and attribute names.
99 | * XML for Analysis supports encoding as defined by SQL Server 2000 to
100 | * address this XML constraint. For column names that contain invalid XML
101 | * name characters (according to the XML 1.0 specification), the nonvalid
102 | * Unicode characters are encoded using the corresponding hexadecimal
103 | * values. These are escaped as _xHHHH_ where HHHH stands for
104 | * the four-digit hexadecimal UCS-2 code for the character in
105 | * most-significant bit first order. For example, the name "Order Details"
106 | * is encoded as Order_x0020_Details, where the space character is
107 | * replaced by the corresponding hexadecimal code.
108 | *
109 | *
110 | * @param name Name of XML element
111 | * @return encoded name
112 | */
113 | private static String encodeElementName(String name) {
114 | StringBuilder buf = new StringBuilder();
115 | char[] nameChars = name.toCharArray();
116 | for (char ch : nameChars) {
117 | String encodedStr =
118 | (ch >= CHAR_TABLE.length ? null : CHAR_TABLE[ch]);
119 | if (encodedStr == null) {
120 | buf.append(ch);
121 | } else {
122 | buf.append(encodedStr);
123 | }
124 | }
125 | return buf.toString();
126 | }
127 |
128 |
129 | public static void element2Text(Element elem, final StringWriter writer)
130 | throws XmlaException
131 | {
132 | try {
133 | TransformerFactory factory = TransformerFactory.newInstance();
134 | Transformer transformer = factory.newTransformer();
135 | transformer.transform(
136 | new DOMSource(elem),
137 | new StreamResult(writer));
138 | } catch (Exception e) {
139 | throw new XmlaException(
140 | CLIENT_FAULT_FC,
141 | USM_DOM_PARSE_CODE,
142 | USM_DOM_PARSE_FAULT_FS,
143 | e);
144 | }
145 | }
146 |
147 | public static Element text2Element(String text)
148 | throws XmlaException
149 | {
150 | return _2Element(new InputSource(new StringReader(text)));
151 | }
152 |
153 | public static Element stream2Element(InputStream stream)
154 | throws XmlaException
155 | {
156 | return _2Element(new InputSource(stream));
157 | }
158 |
159 | private static Element _2Element(InputSource source)
160 | throws XmlaException
161 | {
162 | try {
163 | DocumentBuilderFactory factory =
164 | DocumentBuilderFactory.newInstance();
165 | factory.setIgnoringElementContentWhitespace(true);
166 | factory.setIgnoringComments(true);
167 | factory.setNamespaceAware(true);
168 | DocumentBuilder builder = factory.newDocumentBuilder();
169 | Document doc = builder.parse(source);
170 | return doc.getDocumentElement();
171 | } catch (Exception e) {
172 | throw new XmlaException(
173 | CLIENT_FAULT_FC,
174 | USM_DOM_PARSE_CODE,
175 | USM_DOM_PARSE_FAULT_FS,
176 | e);
177 | }
178 | }
179 |
180 | public static Element[] filterChildElements(
181 | Element parent,
182 | String ns,
183 | String lname)
184 | {
185 | /*
186 | way too noisy
187 | if (LOGGER.isDebugEnabled()) {
188 | StringBuilder buf = new StringBuilder(100);
189 | buf.append("XmlaUtil.filterChildElements: ");
190 | buf.append(" ns=\"");
191 | buf.append(ns);
192 | buf.append("\", lname=\"");
193 | buf.append(lname);
194 | buf.append("\"");
195 | LOGGER.debug(buf.toString());
196 | }
197 | */
198 |
199 | List> rowList = new ArrayList
>();
523 | }
524 |
525 | /**
526 | * Wrapper which indicates that a restriction is to be treated as a
527 | * SQL-style wildcard match.
528 | */
529 | public static class Wildcard {
530 | public final String pattern;
531 |
532 | public Wildcard(String pattern) {
533 | this.pattern = pattern;
534 | }
535 | }
536 |
537 | public static class ElementNameEncoder {
538 | private final Map
77 | *
81 | * null
for root access.
95 | */
96 | public abstract String authenticate(
97 | String username,
98 | String password,
99 | String sessionID);
100 |
101 | /**
102 | * Helper method to create and throw an authentication exception.
103 | * @param reason A textual explanation of why the credentials are
104 | * rejected.
105 | */
106 | protected void throwAuthenticationException(String reason) {
107 | throw new XmlaException(
108 | XmlaConstants.CLIENT_FAULT_FC,
109 | XmlaConstants.CHH_AUTHORIZATION_CODE,
110 | XmlaConstants.CHH_AUTHORIZATION_FAULT_FS,
111 | new Exception(
112 | "There was a problem with the credentials: "
113 | + reason));
114 | }
115 |
116 | public boolean processHttpHeader(
117 | HttpServletRequest request,
118 | HttpServletResponse response,
119 | MapSaxWriter
which, perversely, generates a
23 | * JSON (JavaScript Object Notation) document.
24 | *
25 | * @author jhyde
26 | */
27 | class JsonSaxWriter implements SaxWriter {
28 | private final StringBuilder buf = new StringBuilder();
29 | private int indent;
30 | private String[] indentStrings = INITIAL_INDENT_STRINGS;
31 | private String indentString = indentStrings[0];
32 | private final ArrayStack stack = new ArrayStack();
33 | private OutputStream outputStream;
34 |
35 | private static final String[] INITIAL_INDENT_STRINGS = {
36 | "",
37 | " ",
38 | " ",
39 | " ",
40 | " ",
41 | " ",
42 | " ",
43 | " ",
44 | " ",
45 | " ",
46 | };
47 |
48 | /**
49 | * Creates a JsonSaxWriter.
50 | *
51 | * @param outputStream Output stream
52 | */
53 | public JsonSaxWriter(OutputStream outputStream) {
54 | this.outputStream = outputStream;
55 | }
56 |
57 | public void startDocument() {
58 | stack.push(new Frame(null));
59 | }
60 |
61 | public void endDocument() {
62 | stack.pop();
63 | flush();
64 | }
65 |
66 | public void startSequence(String name, String subName) {
67 | comma();
68 | buf.append(indentString);
69 | if (name == null) {
70 | name = subName;
71 | }
72 | if (stack.peek().name != null) {
73 | assert name.equals(stack.peek().name)
74 | : "In sequence [" + stack.peek() + "], element name ["
75 | + name + "]";
76 | buf.append("[");
77 | } else {
78 | Util.quoteForMdx(buf, name);
79 | buf.append(": [");
80 | }
81 |
82 | assert subName != null;
83 | stack.push(new Frame(subName));
84 | indent();
85 | }
86 |
87 | public void endSequence() {
88 | assert stack.peek() != null : "not in sequence";
89 | stack.pop();
90 | outdent();
91 |
92 | buf.append("\n");
93 | buf.append(indentString);
94 | buf.append("]");
95 | }
96 |
97 | public void startElement(String name) {
98 | comma();
99 | buf.append(indentString);
100 | if (stack.peek().name != null) {
101 | assert name.equals(stack.peek().name)
102 | : "In sequence [" + stack.peek() + "], element name ["
103 | + name + "]";
104 | buf.append("{");
105 | } else {
106 | Util.quoteForMdx(buf, name);
107 | buf.append(": {");
108 | }
109 |
110 | stack.push(new Frame(null));
111 | indent();
112 | }
113 |
114 | public void startElement(String name, Object... attrs) {
115 | startElement(name);
116 | for (int i = 0; i < attrs.length;) {
117 | if (i > 0) {
118 | buf.append(",\n");
119 | } else {
120 | buf.append("\n");
121 | }
122 | String attr = (String) attrs[i++];
123 | buf.append(indentString);
124 | Util.quoteForMdx(buf, attr);
125 | buf.append(": ");
126 | Object value = attrs[i++];
127 | value(value);
128 | }
129 | stack.peek().ordinal = attrs.length / 2;
130 | }
131 |
132 | public void endElement() {
133 | Frame prev = stack.pop();
134 | assert prev.name == null
135 | : "Ended an element, but in sequence " + prev.name;
136 | buf.append("\n");
137 | outdent();
138 | buf.append(indentString);
139 | buf.append("}");
140 | }
141 |
142 | public void element(String name, Object... attrs) {
143 | startElement(name, attrs);
144 | endElement();
145 | }
146 |
147 | public void characters(String data) {
148 | throw new UnsupportedOperationException();
149 | }
150 |
151 | public void textElement(String name, Object data) {
152 | comma();
153 | buf.append(indentString);
154 | Util.quoteForMdx(buf, name);
155 | buf.append(": ");
156 | value(data);
157 | }
158 |
159 | public void completeBeforeElement(String tagName) {
160 | throw new UnsupportedOperationException();
161 | }
162 |
163 | public void verbatim(String text) {
164 | throw new UnsupportedOperationException();
165 | }
166 |
167 | public void flush() {
168 | try {
169 | outputStream.write(buf.toString().substring(1).getBytes());
170 | } catch (IOException e) {
171 | throw Util.newError(e, "While encoding JSON response");
172 | }
173 | }
174 |
175 | // helper methods
176 |
177 | private void indent() {
178 | ++indent;
179 | if (indent >= indentStrings.length) {
180 | final int newLength = indentStrings.length * 2 + 1;
181 | final int INDENT = 2;
182 | assert indentStrings[1].length() == INDENT;
183 | char[] chars = new char[newLength * INDENT];
184 | Arrays.fill(chars, ' ');
185 | String s = new String(chars);
186 | indentStrings = new String[newLength];
187 | for (int i = 0; i < newLength; ++i) {
188 | indentStrings[i] = s.substring(0, i * INDENT);
189 | }
190 | }
191 | indentString = indentStrings[indent];
192 | }
193 |
194 | private void outdent() {
195 | indentString = indentStrings[--indent];
196 | }
197 |
198 | /**
199 | * Writes a value with appropriate quoting for a JavaScript constant
200 | * of that type.
201 | *
202 | *
370 | *
371 | *
360 | * <init-param>
361 | * <param-name>olapConn.User</param-name>
362 | * <param-value>mrossi</param-value>
363 | * </init-param>
364 | * <init-param>
365 | * <param-name>olapConn.Password</param-name>
366 | * <param-value>manhattan</param-value>
367 | * </init-param>
368 | *
369 | *
{("User", "mrossi"), ("Password", "manhattan")}
.
373 | *
374 | * @param prefix Prefix to property name
375 | * @param servletConfig Servlet config
376 | * @return Map containing property names and values
377 | */
378 | private static Map
39 | *
50 | */
51 | public class StringEscaper
52 | {
53 | private final String [] translationTable;
54 |
55 | public static final StringEscaper HTML_ESCAPER =
56 | new Builder()
57 | .defineEscape('&', "&")
58 | .defineEscape('"', """)
59 | .defineEscape('\'', "'") // not "'"
60 | .defineEscape('<', "<")
61 | .defineEscape('>', ">")
62 | .build();
63 |
64 | public static final StringEscaper XML_ESCAPER = HTML_ESCAPER;
65 |
66 | public static final StringEscaper XML_NUMERIC_ESCAPER =
67 | new Builder()
68 | .defineEscape('&',"&")
69 | .defineEscape('"',""")
70 | .defineEscape('\'',"'")
71 | .defineEscape('<',"<")
72 | .defineEscape('>',">")
73 | .defineEscape('\t'," ")
74 | .defineEscape('\n',"
")
75 | .defineEscape('\r',"
")
76 | .build();
77 |
78 | public static final StringEscaper URL_ARG_ESCAPER =
79 | new Builder()
80 | .defineEscape('?', "%3f")
81 | .defineEscape('&', "%26")
82 | .build();
83 |
84 | public static final StringEscaper URL_ESCAPER =
85 | URL_ARG_ESCAPER.toBuilder()
86 | .defineEscape('%', "%%")
87 | .defineEscape('"', "%22")
88 | .defineEscape('\r', "+")
89 | .defineEscape('\n', "+")
90 | .defineEscape(' ', "+")
91 | .defineEscape('#', "%23")
92 | .build();
93 |
94 | /**
95 | * Creates a StringEscaper. Only called from Builder.
96 | */
97 | private StringEscaper(String[] translationTable) {
98 | this.translationTable = translationTable;
99 | }
100 |
101 | /**
102 | * Apply an immutable transformation to the given string.
103 | */
104 | public String escapeString(String s)
105 | {
106 | StringBuilder sb = null;
107 | int n = s.length();
108 | for (int i = 0; i < n; i++) {
109 | char c = s.charAt(i);
110 | String escape;
111 | // codes >= 128 (e.g. Euro sign) are always escaped
112 | if (c > 127) {
113 | escape = "" + Integer.toString(c) + ";";
114 | } else if (c >= translationTable.length) {
115 | escape = null;
116 | } else {
117 | escape = translationTable[c];
118 | }
119 | if (escape == null) {
120 | if (sb != null) {
121 | sb.append(c);
122 | }
123 | } else {
124 | if (sb == null) {
125 | sb = new StringBuilder(n * 2);
126 | sb.append(s.substring(0, i));
127 | }
128 | sb.append(escape);
129 | }
130 | }
131 |
132 | if (sb == null) {
133 | return s;
134 | } else {
135 | return sb.toString();
136 | }
137 | }
138 |
139 | /**
140 | * Applies an immutable transformation to the given string, writing the
141 | * results to a string buffer.
142 | */
143 | public void appendEscapedString(String s, StringBuffer sb)
144 | {
145 | int n = s.length();
146 | for (int i = 0; i < n; i++) {
147 | char c = s.charAt(i);
148 | String escape;
149 | if (c >= translationTable.length) {
150 | escape = null;
151 | } else {
152 | escape = translationTable[c];
153 | }
154 | if (escape == null) {
155 | sb.append(c);
156 | } else {
157 | sb.append(escape);
158 | }
159 | }
160 | }
161 |
162 | /**
163 | * Applies an immutable transformation to the given string, writing the
164 | * results to an {@link Appendable} (such as a {@link StringBuilder}).
165 | */
166 | public void appendEscapedString(String s, Appendable sb) throws IOException
167 | {
168 | int n = s.length();
169 | for (int i = 0; i < n; i++) {
170 | char c = s.charAt(i);
171 | String escape;
172 | if (c >= translationTable.length) {
173 | escape = null;
174 | } else {
175 | escape = translationTable[c];
176 | }
177 | if (escape == null) {
178 | sb.append(c);
179 | } else {
180 | sb.append(escape);
181 | }
182 | }
183 | }
184 |
185 | /**
186 | * Creates a builder from an existing escaper.
187 | */
188 | public Builder toBuilder()
189 | {
190 | return new Builder(
191 | new ArrayListfind
to start searching
128 | * @param find String to find
129 | * @param replace String to replace it with
130 | * @return The string buffer
131 | */
132 | public static StringBuilder replace(
133 | StringBuilder buf,
134 | int start,
135 | String find,
136 | String replace)
137 | {
138 | // Search and replace from the end towards the start, to avoid O(n ^ 2)
139 | // copying if the string occurs very commonly.
140 | int findLength = find.length();
141 | if (findLength == 0) {
142 | // Special case where the seek string is empty.
143 | for (int j = buf.length(); j >= 0; --j) {
144 | buf.insert(j, replace);
145 | }
146 | return buf;
147 | }
148 | int k = buf.length();
149 | while (k > 0) {
150 | int i = buf.lastIndexOf(find, k);
151 | if (i < start) {
152 | break;
153 | }
154 | buf.replace(i, i + find.length(), replace);
155 | // Step back far enough to ensure that the beginning of the section
156 | // we just replaced does not cause a match.
157 | k = i - findLength;
158 | }
159 | return buf;
160 | }
161 |
162 | /**
163 | * Converts a list of SQL-style patterns into a Java regular expression.
164 | *
165 | * camelToUpper("FooBar")
returns "FOO_BAR".
214 | *
215 | * @param s Camel-case string
216 | * @return Upper-case string
217 | */
218 | public static String camelToUpper(String s) {
219 | StringBuilder buf = new StringBuilder(s.length() + 10);
220 | int prevUpper = -1;
221 | for (int i = 0; i < s.length(); ++i) {
222 | char c = s.charAt(i);
223 | if (Character.isUpperCase(c)) {
224 | if (i > prevUpper + 1) {
225 | buf.append('_');
226 | }
227 | prevUpper = i;
228 | } else {
229 | c = Character.toUpperCase(c);
230 | }
231 | buf.append(c);
232 | }
233 | return buf.toString();
234 | }
235 |
236 | /**
237 | * Parses a locale string.
238 | *
239 | *