Package org.exist.xmldb

Source Code of org.exist.xmldb.DatabaseImpl

/*
*  eXist Open Source Native XML Database
*  Copyright (C) 2001 Wolfgang M. Meier
*  wolfgang@exist-db.org
*  http://exist.sourceforge.net
*
*  This program is free software; you can redistribute it and/or
*  modify it under the terms of the GNU Lesser General Public License
*  as published by the Free Software Foundation; er version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public License
*  along with this program; if not, write to the Free Software
*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*  $Id$
*/
package org.exist.xmldb;

import org.apache.log4j.Logger;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;

import org.exist.EXistException;
import org.exist.security.AuthenticationException;
import org.exist.security.SecurityManager;
import org.exist.security.Subject;
import org.exist.security.xacml.AccessContext;
import org.exist.storage.BrokerPool;
import org.exist.util.Configuration;
import org.exist.util.SSLHelper;

import org.xmldb.api.base.Collection;
import org.xmldb.api.base.Database;
import org.xmldb.api.base.ErrorCodes;
import org.xmldb.api.base.XMLDBException;

import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashMap;

/**
* The XMLDB driver class for eXist. This driver manages two different
* internal implementations. The first communicates with a remote
* database using the XMLRPC protocol. The second has direct access
* to an embedded database instance running in the same virtual machine.
* The driver chooses an implementation depending on the XML:DB URI passed
* to getCollection().
*
* When running in embedded mode, the driver can create a new database
* instance if none is available yet. It will do so if the property
* "create-database" is set to "true" or if there is a system property
* "exist.initdb" with value "true".
*
* You may optionally provide the location of an alternate configuration
* file through the "configuration" property. The driver is also able to
* address different database instances - which may have been installed at
* different places.
*
* @author Wolfgang Meier
*/
public class DatabaseImpl implements Database {

  private final static Logger LOG = Logger.getLogger(DatabaseImpl.class);

  //TODO : discuss about other possible values
  protected final static String LOCAL_HOSTNAME = "";

  protected final static int UNKNOWN_CONNECTION = -1;
  protected final static int LOCAL_CONNECTION = 0;
  protected final static int REMOTE_CONNECTION = 1;

  /** Default config filename to configure an Instance */
    public final static String CONF_XML="conf.xml";

  protected boolean autoCreate = false;
  protected String configuration = null;
  protected String currentInstanceName = null;

  private HashMap<String, XmlRpcClient> rpcClients = new HashMap<String, XmlRpcClient>();
  protected ShutdownListener shutdown = null;
  protected int mode = UNKNOWN_CONNECTION;
   
  private Boolean ssl_enable=false;
  private Boolean ssl_allow_self_signed=true;
  private Boolean ssl_verify_hostname=false;

  public DatabaseImpl() {
      final String initdb = System.getProperty( "exist.initdb" );
      if(initdb != null)
        {autoCreate = initdb.equalsIgnoreCase("true");}
  }

  /**
     *  In embedded mode: configure the database instance
   *
     *@exception  XMLDBException  Description of the Exception
   */
  private void configure(String instanceName) throws XMLDBException {
        // System.out.println("Configuring '" + instanceName + "' using " + Configuration.getPath(configuration, null));
    try {
      final Configuration config = new Configuration(configuration, null);
      BrokerPool.configure(instanceName, 1, 5, config);
            if (shutdown != null)
                {BrokerPool.getInstance(instanceName).registerShutdownListener(shutdown);}
        } catch (final Exception e ) {
            throw new XMLDBException(ErrorCodes.VENDOR_ERROR, "configuration error: " + e.getMessage(), e );
    }
    currentInstanceName = instanceName;
  }

    /* @deprecated  Although part of the xmldb API, the design is somewhat inconsistent.
   * @see org.xmldb.api.base.Database#acceptsURI(java.lang.String)
   */
  public boolean acceptsURI(String uri) throws XMLDBException {
    XmldbURI xmldbURI = null;
    try {
            //Ugly workaround for non-URI compliant collection (resources ?) names (most likely IRIs)
      final String newURIString = XmldbURI.recoverPseudoURIs(uri);
            //Remember that DatabaseManager (provided in xmldb.jar) trims the leading "xmldb:" !!!
            //... prepend it to have a real xmldb URI again...
      xmldbURI = XmldbURI.xmldbUriFor(XmldbURI.XMLDB_URI_PREFIX + newURIString);
      return acceptsURI(xmldbURI);
    } catch (final URISyntaxException e) {
            //... even in the error message
      throw new XMLDBException(ErrorCodes.INVALID_DATABASE, "xmldb URI is not well formed: " + XmldbURI.XMLDB_URI_PREFIX + uri);
    }
  }

  public boolean acceptsURI(XmldbURI xmldbURI) throws XMLDBException {
        //TODO : smarter processing (resources names, protocols, servers accessibility...) ? -pb
    return true;
  }

