|
|||||||||||||||||||
Source file | Conditionals | Statements | Methods | TOTAL | |||||||||||||||
DatabaseImpl.java | 46.7% | 66.7% | 87.5% | 62.1% |
|
1 | /* | |
2 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
3 | * contributor license agreements. See the NOTICE file distributed with | |
4 | * this work for additional information regarding copyright ownership. | |
5 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
6 | * (the "License"); you may not use this file except in compliance with | |
7 | * the License. You may obtain a copy of the License at | |
8 | * | |
9 | * http://www.apache.org/licenses/LICENSE-2.0 | |
10 | * | |
11 | * Unless required by applicable law or agreed to in writing, software | |
12 | * distributed under the License is distributed on an "AS IS" BASIS, | |
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | * See the License for the specific language governing permissions and | |
15 | * limitations under the License. | |
16 | * | |
17 | * $Id: DatabaseImpl.java 708000 2008-10-26 14:50:07Z natalia $ | |
18 | */ | |
19 | ||
20 | package org.apache.xindice.client.xmldb.xmlrpc; | |
21 | ||
22 | import org.apache.commons.logging.Log; | |
23 | import org.apache.commons.logging.LogFactory; | |
24 | import org.apache.xindice.client.xmldb.CommonConfigurable; | |
25 | import org.apache.xmlrpc.client.XmlRpcClient; | |
26 | import org.apache.xmlrpc.client.XmlRpcClientConfigImpl; | |
27 | import org.xmldb.api.base.Collection; | |
28 | import org.xmldb.api.base.Database; | |
29 | import org.xmldb.api.base.ErrorCodes; | |
30 | import org.xmldb.api.base.XMLDBException; | |
31 | ||
32 | import java.net.MalformedURLException; | |
33 | import java.net.URL; | |
34 | import java.util.HashMap; | |
35 | import java.util.Map; | |
36 | ||
37 | /** | |
38 | * Implements XML:DB's <code>Database</code> interface using XML-RPC to communicate | |
39 | * with the Xindice XML-RPC server. Usually, this class is not used | |
40 | * directly, but {@link org.apache.xindice.client.xmldb.DatabaseImpl} is used instead. | |
41 | * | |
42 | * Note this class is a database <em>driver</em>, and one class of this database | |
43 | * could be used to connect to <em>many</em> different databases, on one or | |
44 | * different Xindice XML-RPC servers. | |
45 | * | |
46 | * XML-RPC database driver uses following configuration parameters: | |
47 | * <ul> | |
48 | * <li><strong>service-location</strong>: Specifies path to the Xindice server WebApp</li> | |
49 | * <li><strong>xmlrpc-driver</strong>: Specifies name of the SAX parser to use</li> | |
50 | * <li><strong>xmlrpc-user</strong>: If server protected with HTTP Basic Auth, specifies user name</li> | |
51 | * <li><strong>xmlrpc-password</strong>: If server protected with HTTP Basic Auth, specifies password</li> | |
52 | * </ul> | |
53 | * | |
54 | * If one of these parameters is not specified, system properties will be used. | |
55 | * | |
56 | * @author <a href="mailto:james.bates@amplexor.com">James Bates</a> | |
57 | * @author <a href="mailto:vgritsenko@apache.org">Vadim Gritsenko</a> | |
58 | * @version $Revision: 708000 $, $Date: 2008-10-26 14:50:07 +0000 (Sun, 26 Oct 2008) $ | |
59 | */ | |
60 | public class DatabaseImpl extends CommonConfigurable implements Database { | |
61 | ||
62 | private static final Log log = LogFactory.getLog(DatabaseImpl.class); | |
63 | ||
64 | /** | |
65 | * Driver property name for the xml-rpc service location. | |
66 | */ | |
67 | public static final String PROP_SERVICE_LOCATION = "service-location"; | |
68 | ||
69 | /** | |
70 | * System property name for the service location | |
71 | */ | |
72 | public static final String SYSPROP_SERVICE_LOCATION = "xindice.xmlrpc.service-location"; | |
73 | ||
74 | /** | |
75 | * Driver property name for the basic authentication user name. | |
76 | */ | |
77 | public static final String PROP_XMLRPC_USER = "xmlrpc-user"; | |
78 | ||
79 | /** | |
80 | * System property name for the basic authentication user name in case | |
81 | * there were no configuration property passed | |
82 | */ | |
83 | public static final String SYSPROP_XMLRPC_USER = "xindice.xmlrpc.user"; | |
84 | ||
85 | /** | |
86 | * Driver property name for the basic authentication password. | |
87 | */ | |
88 | public static final String PROP_XMLRPC_PASSWORD = "xmlrpc-password"; | |
89 | ||
90 | /** | |
91 | * System property name for the basic authentication password in case | |
92 | * there were no configuration property passed | |
93 | */ | |
94 | public static final String SYSPROP_XMLRPC_PASSWORD = "xindice.xmlrpc.password"; | |
95 | ||
96 | /** | |
97 | * Default path to the XML-RPC service in the web server | |
98 | */ | |
99 | private static final String DEFAULT_SERVICE_LOCATION = "/xindice/"; | |
100 | ||
101 | /** | |
102 | * Prefix used to denote XML:DB URI's that should use this driver | |
103 | */ | |
104 | public static final String DRIVER_NAME = "xindice"; | |
105 | ||
106 | /** | |
107 | * XML:DB conformance level of this driver | |
108 | */ | |
109 | private static final String CONFORMANCE_LEVEL = "0"; | |
110 | ||
111 | /** | |
112 | * Map contains XML_RPC clients for different hostname:port addresses | |
113 | */ | |
114 | private Map clients = new HashMap(); | |
115 | ||
116 | ||
117 | /** | |
118 | * Create a new DatabaseImpl object. | |
119 | */ | |
120 | 1 | public DatabaseImpl() { |
121 | 1 | super(); |
122 | } | |
123 | ||
124 | /** | |
125 | * Create a new DatabaseImpl object with a copy of the properties | |
126 | * from the DatabaseImpl parameter. | |
127 | * | |
128 | * @param config from which the initial parameters for this | |
129 | * DatabaseImpl object are copied. | |
130 | */ | |
131 | 1 | public DatabaseImpl(CommonConfigurable config) { |
132 | 1 | super(config); |
133 | } | |
134 | ||
135 | /** | |
136 | * Determines whether this <code>Database</code> implementation can handle | |
137 | * the URI. It should return true if the Database instance knows how to | |
138 | * handle the URI and false otherwise. | |
139 | * | |
140 | * @param uri the URI to check for. | |
141 | * @return true if the URI can be handled, false otherwise. | |
142 | * @exception XMLDBException with expected error codes.<br /> | |
143 | * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor | |
144 | * specific errors that occur.<br /> | |
145 | * <code>ErrroCodes.INVALID_URI</code> If the URI is not in a valid format. <br /> | |
146 | */ | |
147 | 1013 | public boolean acceptsURI(String uri) throws XMLDBException { |
148 | ||
149 | 1013 | return ((uri != null) && uri.startsWith(getName() + "://")); |
150 | } | |
151 | ||
152 | /** | |
153 | * Initializes XML-RPC service location, basic authentication. | |
154 | * Creates XML-RPC client. | |
155 | * | |
156 | * @param hostPort of the Xindice XML-RPC server | |
157 | * @return XML-RPC client connected to the Xindice server | |
158 | * @throws XMLDBException if host or port information was incorrect and resulted in malformed URL | |
159 | */ | |
160 | 1010 | private XmlRpcClient connect(String hostPort) throws XMLDBException { |
161 | ||
162 | 1010 | synchronized (this) { |
163 | 1010 | if (!clients.containsKey(hostPort)) { |
164 | // Initialize XML-RPC static properties | |
165 | 1 | XmlRpcClientConfigImpl cfg = new XmlRpcClientConfigImpl(); |
166 | 1 | cfg.setEncoding("utf-8"); |
167 | // FIXME Is XmlRpc.setKeepAlive(true); gone forever? | |
168 | ||
169 | /* | |
170 | * Determine the path in the web server to the XML-RPC service. | |
171 | * | |
172 | * In priority order: | |
173 | * DatabaseImpl service-location property | |
174 | * (passed in the serviceLocation parameter) | |
175 | * System property "xindice.xmlrpc.service-location" | |
176 | * Default value "/xindice/" | |
177 | */ | |
178 | 1 | String serviceLocation = getProperty(PROP_SERVICE_LOCATION); |
179 | 1 | if (serviceLocation == null) { |
180 | 0 | serviceLocation = System.getProperty(SYSPROP_SERVICE_LOCATION); |
181 | 0 | if (serviceLocation == null) { |
182 | 0 | serviceLocation = DEFAULT_SERVICE_LOCATION; |
183 | } | |
184 | } | |
185 | 1 | if (!serviceLocation.startsWith("/")) { |
186 | 0 | serviceLocation = "/" + serviceLocation; |
187 | } | |
188 | 1 | if (!serviceLocation.endsWith("/")) { |
189 | 0 | serviceLocation = serviceLocation + "/"; |
190 | } | |
191 | ||
192 | 1 | String xmlRpcURL = "http://" + hostPort + serviceLocation; |
193 | 1 | if (log.isDebugEnabled()) { |
194 | 0 | log.debug("Using URL: '" + xmlRpcURL + "'"); |
195 | } | |
196 | 1 | try { |
197 | 1 | cfg.setServerURL(new URL(xmlRpcURL)); |
198 | } catch (MalformedURLException e) { | |
199 | 0 | throw new XMLDBException(ErrorCodes.INVALID_URI, e); |
200 | } | |
201 | ||
202 | /* | |
203 | * Determine basic authentication parameters | |
204 | */ | |
205 | 1 | String basicUser = getProperty(PROP_XMLRPC_USER); |
206 | 1 | if (basicUser == null) { |
207 | 1 | basicUser = System.getProperty(SYSPROP_XMLRPC_USER); |
208 | } | |
209 | 1 | if (basicUser != null) { |
210 | 0 | String basicPassword = getProperty(PROP_XMLRPC_PASSWORD); |
211 | 0 | if (basicPassword == null) { |
212 | 0 | basicPassword = System.getProperty(SYSPROP_XMLRPC_PASSWORD); |
213 | } | |
214 | ||
215 | 0 | if (log.isDebugEnabled()) { |
216 | 0 | log.debug("Using Basic authentication. User: '" + basicUser + "', password: '" + basicPassword + "'"); |
217 | } | |
218 | 0 | cfg.setBasicUserName(basicPassword); |
219 | 0 | cfg.setBasicPassword(basicPassword); |
220 | } | |
221 | ||
222 | 1 | XmlRpcClient client = new XmlRpcClient(); |
223 | 1 | client.setConfig(cfg); |
224 | ||
225 | 1 | clients.put(hostPort, client); |
226 | } | |
227 | } | |
228 | ||
229 | 1010 | return (XmlRpcClient) clients.get(hostPort); |
230 | } | |
231 | ||
232 | /** | |
233 | * Retrieves a <code>Collection</code> instance based on the URI provided | |
234 | * in the <code>uri</code> parameter. The format of the URI is defined in the | |
235 | * documentation for DatabaseManager.getCollection().<p/> | |
236 | * | |
237 | * Authentication is handled via username and password however it is not | |
238 | * required that the database support authentication. Databases that do not | |
239 | * support authentication MUST ignore the | |
240 | * <code>username</code> and <code>password</code> if those provided are not | |
241 | * null. | |
242 | * | |
243 | * @param uri the URI to use to locate the collection. | |
244 | * @param password The password to use for authentication to the database or | |
245 | * null if the database does not support authentication. | |
246 | * @return A <code>Collection</code> instance for the requested collection or | |
247 | * null if the collection could not be found. | |
248 | * @exception XMLDBException with expected error codes.<br /> | |
249 | * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor | |
250 | * specific errors that occur.<br /> | |
251 | * <code>ErrroCodes.INVALID_URI</code> If the URI is not in a valid format. <br /> | |
252 | * <code>ErrroCodes.PERMISSION_DENIED</code> If the <code>username</code> | |
253 | * and <code>password</code> were not accepted by the database. | |
254 | */ | |
255 | 1010 | public Collection getCollection(String uri, String userName, String password) throws XMLDBException { |
256 | /* TODO: introduce authentication some day */ | |
257 | ||
258 | 1010 | if (!acceptsURI(uri)) { |
259 | 0 | throw new XMLDBException(ErrorCodes.INVALID_URI, |
260 | "Invalid URL: " + uri); | |
261 | } | |
262 | ||
263 | /* Chop off driver prefix, and '://' */ | |
264 | 1010 | uri = uri.substring(getName().length() + 3); |
265 | ||
266 | /* Extract host name & port, if present */ | |
267 | 1010 | int firstSlash = uri.indexOf('/'); |
268 | 1010 | if (firstSlash == -1) { |
269 | 0 | throw new XMLDBException(ErrorCodes.INVALID_URI, |
270 | "Invalid URL (must have '/'): " + uri); | |
271 | } | |
272 | ||
273 | /* Extract collection name */ | |
274 | 1010 | String collPath = uri.substring(firstSlash); |
275 | 1010 | if (!collPath.startsWith("/")) { |
276 | 0 | throw new XMLDBException(ErrorCodes.INVALID_URI, |
277 | "Invalid URL (collection name must start with '/'): " + uri); | |
278 | } | |
279 | ||
280 | 1010 | String hostPort = uri.substring(0, firstSlash); |
281 | ||
282 | /* Absent host defaults to localhost and standard Xindice HTTP port */ | |
283 | 1010 | if (hostPort.equals("")) { |
284 | 0 | hostPort = "127.0.0.1:8888"; |
285 | } | |
286 | ||
287 | 1010 | try { |
288 | 1010 | return new CollectionImpl(connect(hostPort), collPath); |
289 | } catch (XMLDBException e) { | |
290 | 2 | if (e.errorCode == ErrorCodes.NO_SUCH_COLLECTION) { |
291 | // per getCollection contract, return null if not found | |
292 | 1 | return null; |
293 | } | |
294 | 1 | throw e; |
295 | } | |
296 | } | |
297 | ||
298 | /** | |
299 | * Returns the prefix used in XML:DB to denote URI's that this driver can | |
300 | * handle. | |
301 | * | |
302 | * @return the prefix driver name | |
303 | * @exception XMLDBException with expected error codes.<br /> | |
304 | * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor | |
305 | * specific errors that occur.<br /> | |
306 | */ | |
307 | 2027 | public String getName() throws XMLDBException { |
308 | 2027 | return DRIVER_NAME; |
309 | } | |
310 | ||
311 | /** | |
312 | * Returns an array of names associated with the Database instance. | |
313 | * | |
314 | * @return the array of name of the object. | |
315 | * @exception XMLDBException with expected error codes.<br /> | |
316 | * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor | |
317 | * specific errors that occur.<br /> | |
318 | */ | |
319 | 2 | public String[] getNames() throws XMLDBException { |
320 | 2 | return new String[]{ getName() }; |
321 | } | |
322 | ||
323 | /** | |
324 | * Returns the XML:DB API Conformance level for the implementation. This can | |
325 | * be used by client programs to determine what functionality is available to | |
326 | * them. | |
327 | * | |
328 | * @return the XML:DB API conformance level for this implementation. | |
329 | * @exception XMLDBException with expected error codes.<br /> | |
330 | * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor | |
331 | * specific errors that occur.<br /> | |
332 | */ | |
333 | 0 | public String getConformanceLevel() throws XMLDBException { |
334 | 0 | return CONFORMANCE_LEVEL; |
335 | } | |
336 | } |
|