Marvin Services

Contents

Introduction

Marvin Services provides you seamless integration of third-party calculations into MarvinSketch, cxcalc, and via Chemical Terms into Instant JChem or JChem for Excel. Industrial standard solutions like SOAP with WSDL are supported along with the more lightweight XML-RPC or JSON-RPC protocols. Java based calculations can be called without server, directly from the jar file.

Easy to use graphical interface for convenient usage in MarvinSketch, a single configuration file for easy maintenance, and automatic integration to other products mentioned above.

Manage Marvin services

Configure services in MarvinSketch preferences with provided graphical user interface. The preferences tab offers all available service implementation with editor support. For custom service implementations or alternative editors, user-defined mapping can be used by placing a file named servicedescriptoreditormapping.xml to the user's ChemAxon directory - usually located in user home as .chemaxon or ChemAxon, depending on operating system. The file should be valid to the following XSD schema:

	<?xml version="1.0" encoding="utf-8"?>
	<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
	<xs:attribute name="class" type="xs:string" />
	<xs:attribute name="editor" type="xs:string" />
	<xs:complexType name="descriptorType">
	  <xs:attribute ref="class" />
	  <xs:attribute ref="editor" />
	</xs:complexType>
	<xs:element name="DescriptorEditorMapping">
	  <xs:complexType>
	    <xs:sequence>
	      <xs:element name="Descriptor" type="descriptorType" minOccurs="0" maxOccurs="unbounded" />
	    </xs:sequence>
	  </xs:complexType>
	</xs:element>
	</xs:schema>
		

The code attribute is the class name of the ServiceDescriptor implementation, and the editor attribute is the class name of the ServiceDescriptorEditor implementation.

Services saved by MarvinSketch are available by their names in cxcalc and as Chemical Terms function. Arguments also can be referred by their names set at the editor. Please note that the built-in editor has type restrictions that can be avoided by direct access of the Marvin Services API. The most important types such as MDocument, Molecule, String, Integer, Long, Double, Float and Boolean are available.

Service implementations

Although the most common services are already implemented, Marvin Services provides an extensible API to implement custom services. Please note that some implementations has type restrictions handled by the built-in editor. The supported service protocols listed below:

Local Service

Local service is for accessing third party java functions. It does not require server or network connection. Java Archive (JAR) file acts as "server", any public class with default constructor acts as "service", and all the public method can be called. Please mind that the build-in editor may hide methods that requires unsupported argument type. Direct access of the Marvin Services API does not have this restriction, any type can be used.

Local Service is the most easy way to embed third-party calculation to MarvinSketch application, cxcalc or Chemical Terms, however java coding is required to assemble the jar files. Also note that these services can not be accessed in non-java environment such as Marvin .NET or JChem for Excel.

Keep in mind that classes used via service call should be stateless, as each service call will create a new instance of the class by the default constructor before calling the function.

Local Service makes good use of the Alias and Description annotations. Any methods annotated can provide default names and description for services and arguments. Also, these aliases are available from cxcalc as well - so a default service and argument name can be guaranteed by the class author.

Calling Local Service from API

The following code snippet calls the Integer countAtoms(Molecule) function of example.services.SampleService class located in localserviceexample.jar.

	// input molecule
	Molecule input = MolImporter.importMol("c1ccncc1");

	// initialize descriptor
	LocalServiceDescriptor descriptor = new LocalServiceDescriptor();
	descriptor.setURL("/path/to/localserviceexample.jar");
	descriptor.setClassName("example.services.SampleService");
	descriptor.setMethodName("countAtoms");
	descriptor.addArgument(ServiceArgument.createArgument(new Molecule()));

	// asynchronous call
	descriptor.getServiceHandler().callService(descriptor, new AsyncCallback<Integer>() {

	    @Override
	    public void onSuccess(Integer result) {
	        System.out.println("Asynchronous call returned " + result);
	    }

	    @Override
	    public void onFailure(ServiceException caught) {
	        System.err.println("Asynchronous call failed.");
	    }
	}, input);

	// synchronized call
	Object result = null;
	try {
  	    result = descriptor.getServiceHandler().callService(descriptor, input);
	} catch (ServiceException e) {
	    System.err.println("Service call failed.");
	}
	System.out.println("Synchronized call returned " + result);
		
