|
|||||||||||||||||||
| 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 | } |
|
||||||||||