Package com.sun.xml.ws.client.sei

Source Code of com.sun.xml.ws.client.sei.ResponseBuilder$ImageBuilder

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License").  You
* may not use this file except in compliance with the License.  You can
* obtain a copy of the License at
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt.  See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license."  If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above.  However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/

package com.sun.xml.ws.client.sei;

import com.sun.xml.ws.api.SOAPVersion;
import com.sun.xml.ws.api.message.Attachment;
import com.sun.xml.ws.api.message.AttachmentSet;
import com.sun.xml.ws.api.message.Message;
import com.sun.xml.ws.api.model.ParameterBinding;
import com.sun.xml.ws.api.streaming.XMLStreamReaderFactory;
import com.sun.xml.ws.message.AttachmentUnmarshallerImpl;
import com.sun.xml.ws.model.ParameterImpl;
import com.sun.xml.ws.model.WrapperParameter;
import com.sun.xml.ws.resources.ServerMessages;
import com.sun.xml.ws.spi.db.RepeatedElementBridge;
import com.sun.xml.ws.spi.db.XMLBridge;
import com.sun.xml.ws.spi.db.DatabindingException;
import com.sun.xml.ws.spi.db.PropertyAccessor;
import com.sun.xml.ws.spi.db.WrapperComposite;
import com.sun.xml.ws.streaming.XMLStreamReaderUtil;
import com.sun.xml.ws.encoding.StringDataContentHandler;
import com.sun.xml.ws.encoding.DataHandlerDataSource;

import javax.activation.DataHandler;
import javax.imageio.ImageIO;
import javax.xml.bind.JAXBException;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFault;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.transform.Source;
import javax.xml.ws.Holder;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.soap.SOAPFaultException;
import java.awt.Image;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
* Reads a response {@link Message}, disassembles it, and moves obtained Java values
* to the expected places.
*
* @author Kohsuke Kawaguchi
* @author Jitendra Kotamraju
*/
public abstract class ResponseBuilder {
    /**
     * Reads a response {@link Message}, disassembles it, and moves obtained Java values
     * to the expected places.
     *
     * @param reply
     *      The reply {@link Message} to be de-composed.
     * @param args
     *      The Java arguments given to the SEI method invocation.
     *      Some parts of the reply message may be set to {@link Holder}s in the arguments.
     * @return
     *      If a part of the reply message is returned as a return value from
     *      the SEI method, this method returns that value. Otherwise null.
     * @throws JAXBException
     *      if there's an error during unmarshalling the reply message.
     * @throws XMLStreamException
     *      if there's an error during unmarshalling the reply message.
     */
    public abstract Object readResponse(Message reply, Object[] args) throws JAXBException, XMLStreamException;
   
    static final class WrappedPartBuilder {
        private final XMLBridge bridge;
        private final ValueSetter setter;
        public WrappedPartBuilder(XMLBridge bridge, ValueSetter setter) {
            this.bridge = bridge;
            this.setter = setter;
        }
        final Object readResponse(Object[] args, XMLStreamReader r, AttachmentSet att) throws JAXBException {
            Object obj;
            AttachmentUnmarshallerImpl au = (att != null)?new AttachmentUnmarshallerImpl(att):null;
            if (bridge instanceof RepeatedElementBridge) {
                RepeatedElementBridge rbridge = (RepeatedElementBridge)bridge;
                ArrayList list = new ArrayList();
                QName name = r.getName();
                while (r.getEventType()==XMLStreamReader.START_ELEMENT && name.equals(r.getName())) {
                    list.add(rbridge.unmarshal(r, au));
                    XMLStreamReaderUtil.toNextTag(r, name);
                }
                obj = rbridge.collectionHandler().convert(list);
            } else {
                obj = bridge.unmarshal(r, au);
            }
            return setter.put(obj,args);
        }
    }
    /**
     * {@link ResponseBuilder.PartBuilder} keyed by the element name (inside the wrapper element.)
     */
    protected Map<QName,WrappedPartBuilder> wrappedParts = null;
    protected QName wrapperName;
   