Use annotations to define default names and description

Local Service can look up default service and argument names, as well as description information from annotations. These values used in MarvinSketch when adding the Local Service to the services list by automatically completing the form. The values can be edited manually, but the defaults are always available from Chemical Terms or cxcalc - as well as the optionally overwritten ones. You can find a sample class can be used as a Local Service below. To download the sample service jar file with source, click here.

/*
 * Copyright (c) 1998-2012 ChemAxon Ltd. All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * ChemAxon. You shall not disclose such Confidential Information
 * and shall use it only in accordance with the terms of the agreements
 * you entered into with ChemAxon.
 *
 */
package example.services;

import chemaxon.formats.MolFormatException;
import chemaxon.formats.MolImporter;
import chemaxon.marvin.services.localservice.Alias;
import chemaxon.marvin.services.localservice.Description;
import chemaxon.struc.Molecule;

/**
 * This is a sample class to demonstrate how to write
 * classes for Marvin Services Local Service implementation.
 * @author Istvan Rabel
 */
public class SampleService {

    /**
     * Returns the number of atoms in the specified molecule
     * @param molecule the molecule being checked
     * @return the number of atoms in the molecule
     */
    /* 
     * (non-javadoc)
     * This method can be called as a LocalService from
     * Marvin Sketch, cxcalc and Chemical Terms.
     * Annotations are used to provide default names
     * for Service and arguments, as well as a description.
     */
    @Alias(name="AtomCount", params={"Structure"})
    @Description("Returns the number of atoms in the structure")
    public Integer countAtoms(Molecule molecule) {
        return molecule.getAtomCount();
    }
    
    /**
     * Returns a formatted (HTML) message with the number of
     * atoms in the molecule imported from argument.
     * @param moleculeString a string representation of a molecule
     * @return a formatted (HTML) message with the number of atoms
     */
    /* 
     * (non-javadoc)
     * This method can be called as a LocalService from
     * Marvin Sketch, cxcalc and Chemical Terms.
     * Annotations are used to provide default names
     * for Service and arguments, as well as a description.
     */
    @Alias(name="AtomCountText", params={"Molecule"})
    @Description("Returns a formatted text message containing the number of atoms in the structure.")
    public String countAtomsHTML(String moleculeString) {
        
        // import the molecule
        Molecule molecule = null;
        try {
            molecule = MolImporter.importMol(moleculeString);
        } catch (MolFormatException e) {
            // invalid molecule string
            molecule = new Molecule();
        }
    
        // get the atom count
        int value = countAtoms(molecule);
    
        // build and return the result string
        StringBuilder builder = new StringBuilder("<html><body>");
        if(value > 1) {
            builder.append("The structure has <font color='blue'><b>");
            builder.append(value);
            builder.append("</b></font> atoms.");
        } else {
            builder.append("The structure has <font color='red'><i>"
                + (value == 0 ? "no atoms" : "only one atom")
                + "</i></font>.");
        }
        builder.append("</body></html>");
        return builder.toString();
    }
    
}
		

WSDL/SOAP

SOAP Services defined by WSDL can be accessed via Marvin Services. The user interface supports automatic discovery of various options from local file system, or any available URL.

Supported Types and their mapping
SOAPJava
xs:stringjava.lang.String
xs:intjava.lang.Integer
xs:doublejava.lang.Double
xs:floatjava.lang.Float*
xs:booleanjava.lang.Boolean
xs:anytypejava.lang.Object

* Please note that WSDL Float support is not complete.

XML-RPC

XML-RPC Service uses the standard XML-RPC protocol to access remote calculations. The XML for the request is automatically generated from the provided attributes and arguments, and the response is parsed to unwrap the result of service call.