    /* Returns a collection from the given "uri".
     * @deprecated  Although part of the xmldb API, the design is somewhat inconsistent.
     * @see org.exist.xmldb.DatabaseImpl#getCollection(org.exist.xmldb.XmldbURI, java.lang.String, java.lang.String)
     * @see org.xmldb.api.base.Database#getCollection(java.lang.String, java.lang.String, java.lang.String)
     */
    @Override
    public Collection getCollection(final String uri, final String user, final String password) throws XMLDBException {
        try {
            //Ugly workaround for non-URI compliant collection names (most likely IRIs)
            final String newURIString = XmldbURI.recoverPseudoURIs(uri);
            //Remember that DatabaseManager (provided in xmldb.jar) trims the leading "xmldb:" !!!
            //... prepend it to have a real xmldb URI again...
            final XmldbURI xmldbURI = XmldbURI.xmldbUriFor(XmldbURI.XMLDB_URI_PREFIX + newURIString);
            return getCollection(xmldbURI, user, password);
        } catch(final URISyntaxException e) {
            //... even in the error message
            throw new XMLDBException(ErrorCodes.INVALID_DATABASE, "xmldb URI is not well formed: " + XmldbURI.XMLDB_URI_PREFIX + uri);
        }
    }

    public Collection getCollection(final XmldbURI xmldbURI, final String user, final String password) throws XMLDBException {
        if(XmldbURI.API_LOCAL.equals(xmldbURI.getApiName())) {
            return getLocalCollection(xmldbURI, user, password);
        } else if (XmldbURI.API_XMLRPC.equals(xmldbURI.getApiName())) {
            return getRemoteCollection(xmldbURI, user, password);
        } else {
            throw new XMLDBException(ErrorCodes.INVALID_DATABASE, "Unknown or unparsable API for: " + xmldbURI);
        }
    }

    /**
     * @param xmldbURI
     * @param user
     * @param password
     * @return The collection
     * @throws XMLDBException
     */
    private Collection getLocalCollection(final XmldbURI xmldbURI, final String user, final String password) throws XMLDBException {
        mode = LOCAL_CONNECTION;
       
        // use local database instance
        if(!BrokerPool.isConfigured(xmldbURI.getInstanceName())) {
            if(autoCreate) {
                configure(xmldbURI.getInstanceName());
            } else {
                throw new XMLDBException(ErrorCodes.COLLECTION_CLOSED, "Local database server is not running");
            }
        }
       
        BrokerPool pool;
        try {
            pool = BrokerPool.getInstance(xmldbURI.getInstanceName());
        } catch (final EXistException e) {
            throw new XMLDBException( ErrorCodes.VENDOR_ERROR, "Can not access to local database instance", e);
        }
        final Subject u = getUser(user, password, pool);
        try {
            return  new LocalCollection(u, pool, xmldbURI.toCollectionPathURI(), AccessContext.XMLDB);
        } catch(final XMLDBException e) {
            switch (e.errorCode) {
              case ErrorCodes.NO_SUCH_RESOURCE:
              case ErrorCodes.NO_SUCH_COLLECTION:
              case ErrorCodes.INVALID_COLLECTION:
              case ErrorCodes.INVALID_RESOURCE:
                LOG.info(e.getMessage());
                return null;
              default:
                LOG.error(e.getMessage(), e);
                throw e;
            }
        }
    }

    /**
     * @param xmldbURI
     * @param user
     * @param password
     * @return The collection
     * @throws XMLDBException
     */
    private Collection getRemoteCollection(final XmldbURI xmldbURI, String user, String password) throws XMLDBException {
        mode = REMOTE_CONNECTION;
        if(user == null) {
            //TODO : read this from configuration
            user = SecurityManager.GUEST_USER;
            password = SecurityManager.GUEST_USER;
        }
   
        if(password == null) {
            password = "";
        }
       
        try {
            final String protocol = ssl_enable ? "https" : "http";
            if(ssl_enable){
                SSLHelper.initialize(ssl_allow_self_signed, ssl_verify_hostname);
            }

            final URL url = new URL(protocol, xmldbURI.getHost(), xmldbURI.getPort(), xmldbURI.getContext());

            final XmlRpcClient rpcClient = getRpcClient(user, password, url);
            return readCollection(xmldbURI.getRawCollectionPath(), rpcClient);

        } catch(final MalformedURLException e) {
            //Should never happen
            throw new XMLDBException(ErrorCodes.INVALID_DATABASE, e.getMessage());
        } catch(final XMLDBException e) {
            switch (e.errorCode) {
              case ErrorCodes.NO_SUCH_RESOURCE:
              case ErrorCodes.NO_SUCH_COLLECTION:
              case ErrorCodes.INVALID_COLLECTION:
              case ErrorCodes.INVALID_RESOURCE:
                LOG.info(e.getMessage());
                return null;
              default:
                LOG.error(e.getMessage(), e);
                throw e;
            }
        }
    }