    protected Object readWrappedResponse(Message msg, Object[] args) throws JAXBException, XMLStreamException {
        Object retVal = null;

        if (!msg.hasPayload()) {
            throw new WebServiceException("No payload. Expecting payload with "+wrapperName+" element");
        }
        XMLStreamReader reader = msg.readPayload();
        XMLStreamReaderUtil.verifyTag(reader,wrapperName);
        reader.nextTag();

        while(reader.getEventType()==XMLStreamReader.START_ELEMENT) {
            // TODO: QName has a performance issue
            WrappedPartBuilder part = wrappedParts.get(reader.getName());
            if(part==null) {
                // no corresponding part found. ignore
                XMLStreamReaderUtil.skipElement(reader);
                reader.nextTag();
            } else {
                Object o = part.readResponse(args,reader, msg.getAttachments());
                // there's only at most one ResponseBuilder that returns a value.
                if(o!=null) {
                    assert retVal==null;
                    retVal = o;
                }
            }
            // skip any whitespace
            if (reader.getEventType() != XMLStreamConstants.START_ELEMENT &&
                    reader.getEventType() != XMLStreamConstants.END_ELEMENT) {
                XMLStreamReaderUtil.nextElementContent(reader);
            }
        }

        // we are done with the body
        reader.close();
        XMLStreamReaderFactory.recycle(reader);

        return retVal;
    }

    static final class None extends ResponseBuilder {
        private None(){
        }
        @Override
        public Object readResponse(Message msg, Object[] args) {          
            msg.consume();
            return null;
        }
    }

    /**
     * The singleton instance that produces null return value.
     * Used for operations that doesn't have any output.
     */
    public final static ResponseBuilder NONE = new None();

    /**
     * Returns the 'uninitialized' value for the given type.
     *
     * <p>
     * For primitive types, it's '0', and for reference types, it's null.
     */
    @SuppressWarnings("element-type-mismatch")
    public static Object getVMUninitializedValue(Type type) {
        // if this map returns null, that means the 'type' is a reference type,
        // in which case 'null' is the correct null value, so this code is correct.
        return primitiveUninitializedValues.get(type);
    }

    private static final Map<Class,Object> primitiveUninitializedValues = new HashMap<Class, Object>();

    static {
        Map<Class, Object> m = primitiveUninitializedValues;
        m.put(int.class,(int)0);
        m.put(char.class,(char)0);
        m.put(byte.class,(byte)0);
        m.put(short.class,(short)0);
        m.put(long.class,(long)0);
        m.put(float.class,(float)0);
        m.put(double.class,(double)0);
    }

    /**
     * {@link ResponseBuilder} that sets the VM uninitialized value to the type.
     */
    public static final class NullSetter extends ResponseBuilder {
        private final ValueSetter setter;
        private final Object nullValue;

        public NullSetter(ValueSetter setter, Object nullValue){
            assert setter!=null;
            this.nullValue = nullValue;
            this.setter = setter;
        }
        @Override
        public Object readResponse(Message msg, Object[] args) {
            return setter.put(nullValue, args);
        }
    }

    /**
     * {@link ResponseBuilder} that is a composition of multiple
     * {@link ResponseBuilder}s.
     *
     * <p>
     * Sometimes we need to look at multiple parts of the reply message
     * (say, two header params, one body param, and three attachments, etc.)
     * and that's when this object is used to combine multiple {@link ResponseBuilder}s
     * (that each responsible for handling one part).
     *
     * <p>
     * The model guarantees that only at most one {@link ResponseBuilder} will
     * return a value as a return value (and everything else has to go to
     * {@link Holder}s.)
     */
    public static final class Composite extends ResponseBuilder {
        private final ResponseBuilder[] builders;

