Package org.apache.xindice.client.xmldb.xmlrpc

Source Code of org.apache.xindice.client.xmldb.xmlrpc.CollectionImpl

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $Id: CollectionImpl.java 511426 2007-02-25 03:25:02Z vgritsenko $
*/

package org.apache.xindice.client.xmldb.xmlrpc;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xindice.client.xmldb.ResourceSetImpl;
import org.apache.xindice.client.xmldb.XindiceCollection;
import org.apache.xindice.client.xmldb.resources.XMLResourceImpl;
import org.apache.xindice.client.xmldb.resources.BinaryResourceImpl;
import org.apache.xindice.core.FaultCodes;
import org.apache.xindice.core.meta.MetaData;
import org.apache.xindice.server.rpc.RPCDefaultMessage;
import org.apache.xindice.server.rpc.RPCMessageInterface;
import org.apache.xindice.util.SymbolDeserializer;
import org.apache.xindice.xml.TextWriter;
import org.apache.xindice.xml.dom.DOMParser;
import org.apache.xindice.xml.dom.DocumentImpl;
import org.apache.xmlrpc.XmlRpcClient;
import org.apache.xmlrpc.XmlRpcException;

import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xmldb.api.base.Collection;
import org.xmldb.api.base.ErrorCodes;
import org.xmldb.api.base.Resource;
import org.xmldb.api.base.ResourceSet;
import org.xmldb.api.base.XMLDBException;
import org.xmldb.api.modules.XMLResource;

import javax.xml.parsers.DocumentBuilderFactory;
import java.io.IOException;
import java.io.StringReader;
import java.util.Hashtable;
import java.util.Vector;
import java.util.StringTokenizer;

/**
* Implementation of XML:DB's <code>Collection</code> interface using
* XML-RPC to interact with database server
*
* @author <a href="mailto:james.bates@amplexor.com">James Bates</a>
* @author <a href="mailto:kstaken@xmldatabases.org">Kimbro Staken</a>
* @version $Revision: 511426 $, $Date: 2007-02-24 22:25:02 -0500 (Sat, 24 Feb 2007) $
*/
public class CollectionImpl extends XindiceCollection {

    private static final Log log = LogFactory.getLog(CollectionImpl.class);

    /**
     * The XML-RPC client stub, connected to the server
     */
    private XmlRpcClient client = null;

    /**
     * Creates new <code>CollectionImpl</code> instance representing connection
     * to server collection.
     *
     * @param client XML-RPC client connected to the Xindice XML-RPC server.
     * @param collPath is the name of the collection to open.
     * @exception XMLDBException thrown if a connection could not be established,
     *            because of URL syntax errors, or connection failure, or if no
     *            collection with path <code>collPath</code> could be located.
     */
    public CollectionImpl(XmlRpcClient client, String collPath) throws XMLDBException {
        super(collPath);
        this.client = client;

        /* Just check the collection does actually exist */
        Hashtable params = new Hashtable();
        params.put(RPCDefaultMessage.COLLECTION, collPath);
        String exists = (String) runRemoteCommand("GetCollectionConfiguration", params);

        if (!"yes".equals(exists)) {
            throw new XMLDBException(ErrorCodes.NO_SUCH_COLLECTION,
                                     FaultCodes.COL_COLLECTION_NOT_FOUND,
                                     "Collection not found: " + collPath);
        }
    }