    public static Collection readCollection(final String c, final XmlRpcClient rpcClient) throws XMLDBException {
        final XmldbURI path;
        try {
            path = XmldbURI.xmldbUriFor(c);
        } catch (final URISyntaxException e) {
            throw new XMLDBException(ErrorCodes.INVALID_URI,e);
        }
       
        final XmldbURI[] components = path.getPathSegments();
        if(components.length == 0) {
            throw new XMLDBException(ErrorCodes.NO_SUCH_COLLECTION, "Could not find collection: " + path.toString());
        }
       
        XmldbURI rootName = components[0];
        if(XmldbURI.RELATIVE_ROOT_COLLECTION_URI.equals(rootName)) {
            rootName = XmldbURI.ROOT_COLLECTION_URI;
        }
       
        Collection current = RemoteCollection.instance(rpcClient, rootName);
        for(int i = 1 ; i < components.length ; i++) {
            current = ((RemoteCollection)current).getChildCollection(components[i]);
            if (current == null) {
                throw new XMLDBException(ErrorCodes.NO_SUCH_COLLECTION , "Could not find collection: " + c);
            }
        }
        return current;
    }

  /**
   * @param user
   * @param pool
   * @return the User object corresponding to the username in <code>user</code>
   * @throws XMLDBException
   */
  private Subject getUser(String user, String password, BrokerPool pool) throws XMLDBException {
    if (user == null) {
      user = SecurityManager.GUEST_USER;
      password = SecurityManager.GUEST_USER;
    }
    final SecurityManager securityManager = pool.getSecurityManager();
    try {
        return securityManager.authenticate(user, password);
  } catch (final AuthenticationException e) {
    throw new XMLDBException(ErrorCodes.PERMISSION_DENIED, e.getMessage(), e);
  }
  }

  /**
   * RpcClients are cached by address+user. The password is transparently changed.
   * @param user
   * @param password
   * @param url
   * @throws XMLDBException
   */
  private XmlRpcClient getRpcClient(String user, String password, URL url) throws XMLDBException {
      final String key = user + "@" + url.toString();
      XmlRpcClient client = rpcClients.get(key);
      final XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
      config.setEnabledForExtensions(true);
      config.setServerURL(url);
      config.setBasicUserName(user);
      config.setBasicPassword(password);
      if (client == null) {
          client = new XmlRpcClient();
          rpcClients.put(key, client);
      }
      client.setConfig(config);
      return client;
  }

  /**
   * Register a ShutdownListener for the current database instance. The ShutdownListener is called
     * after the database has shut down. You have to register a listener before any calls to getCollection().
   *
   * @param listener
   * @throws XMLDBException
   */
  public void setDatabaseShutdownListener(ShutdownListener listener) throws XMLDBException {
    shutdown = listener;
  }

  public String getConformanceLevel() throws XMLDBException {
        //TODO : what is to be returned here ? -pb
    return "0";
  }

  //WARNING : returning such a default value is dangerous IMHO ? -pb
  /** @deprecated */
  public String getName() throws XMLDBException {
    return (currentInstanceName != null) ? currentInstanceName : "exist";
  }

    //WARNING : returning such *a* default value is dangerous IMHO ? -pb
  public String[] getNames() throws XMLDBException {
    return new String[] { (currentInstanceName != null) ? currentInstanceName : "exist" };
  }

    public String getProperty(String property) throws XMLDBException {
       
        if ("create-database".equals(property)) {
            //TODO : rename ?
            return Boolean.valueOf(autoCreate).toString();
        }
       
        if ("database-id".equals(property))
        {
            //TODO : consider multivalued property
            return currentInstanceName;
        }

        if ("configuration".equals(property)) {
            return configuration;
        }

        if ("ssl-enable".equals(property)) {
            return "" + ssl_enable;
        }
       
        if ("ssl-allow-self-signed".equals(property)) {
            return "" + ssl_allow_self_signed;
        }
       
        if ("ssl-verify-hostname".equals(property)) {
            return "" + ssl_allow_self_signed;
        }
       
        return null;
    }

    public void setProperty(String property, String value) throws XMLDBException {

        if ("create-database".equals(property)) {
            autoCreate = "true".equals(value);
        }
       
        //TODO : rename ?
        if ("database-id".equals(property))
        {
            //TODO : consider multivalued property
            currentInstanceName = value;
        }

        if ("configuration".equals(property)) {
            configuration = value;
        }

        if ("ssl-enable".equals(property)) {
            ssl_enable = Boolean.valueOf(value);
        }

        if ("ssl-allow-self-signed".equals(property)) {
            ssl_allow_self_signed = Boolean.valueOf(value);
        }

        if ("ssl-verify-hostname".equals(property)) {
            ssl_verify_hostname = Boolean.valueOf(value);
        }

    }
}
TOP

Related Classes of org.exist.xmldb.DatabaseImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.