        public Composite(ResponseBuilder... builders) {
            this.builders = builders;
        }

        public Composite(Collection<? extends ResponseBuilder> builders) {
            this(builders.toArray(new ResponseBuilder[builders.size()]));
        }

        @Override
        public Object readResponse(Message msg, Object[] args) throws JAXBException, XMLStreamException {
            Object retVal = null;
            for (ResponseBuilder builder : builders) {
                Object r = builder.readResponse(msg,args);
                // there's only at most one ResponseBuilder that returns a value.
                if(r!=null) {
                    assert retVal==null;
                    retVal = r;
                }
            }
            return retVal;
        }
    }
   
    /**
     * Reads an Attachment into a Java parameter.
     */
    public static abstract class AttachmentBuilder extends ResponseBuilder {
        protected final ValueSetter setter;
        protected final ParameterImpl param;
        private final String pname;
        private final String pname1;
           
        AttachmentBuilder(ParameterImpl param, ValueSetter setter) {
            this.setter = setter;
            this.param = param;
            this.pname = param.getPartName();
            this.pname1 = "<"+pname;
        }

        /**
         * Creates an AttachmentBuilder based on the parameter type
         *
         * @param param
         *      runtime Parameter that abstracts the annotated java parameter
         * @param setter
         *      specifies how the obtained value is set into the argument. Takes
         *      care of Holder arguments.
         */
        public static ResponseBuilder createAttachmentBuilder(ParameterImpl param, ValueSetter setter) {
            Class type = (Class)param.getTypeInfo().type;
            if (DataHandler.class.isAssignableFrom(type)) {
                return new DataHandlerBuilder(param, setter);
            } else if (byte[].class==type) {
                return new ByteArrayBuilder(param, setter);
            } else if(Source.class.isAssignableFrom(type)) {
                return new SourceBuilder(param, setter);
            } else if(Image.class.isAssignableFrom(type)) {
                return new ImageBuilder(param, setter);
            } else if(InputStream.class==type) {
                return new InputStreamBuilder(param, setter);
            } else if(isXMLMimeType(param.getBinding().getMimeType())) {
                return new JAXBBuilder(param, setter);
            } else if(String.class.isAssignableFrom(type)) {
                return new StringBuilder(param, setter);
            } else {
                throw new UnsupportedOperationException("Unexpected Attachment type ="+type);
            }
        }
       
        @Override
        public Object readResponse(Message msg, Object[] args) throws JAXBException, XMLStreamException {
            // TODO not to loop
            for (Attachment att : msg.getAttachments()) {
                String part = getWSDLPartName(att);
                if (part == null) {
                    continue;
                }
                if(part.equals(pname) || part.equals(pname1)){
                    return mapAttachment(att, args);
                }
            }
            return null;
        }
       
        abstract Object mapAttachment(Attachment att, Object[] args) throws JAXBException;
    }
       
    private static final class DataHandlerBuilder extends AttachmentBuilder {
        DataHandlerBuilder(ParameterImpl param, ValueSetter setter) {
            super(param, setter);
        }
       
        @Override
        Object mapAttachment(Attachment att, Object[] args) {
            return setter.put(att.asDataHandler(), args);
        }
    }

    private static final class StringBuilder extends AttachmentBuilder {
        StringBuilder(ParameterImpl param, ValueSetter setter) {
            super(param, setter);
        }

        @Override
        Object mapAttachment(Attachment att, Object[] args) {
            att.getContentType();
            StringDataContentHandler sdh = new StringDataContentHandler();
            try {
                String str = (String)sdh.getContent(new DataHandlerDataSource(att.asDataHandler()));
                return setter.put(str, args);
            } catch(Exception e) {
                throw new WebServiceException(e);
            }

        }
    }

    private static final class ByteArrayBuilder extends AttachmentBuilder {
        ByteArrayBuilder(ParameterImpl param, ValueSetter setter) {
            super(param, setter);
        }
       