    /**
     * Submits a command for RPC to database server
     *
     * @param cmdName command name
     * @param params hashtable containing named parameters to send to server
     * @return the return value from the server. Type of return value depends on
     *         command.
     *
     * @exception XMLDBException thrown if XML-RPC reports an exception.
     */
    private Object runRemoteCommand(String cmdName, Hashtable params) throws XMLDBException {
        try {
            params.put(RPCMessageInterface.MESSAGE_PARAM, cmdName);
            Vector v = new Vector();
            v.add(params);
            return ((Hashtable) client.execute("run", v)).get(RPCDefaultMessage.RESULT);
        } catch (XmlRpcException e) {
            if (log.isDebugEnabled()) {
                log.debug("Got XmlRpc exception running command " + cmdName + ", code: " + e.code + ", msg: " + e.getMessage());
            }

            if (e.code != 0) {
                throw new XMLDBException(e.code /  FaultCodes.MAX_CODE,
                                         e.code % FaultCodes.MAX_CODE,
                                         e.getMessage());
            }

            throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, FaultCodes.GEN_GENERAL_ERROR,
                                     "Failed to execute command '" + cmdName + "' on server: " + client.getURL() + ", message: " + e.getMessage(), e);
        } catch (IOException e) {
            throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, FaultCodes.GEN_GENERAL_ERROR,
                                     "Cannot communicate with the server: " + client.getURL(), e);
        }
    }

    /**
     * Retrieves a <code>Resource</code> from the database. If the
     * <code>Resource</code> could not be
     * located a null value will be returned.
     *
     * @param id the unique id for the requested resource.
     * @return The retrieved <code>Resource</code> instance.
     * @exception XMLDBException with expected error codes.<br />
     <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
     *  specific errors that occur.<br />
     <code>ErrorCodes.COLLECTION_CLOSED</code> if the <code>close</code>
     *  method has been called on the <code>Collection</code><br />
     */
    public Resource getResource(String id) throws XMLDBException {
        checkOpen();

        try {
            if (id == null) {
                return null;
            }

            Hashtable params = new Hashtable();
            params.put(RPCDefaultMessage.COLLECTION, collPath);
            params.put(RPCDefaultMessage.NAME, id);
            params.put(RPCDefaultMessage.COMPRESSED, "true");

            Object result = runRemoteCommand("GetResource", params);

            if (result == null) {
                // No resource found
                return null;
            } else if (result instanceof Hashtable) {
                // Result is compressed XML.
                Hashtable compressed = (Hashtable) result;
                SymbolDeserializer symbolDeserial = new SymbolDeserializer();
                return new XMLResourceImpl(id, id, this, symbolDeserial.getSymbols(compressed), (byte[]) compressed.get("document"));
            } else if (result instanceof byte[]) {
                // Result is binary.
                return new BinaryResourceImpl(id, this, (byte[]) result);
            } else {
                // Result is XML.
                return new XMLResourceImpl(id, this, (String) result);
            }

        } catch (XMLDBException x) {

            throw x;  // propagate any xmldb exception.
        } catch (Exception e) {

            throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, e);
        }
    }

    /**
     * Returns the number of resources currently stored in this collection or 0
     * if the collection is empty.
     *
     * @return the number of resource in the collection.
     * @exception XMLDBException with expected error codes.<br />
     <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
     *  specific errors that occur.<br />
     <code>ErrorCodes.COLLECTION_CLOSED</code> if the <code>close</code>
     *  method has been called on the <code>Collection</code><br />
     */
    public int getResourceCount() throws XMLDBException {

        checkOpen();
        try {

            Hashtable params = new Hashtable();
            params.put(RPCDefaultMessage.COLLECTION, collPath);
            return ((Integer) runRemoteCommand("GetDocumentCount", params)).intValue();
        } catch (XMLDBException x) {

            throw x;  // propagate any xmldb exception.
        } catch (Exception e) {

            throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, e);
        }
    }

    /**
     * Stores the provided resource into the database. If the resource does not
     * already exist it will be created. If it does already exist it will be
     * updated.
     *
     * @param res the resource to store in the database.
     * @exception XMLDBException with expected error codes.<br />
     <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
     *  specific errors that occur.<br />
     <code>ErrorCodes.INVALID_RESOURCE</code> if the <code>Resource</code> is
     *   not valid.
     *  <code>ErrorCodes.COLLECTION_CLOSED</code> if the <code>close</code>
     *  method has been called on the <code>Collection</code><br />
     */
    public void storeResource(Resource res) throws XMLDBException {

        if (res.getContent() == null) {
            throw new XMLDBException(ErrorCodes.INVALID_RESOURCE, "no resource data");
        }

        checkOpen();
        try {
            Hashtable params = new Hashtable();
            params.put(RPCDefaultMessage.COLLECTION, collPath);
            params.put(RPCDefaultMessage.NAME, res.getId());
            params.put(RPCDefaultMessage.DOCUMENT, res.getContent());

            String name = (String) runRemoteCommand("InsertResource", params);
            if (res instanceof XMLResource) {
                ((XMLResourceImpl) res).setId(name);
            } else {
                ((BinaryResourceImpl) res).setId(name);
            }

        } catch (XMLDBException x) {

            throw x;  // propagate any xmldb exception.
        } catch (Exception e) {

            throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, e);
        }
    }

    /* see superclass for documentation */
    public boolean isOpen() {

        return (client != null);
    }

    /* see superclass for documentation */
    public String getURI() {

        return "xmldb:" + DatabaseImpl.DRIVER_NAME + "://" +
                client.getURL().getHost() + ':' + client.getURL().getPort() +
                collPath;
    }

    /**
     * Returns a <code>Collection</code> instance for the requested child collection
     * if it exists.
     *
     * @param name the name of the child collection to retrieve.
     * @return the requested child collection or null if it couldn't be found.
     * @exception XMLDBException with expected error codes.<br />
     <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
     *  specific errors that occur.<br />
     <code>ErrorCodes.COLLECTION_CLOSED</code> if the <code>close</code>
     *  method has been called on the <code>Collection</code><br />
     */
    public Collection getChildCollection(String name) throws XMLDBException {

        if (name.indexOf('/') != -1) {
            throw new XMLDBException(ErrorCodes.INVALID_COLLECTION);
        }

        try {
            return new CollectionImpl(client, collPath + "/" + name);
        } catch (XMLDBException e) {

            if (e.errorCode == ErrorCodes.NO_SUCH_COLLECTION) {
                // per getChildCollection contract, return null if not found
                return null;
            }

            throw e;
        }
    }

    /**
     * Creates a new unique ID within the context of the <code>Collection</code>
     *
     * @return the created id as a string.
     * @exception XMLDBException with expected error codes.<br />
     <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
     *  specific errors that occur.<br />
     <code>ErrorCodes.COLLECTION_CLOSED</code> if the <code>close</code>
     *  method has been called on the <code>Collection</code><br />
     */
    public String createId() throws XMLDBException {

        checkOpen();
        try {

            Hashtable params = new Hashtable();
            params.put(RPCDefaultMessage.COLLECTION, collPath);
            return (String) runRemoteCommand("CreateNewOID", params);
        } catch (XMLDBException x) {

            throw x;  // propagate any xmldb exception.
        } catch (Exception e) {

            throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, e);
        }
    }

    /**
     * Releases all resources consumed by the <code>Collection</code>.
     * The <code>close</code> method must
     * always be called when use of a <code>Collection</code> is complete. It is
     * not safe to use a  <code>Collection</code> after the <code>close</code>
     * method has been called.
     *
     * @exception XMLDBException with expected error codes.<br />
     <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
     *  specific errors that occur.<br />
     */
    public void close() throws XMLDBException {
        client = null;
    }

    /**
     * Returns the parent collection for this collection or null if no parent
     * collection exists.
     *
     * @return the parent <code>Collection</code> instance.
     * @exception XMLDBException with expected error codes.<br />
     <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
     *  specific errors that occur.<br />
     <code>ErrorCodes.COLLECTION_CLOSED</code> if the <code>close</code>
     *  method has been called on the <code>Collection</code><br />
     */
    public Collection getParentCollection() throws XMLDBException {

        // If there's only one slash then it's the root.
        if (collPath.lastIndexOf("/") == 0) {
            return null;
        }

        try {
            return new CollectionImpl(client, collPath.substring(0, collPath.lastIndexOf('/')));
        } catch (XMLDBException e) {
            if (e.errorCode == ErrorCodes.NO_SUCH_COLLECTION) {
                // per getParentCollection contract, return null if no parent
                return null;
            }
            throw e;
        }
    }

    /**
     * Removes the <code>Resource</code> from the database.
     *
     * @param res the resource to remove.
     * @exception XMLDBException with expected error codes.<br />
     <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
     *  specific errors that occur.<br />
     <code>ErrorCodes.INVALID_RESOURCE</code> if the <code>Resource</code> is
     *   not valid.<br />
     <code>ErrorCodes.NO_SUCH_RESOURCE</code> if the <code>Resource</code> is
     *   not known to this <code>Collection</code>.
     *  <code>ErrorCodes.COLLECTION_CLOSED</code> if the <code>close</code>
     *  method has been called on the <code>Collection</code><br />
     */
    public void removeResource(Resource res) throws XMLDBException {

        if (res == null || res.getId() == null || res.getId().length() == 0) {
            // Query result resource will have null ID
            throw new XMLDBException(ErrorCodes.INVALID_RESOURCE,
                                     "Resource passed is null or its ID is empty.");
        }

        checkOpen();
        try {
            // TODO: Test BinaryResource workings
            Hashtable params = new Hashtable();
            params.put(RPCDefaultMessage.COLLECTION, collPath);
            params.put(RPCDefaultMessage.NAME, res.getId());
            runRemoteCommand("RemoveDocument", params);
        } catch (XMLDBException x) {

            throw x;  // propagate any xmldb exception.
        } catch (Exception e) {
            throw new XMLDBException(ErrorCodes.NO_SUCH_RESOURCE, e);
        }
    }

    /**
     * Returns a list of collection names naming all child collections
     * of the current collection. If no child collections exist an empty list is
     * returned.
     *
     * @return an array containing collection names for all child
     *      collections.
     * @exception XMLDBException with expected error codes.<br />
     <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
     *  specific errors that occur.<br />
     <code>ErrorCodes.COLLECTION_CLOSED</code> if the <code>close</code>
     *  method has been called on the <code>Collection</code><br />
     */
    public String[] listChildCollections() throws XMLDBException {

        checkOpen();
        try {

            Hashtable params = new Hashtable();
            params.put(RPCDefaultMessage.COLLECTION, collPath);
            Vector list = (Vector) runRemoteCommand("ListCollections", params);

            return (String[]) list.toArray(new String[list.size()]);
        } catch (XMLDBException x) {

            throw x;  // propagate any xmldb exception.
        } catch (Exception e) {

            throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, e);
        }
    }

    /**
     * Returns the number of child collections under this
     * <code>Collection</code> or 0 if no child collections exist.
     *
     * @return the number of child collections.
     * @exception XMLDBException with expected error codes.<br />
     <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
     *  specific errors that occur.<br />
     <code>ErrorCodes.COLLECTION_CLOSED</code> if the <code>close</code>
     *  method has been called on the <code>Collection</code><br />
     */
    public int getChildCollectionCount() throws XMLDBException {

        checkOpen();
        try {

            Hashtable params = new Hashtable();
            params.put(RPCDefaultMessage.COLLECTION, collPath);
            Integer result = (Integer) runRemoteCommand("GetCollectionCount", params);
            return result.intValue();

        } catch (XMLDBException x) {

            throw x;  // propagate any xmldb exception.
        } catch (Exception e) {
            throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, e);
        }
    }

    /**
     * Returns a list of the ids for all resources stored in the collection.
     *
     * @return a string array containing the names for all
     *  <code>Resource</code>s in the collection.
     * @exception XMLDBException with expected error codes.<br />
     <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
     *  specific errors that occur.<br />
     <code>ErrorCodes.COLLECTION_CLOSED</code> if the <code>close</code>
     *  method has been called on the <code>Collection</code><br />
     */
    public String[] listResources() throws XMLDBException {

        checkOpen();
        try {

            Hashtable params = new Hashtable();
            params.put(RPCDefaultMessage.COLLECTION, collPath);
            Vector list = (Vector) runRemoteCommand("ListDocuments", params);

            return (String[]) list.toArray(new String[list.size()]);
        } catch (XMLDBException x) {

            throw x;  // propagate any xmldb exception.
        } catch (Exception e) {

            throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, e);
        }
    }

    /* see superclass for documentation */
    public ResourceSet query(String name, String queryLang, String query, Hashtable nsMap) throws XMLDBException {

        checkOpen();
        try {
            Hashtable params = new Hashtable();
            params.put(RPCDefaultMessage.COLLECTION, collPath);
            params.put(RPCDefaultMessage.TYPE, queryLang);
            params.put(RPCDefaultMessage.NAMESPACES, nsMap);
            params.put(RPCDefaultMessage.QUERY, query);

            if (name != null) {
                params.put(RPCDefaultMessage.NAME, name);
            }

            String result = (String) runRemoteCommand("Query", params);
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true);
            Document resultDoc = dbf.newDocumentBuilder().parse(new InputSource(new StringReader(result)));

            ResourceSetImpl rs = new ResourceSetImpl(this, resultDoc);
            return rs;
        } catch (Exception e) {
            e.printStackTrace();
            throw FaultCodes.createXMLDBException(FaultCodes.QRY_PROCESSING_ERROR, "Query error", e);
        }
    }

    /* see superclass for documentation */
    public Collection createCollection(String name) throws XMLDBException {
        return createCollection(name, null);
    }

    // See superclass for documentation. Note that first argument is the path, not the name.
    public Collection createCollection(String path, Document configuration) throws XMLDBException {
        checkOpen();
        try {
            Hashtable params = new Hashtable();
            params.put(RPCDefaultMessage.COLLECTION, collPath);
            params.put(RPCDefaultMessage.NAME, path);
            if (configuration != null) {
                params.put(RPCDefaultMessage.CONFIGURATION, TextWriter.toString(configuration));
            }

            runRemoteCommand("CreateCollection", params);

            // Traverse path to get newly created collection
            org.xmldb.api.base.Collection col = this;
            if (path.indexOf("/") != -1) {
                StringTokenizer st = new StringTokenizer(path, "/");
                while (col != null && st.hasMoreTokens()) {
                    path = st.nextToken().trim();
                    if (path.length() == 0) {
                        continue;
                    }

                    if (st.hasMoreTokens()) {
                        col = col.getChildCollection(path);
                    } else {
                        break;
                    }
                }
            }

            return col.getChildCollection(path);
        } catch (XMLDBException x) {

            throw x;  // propagate any xmldb exception.
        } catch (Exception e) {
            throw new XMLDBException(ErrorCodes.INVALID_COLLECTION, "Cannot create child collection", e);
        }
    }

    /* see superclass for documentation */
    public void removeCollection(String childName) throws XMLDBException {

        if (null == childName || childName.length() == 0) {
            throw new XMLDBException(ErrorCodes.NO_SUCH_COLLECTION,
                                     FaultCodes.COL_COLLECTION_NOT_FOUND,
                                     "Cannot remove child collection '" + childName + "': Name is empty");
        }

        checkOpen();
        try {
            Hashtable params = new Hashtable();
            params.put(RPCDefaultMessage.COLLECTION, collPath);
            params.put(RPCDefaultMessage.NAME, childName);

            String result = (String) runRemoteCommand("RemoveCollection", params);
            if (!result.equals("yes")) {
                throw new XMLDBException(ErrorCodes.INVALID_COLLECTION,
                                         "Cannot remove child collection '" + childName + "'");
            }
        } catch (XMLDBException x) {
            throw x;  // propagate any xmldb exception.
        } catch (Exception e) {
            throw new XMLDBException(ErrorCodes.INVALID_COLLECTION,
                                     "Cannot remove child collection '" + childName + "'", e);
        }
    }

    /* see superclass for documentation */
    public String[] listIndexers() throws XMLDBException {
        checkOpen();
        try {
            Hashtable params = new Hashtable();
            params.put(RPCDefaultMessage.COLLECTION, collPath);
            Vector list = (Vector) runRemoteCommand("ListIndexers", params);

            return (String[]) list.toArray(new String[list.size()]);
        } catch (Exception e) {
            throw FaultCodes.createXMLDBException(e);
        }
    }

    /* see superclass for documentation */
    public void createIndexer(Document configuration) throws XMLDBException {
        checkOpen();
        try {
            Hashtable params = new Hashtable();
            params.put(RPCDefaultMessage.COLLECTION, collPath);
            params.put(RPCDefaultMessage.CONFIGURATION, TextWriter.toString(configuration));

            runRemoteCommand("CreateIndexer", params);
        } catch (Exception e) {
            throw FaultCodes.createXMLDBException(e);
        }
    }

    /* see superclass for documentation */
    public void dropIndexer(String name) throws XMLDBException {
        checkOpen();
        try {
            Hashtable params = new Hashtable();
            params.put(RPCDefaultMessage.COLLECTION, collPath);
            params.put(RPCDefaultMessage.NAME, name);

            runRemoteCommand("RemoveIndexer", params);
        } catch (Exception e) {
            throw FaultCodes.createXMLDBException(e);
        }
    }

    /* see superclass for documentation */
    public void shutdown() throws XMLDBException {
        checkOpen();
        try {
            Hashtable params = new Hashtable();

            runRemoteCommand("Shutdown", params);
        } catch (Exception e) {
            throw FaultCodes.createXMLDBException(e);
        }
    }

    public MetaData getMetaData(String id) throws XMLDBException {
        checkOpen();
        try {
            Hashtable params = new Hashtable();
            params.put(RPCDefaultMessage.COLLECTION, collPath);
            if (id != null) {
                params.put(RPCDefaultMessage.NAME, id);
            }
            params.put(RPCDefaultMessage.COMPRESSED, "true");

            Object result = runRemoteCommand(id == null ? "GetCollectionMeta" : "GetDocumentMeta", params);
            Document metaDoc = DOMParser.toDocument(result.toString());
            MetaData meta = new MetaData(id);
            meta.streamFromXML(metaDoc.getDocumentElement(), true);
            return meta;
        } catch (Exception e) {
            throw FaultCodes.createXMLDBException(e);
        }
    }

    public void setMetaData(String id, MetaData meta) throws XMLDBException {
        checkOpen();
        try {
            Hashtable params = new Hashtable();
            params.put(RPCDefaultMessage.COLLECTION, collPath);
            if (id != null) {
                params.put(RPCDefaultMessage.NAME, id);
            }
            params.put(RPCDefaultMessage.COMPRESSED, "true");

            Document doc = new DocumentImpl();
            doc.appendChild(meta.streamToXML(doc, true));
            params.put(RPCDefaultMessage.META, TextWriter.toString(doc));

            // Object result =
            runRemoteCommand(id == null ? "SetCollectionMeta" : "SetDocumentMeta", params);
            // Document metaDoc = DOMParser.toDocument(result.toString());
            // meta = new MetaData(id);
            // meta.streamFromXML(metaDoc.getDocumentElement(), true);
            // return meta;
        } catch (Exception e) {
            throw FaultCodes.createXMLDBException(e);
        }
    }
}
TOP

Related Classes of org.apache.xindice.client.xmldb.xmlrpc.CollectionImpl

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.