Calling XML-RPC Service from API
    XMLRPCServiceDescriptor descriptor = new XMLRPCServiceDescriptor();
    descriptor.setURL("http://sample.server.net/xmlrpc");
    descriptor.setMethodName("SampleService.countAtoms");
    
    descriptor.addArgument(ServiceArgument.createArgument(""));
    descriptor.addArgument(ServiceArgument.createArgument(""));

    Object result = null;
    try {
        result = descriptor.getServiceHandler().callService(descriptor, "C1CCNCCC1", "C");
    } catch (ServiceException e) {
        System.err.println("Service call failed.");
    }
    System.out.println("Synchronized call returned: " + String.valueOf(result));

    
    descriptor.getServiceHandler().callService(descriptor, new AsyncCallback<Integer>() {

        @Override
        public void onSuccess(Integer result) {
            System.out.println("Asynchronous call returned: " + result);
        }

        @Override
        public void onFailure(ServiceException caught) {
            System.err.println("Asynchronous call failed.");
        }
        
    }, "C1CCNCCC1", "C");
		

JSON-RPC

Marvin supports protocol versions 1.0 and 1.1. JSON Schema Service Descriptor can also be used for runtime parameter discovery. The response MUST be a textual representation of any finite combinations of java.lang.Boolean, java.lang.Number, java.lang.String, java.lang.Object[], java.util.Map<java.lang.String, java.lang.Object>, and null

Calling JSON-RPC Service from API
    JsonServiceDescriptor descriptor = new JsonServiceDescriptor();
    descriptor.setURL("http://api.geonames.org/");
    descriptor.setMethodName("citiesJSON");
    
    descriptor.addArgument(ServiceArgument.createArgument("north", new Double(0)));
    descriptor.addArgument(ServiceArgument.createArgument("south", new Double(0)));
    descriptor.addArgument(ServiceArgument.createArgument("east", new Double(0)));
    descriptor.addArgument(ServiceArgument.createArgument("west", new Double(0)));
    descriptor.addArgument(ServiceArgument.createArgument("language", ""));
    descriptor.addArgument(ServiceArgument.createArgument("username", ""));

    Object result = null;
    try {
        result = descriptor.getServiceHandler().callService(descriptor, 44.1, -9.9, 22.4, 55.2, "en", "demo");
    } catch (ServiceException e) {
        System.err.println("Service call failed.");
    }
    StringBuilder builder = new StringBuilder();
    Object[] array = (Object[]) ((Map<String, Object>)result).get("geonames");
    for(Object obj : array) {
        builder.append("Synchronized call returned: " + ((Map<String, Object>)obj).get("name") + "\n");
    }
    System.out.println(builder.toString());

    descriptor.getServiceHandler().callService(descriptor, new AsyncCallback<Map<String, Object>>() {

        @Override
        public void onSuccess(Map<String, Object> result) {
            StringBuilder builder = new StringBuilder();
            Object[] array = (Object[]) result.get("geonames");
            for(Object obj : array) {
                builder.append("Asynchronous call returned: " + ((Map<String, Object>)obj).get("name") + "\n");
            }
            System.out.println(builder.toString());
        }

        @Override
        public void onFailure(ServiceException caught) {
            System.err.println("Asynchronous call failed.");
        }
        
    }, 44.1, -9.9, 22.4, 55.2, "en", "demo");
		

HTTP

HTTP Service is the most lightweight and unrestricted remote service: it supports POST and GET requests to a predefined URL. Result is retrieved as is.

Accessing chemicalize.org via HTTP Service from API
    HTTPServiceDescriptor descriptor = new HTTPServiceDescriptor();
    descriptor.setURL("http://chemicalize.org/tomcat-files/datapage.jsp");
    
    descriptor.addArgument(ServiceArgument.createArgument("mol", ""));
    descriptor.addArgument(ServiceArgument.createArgument("special", ""));
    descriptor.addArgument(ServiceArgument.createArgument("pka_width", 0));

    Object result = null;
    try {
        result = descriptor.getServiceHandler().callService(descriptor, "c1ccnccc1", "pka", 300);
    } catch (ServiceException e) {
       System.err.println("Service call failed.");
    }
    System.out.println("Synchronized call returned: " + String.valueOf(result));

    
    descriptor.getServiceHandler().callService(descriptor, new AsyncCallback<String>() {

        @Override
        public void onSuccess(String result) {
            System.out.println("Aynchronous call returned: " + result);
        }

        @Override
        public void onFailure(ServiceException caught) {
            System.err.println("Asynchronous call failed.");
        }
        
    }, "c1ccnccc1", "pka", 300);
		