        @Override
        Object mapAttachment(Attachment att, Object[] args) {
            return setter.put(att.asByteArray(), args);
        }
    }
       
    private static final class SourceBuilder extends AttachmentBuilder {
        SourceBuilder(ParameterImpl param, ValueSetter setter) {
            super(param, setter);
        }
       
        @Override
        Object mapAttachment(Attachment att, Object[] args) {
            return setter.put(att.asSource(), args);
        }
    }
       
    private static final class ImageBuilder extends AttachmentBuilder {
        ImageBuilder(ParameterImpl param, ValueSetter setter) {
            super(param, setter);
        }
       
        @Override
        Object mapAttachment(Attachment att, Object[] args) {
            Image image;
            InputStream is = null;
            try {
                is = att.asInputStream();
                image = ImageIO.read(is);
            } catch(IOException ioe) {
                throw new WebServiceException(ioe);
            } finally {
                if (is != null) {
                    try {
                        is.close();
                    } catch(IOException ioe) {
                        throw new WebServiceException(ioe);
                    }
                }
            }
            return setter.put(image, args);
        }
    }
       
    private static final class InputStreamBuilder extends AttachmentBuilder {
        InputStreamBuilder(ParameterImpl param, ValueSetter setter) {
            super(param, setter);
        }
       
        @Override
        Object mapAttachment(Attachment att, Object[] args) {
            return setter.put(att.asInputStream(), args);
        }
    }
       
    private static final class JAXBBuilder extends AttachmentBuilder {
        JAXBBuilder(ParameterImpl param, ValueSetter setter) {
            super(param, setter);
        }
       
        @Override
        Object mapAttachment(Attachment att, Object[] args) throws JAXBException {
            Object obj = param.getXMLBridge().unmarshal(att.asInputStream());
            return setter.put(obj, args);
        }
    }
   
    /**
     * Gets the WSDL part name of this attachment.
     *
     * <p>
     * According to WSI AP 1.0
     * <PRE>
     * 3.8 Value-space of Content-Id Header
     *   Definition: content-id part encoding
     *   The "content-id part encoding" consists of the concatenation of:
     * The value of the name attribute of the wsdl:part element referenced by the mime:content, in which characters disallowed in content-id headers (non-ASCII characters as represented by code points above 0x7F) are escaped as follows:
     *     o Each disallowed character is converted to UTF-8 as one or more bytes.
     *     o Any bytes corresponding to a disallowed character are escaped with the URI escaping mechanism (that is, converted to %HH, where HH is the hexadecimal notation of the byte value).
     *     o The original character is replaced by the resulting character sequence.
     * The character '=' (0x3D).
     * A globally unique value such as a UUID.
     * The character '@' (0x40).
     * A valid domain name under the authority of the entity constructing the message.
     * </PRE>
     *
     * So a wsdl:part fooPart will be encoded as:
     *      <fooPart=somereallybignumberlikeauuid@example.com>
     *
     * @return null
     *      if the parsing fails.
     */
    @SuppressWarnings("FinalStaticMethod")
    public static final String getWSDLPartName(com.sun.xml.ws.api.message.Attachment att){
        String cId = att.getContentId();

        int index = cId.lastIndexOf('@', cId.length());
        if(index == -1){
            return null;
        }
        String localPart = cId.substring(0, index);
        index = localPart.lastIndexOf('=', localPart.length());
        if(index == -1){
            return null;
        }
        try {
            return java.net.URLDecoder.decode(localPart.substring(0, index), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new WebServiceException(e);
        }
    }


    /**
     * Reads a header into a JAXB object.
     */
    public static final class Header extends ResponseBuilder {
        private final XMLBridge<?> bridge;
        private final ValueSetter setter;
        private final QName headerName;
        private final SOAPVersion soapVersion;

        /**
         * @param soapVersion
         *      SOAP1.1 or 1.2
         * @param name
         *      The name of the header element.
         * @param bridge
         *      specifies how to unmarshal a header into a JAXB object.
         * @param setter
         *      specifies how the obtained value is returned to the client.
         */
        public Header(SOAPVersion soapVersion, QName name, XMLBridge<?> bridge, ValueSetter setter) {
            this.soapVersion = soapVersion;
            this.headerName = name;
            this.bridge = bridge;
            this.setter = setter;
        }

        public Header(SOAPVersion soapVersion, ParameterImpl param, ValueSetter setter) {
            this(soapVersion,
                param.getTypeInfo().tagName,
                param.getXMLBridge(),
                setter);
            assert param.getOutBinding()== ParameterBinding.HEADER;
        }

        private SOAPFaultException createDuplicateHeaderException() {
            try {
                SOAPFault fault = soapVersion.getSOAPFactory().createFault();
                fault.setFaultCode(soapVersion.faultCodeServer);
                fault.setFaultString(ServerMessages.DUPLICATE_PORT_KNOWN_HEADER(headerName));
                return new SOAPFaultException(fault);
            } catch(SOAPException e) {
                throw new WebServiceException(e);
            }
        }

        @Override
        public Object readResponse(Message msg, Object[] args) throws JAXBException {
            com.sun.xml.ws.api.message.Header header = null;
            Iterator<com.sun.xml.ws.api.message.Header> it =
                msg.getHeaders().getHeaders(headerName,true);
            if (it.hasNext()) {
                header = it.next();
                if (it.hasNext()) {
                    throw createDuplicateHeaderException();
                }
            }

            if(header!=null)
                return setter.put( header.readAsJAXB(bridge), args );
            else
                // header not found.
                return null;
        }
    }

    /**
     * Reads the whole payload into a single JAXB bean.
     */
    public static final class Body extends ResponseBuilder {
        private final XMLBridge<?> bridge;
        private final ValueSetter setter;

        /**
         * @param bridge
         *      specifies how to unmarshal the payload into a JAXB object.
         * @param setter
         *      specifies how the obtained value is returned to the client.
         */
        public Body(XMLBridge<?> bridge, ValueSetter setter) {
            this.bridge = bridge;
            this.setter = setter;
        }

        @Override
        public Object readResponse(Message msg, Object[] args) throws JAXBException {
            return setter.put( msg.readPayloadAsJAXB(bridge), args );
        }
    }

    /**
     * Treats a payload as multiple parts wrapped into one element,
     * and processes all such wrapped parts.
     */
    public static final class DocLit extends ResponseBuilder {
        /**
         * {@link PartBuilder} keyed by the element name (inside the wrapper element.)
         */
        private final PartBuilder[] parts;

        private final XMLBridge wrapper;

        private boolean dynamicWrapper;

        public DocLit(WrapperParameter wp, ValueSetterFactory setterFactory) {
            wrapperName = wp.getName();
            wrapper = wp.getXMLBridge();
            Class wrapperType = (Class) wrapper.getTypeInfo().type;    
            dynamicWrapper = WrapperComposite.class.equals(wrapperType);

            List<PartBuilder> tempParts = new ArrayList<PartBuilder>();

            List<ParameterImpl> children = wp.getWrapperChildren();
            for (ParameterImpl p : children) {
                if(p.isIN())
                    continue;
                QName name = p.getName();
                if (dynamicWrapper) {
                    if (wrappedParts == null) wrappedParts = new HashMap<QName,WrappedPartBuilder>();
                    XMLBridge xmlBridge = p.getInlinedRepeatedElementBridge();
                    if (xmlBridge == null) xmlBridge = p.getXMLBridge();
                    wrappedParts.put( p.getName(), new WrappedPartBuilder(xmlBridge, setterFactory.get(p)));
                } else {               
                    try {                       
                        tempParts.add(new PartBuilder(
                                    wp.getOwner().getBindingContext().getElementPropertyAccessor(
                                        wrapperType,
                                        name.getNamespaceURI(),
                                        p.getName().getLocalPart()),
                                    setterFactory.get(p)
                                ));
                        // wrapper parameter itself always bind to body, and
                        // so do all its children
                        assert p.getBinding()== ParameterBinding.BODY;
                    } catch (JAXBException e) {
                        throw new WebServiceException// TODO: i18n
                            wrapperType+" do not have a property of the name "+name,e);
                    }
                }
            }
            this.parts = tempParts.toArray(new PartBuilder[tempParts.size()]);
        }

        @Override
        public Object readResponse(Message msg, Object[] args) throws JAXBException, XMLStreamException {
            if (dynamicWrapper) return readWrappedResponse(msg, args);
            Object retVal = null;

            if (parts.length>0) {
                if (!msg.hasPayload()) {
                    throw new WebServiceException("No payload. Expecting payload with "+wrapperName+" element");
                }
                XMLStreamReader reader = msg.readPayload();
                XMLStreamReaderUtil.verifyTag(reader,wrapperName);
                Object wrapperBean = wrapper.unmarshal(reader, (msg.getAttachments() != null) ?
                    new AttachmentUnmarshallerImpl(msg.getAttachments()): null);

                try {
                    for (PartBuilder part : parts) {
                        Object o = part.readResponse(args,wrapperBean);
                        // there's only at most one ResponseBuilder that returns a value.
                        // TODO: reorder parts so that the return value comes at the end.
                        if(o!=null) {
                            assert retVal==null;
                            retVal = o;
                        }
                    }
                } catch (DatabindingException e) {
                    // this can happen when the set method throw a checked exception or something like that
                    throw new WebServiceException(e);    // TODO:i18n
                }

                // we are done with the body
                reader.close();
                XMLStreamReaderFactory.recycle(reader);
            } else {
                msg.consume();
            }

            return retVal;
        }

        /**
         * Unmarshals each wrapped part into a JAXB object and moves it
         * to the expected place.
         */
        static final class PartBuilder {
            private final PropertyAccessor accessor;
            private final ValueSetter setter;

            /**
             * @param accessor
             *      specifies which portion of the wrapper bean to obtain the value from.
             * @param setter
             *      specifies how the obtained value is returned to the client.
             */
            public PartBuilder(PropertyAccessor accessor, ValueSetter setter) {
                this.accessor = accessor;
                this.setter = setter;
                assert accessor!=null && setter!=null;
            }

            final Object readResponse( Object[] args, Object wrapperBean ) {
                Object obj = accessor.get(wrapperBean);
                return setter.put(obj,args);
            }


        }
    }

    /**
     * Treats a payload as multiple parts wrapped into one element,
     * and processes all such wrapped parts.
     */
    public static final class RpcLit extends ResponseBuilder {
        public RpcLit(WrapperParameter wp, ValueSetterFactory setterFactory) {
            assert wp.getTypeInfo().type== WrapperComposite.class;
            wrapperName = wp.getName();
            wrappedParts = new HashMap<QName,WrappedPartBuilder>();
            List<ParameterImpl> children = wp.getWrapperChildren();
            for (ParameterImpl p : children) {
                wrappedParts.put( p.getName(), new WrappedPartBuilder(
                    p.getXMLBridge(), setterFactory.get(p)
                ));
                // wrapper parameter itself always bind to body, and
                // so do all its children
                assert p.getBinding()== ParameterBinding.BODY;
            }
        }

        @Override
        public Object readResponse(Message msg, Object[] args) throws JAXBException, XMLStreamException {
            return readWrappedResponse(msg, args);
        }
    }
   
    private static boolean isXMLMimeType(String mimeType){
        return mimeType.equals("text/xml") || mimeType.equals("application/xml");
    }
}
TOP

Related Classes of com.sun.xml.ws.client.sei.ResponseBuilder$ImageBuilder

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.