Configuration

The location of service configuration file is provided by the servicesConfigURL User Setting, and defaults to servicesconfig.xml in the users's ChemAxon folder - located in user home as .chemaxon or ChemAxon, depending on operating system. The file should be valid for the following XSD schema:

	<?xml version="1.0" encoding="utf-8"?>
	<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">

	<!--  definition of attributes -->
	<xs:attribute name="alias"   type="xs:string" />
	<xs:attribute name="name"   type="xs:string" />
	<xs:attribute name="class"  type="xs:string" />
	<xs:attribute name="url"    type="xs:string" />
	<xs:attribute name="method" type="xs:string" />
	<xs:attribute name="descriptor" type="xs:string" default="chemaxon.marvin.services.descriptors.LocalServiceDescriptor"/>
	<xs:attribute name="value" type="xs:string" />
	<xs:attribute name="expression" type="xs:string" />

	<!-- definition of complex types -->
	<xs:complexType name="evaluationType">
	  <xs:simpleContent>
	    <xs:extension base="xs:string">
	      <xs:attribute ref="class" use="optional" default="chemaxon.marvin.services.ChemicalTermsArgument" />
	      <xs:attribute ref="expression" use="optional" />
	    </xs:extension>
	  </xs:simpleContent>
	</xs:complexType>

	<xs:complexType name="argumentType">
	  <xs:all>
	    <xs:element name="Evaluate" type="evaluationType" minOccurs="0" maxOccurs="1" />
	  </xs:all>
	  <xs:attribute name="class" use="optional" default="java.lang.String">
	    <xs:simpleType>
	      <xs:restriction base="xs:string">
	        <xs:enumeration value="java.lang.Integer" />
	        <xs:enumeration value="java.lang.Float" />
	        <xs:enumeration value="java.lang.Double" />
	        <xs:enumeration value="java.lang.String" />
	        <xs:enumeration value="java.lang.Long" />
	        <xs:enumeration value="java.lang.Boolean" />
	        <xs:enumeration value="chemaxon.struc.Molecule" />
	        <xs:enumeration value="chemaxon.struc.MDocument" />
	      </xs:restriction>
	    </xs:simpleType>
	  </xs:attribute>
	  <xs:attribute ref="value" use="optional" />
	  <xs:attribute ref="name" use="optional" default="Unnamed" />
	  <xs:attribute ref="alias" use="optional" />	
	</xs:complexType>

	<xs:complexType name="serviceType">
	  <xs:all>
	    <xs:element name="Arguments" minOccurs="0">
	      <xs:complexType>
	        <xs:sequence>
	          <xs:element name="Argument" type="argumentType" maxOccurs="unbounded" />
	        </xs:sequence>
	      </xs:complexType>
	    </xs:element>
	  </xs:all>
	  <xs:attribute ref="name" use="required" />
	  <xs:attribute ref="url" use="required" />
	  <xs:attribute ref="method" use="required" />
	  <xs:attribute ref="class" use="optional" />
	  <xs:attribute ref="descriptor" use="optional" />
	  <xs:attribute ref="alias" use="optional" />
	  <xs:anyAttribute processContents="skip" />
	</xs:complexType>

	<xs:element name="Services">
	  <xs:complexType>
	    <xs:sequence>
	      <xs:element name="Service" type="serviceType" minOccurs="0" maxOccurs="unbounded" />
	    </xs:sequence>
	  </xs:complexType>
	</xs:element>

	</xs:schema>
				

Calling services

Configured services are accessible via MarvinSketch Tools/Services menu. A disabled entry indicates that the protocols for the service are not available - due to missing libraries. Calling a service from MarvinSketch will pop-up a dialog with the input data, the attributes and the results. Outside MarvinSketch such as cxcalc or Chemical Terms, the services are available by their names or aliases.

Viewing the results

In MarvinSketch the result is recognized automatically: if a Molecule or MDocument is returned, it is rendered. All other typed are displayed in a HTML view. Please note that returning partial HTML documents can provide nice looking results - with images as well.