Page tree

This manual gives you a walk-through on how to use local services.


Introduction
 

A local service is for accessing third party calculations implemented in Java. It does not require server or network connection. A Java Archive (JAR) file acts as "server", any public class with default constructor acts as "service", and all public methods can be called. Please keep in 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 from a non-java environment such as Marvin .NET or JChem for Excel.
 

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.

 

Examples
 

Example #1 
 

In this basic example we implement a method that returns the string "Hello <molecule name>!", where <molecule name> is the name of the molecule generated by ChemAxon's S2N (Structure to Name) converting tool. The following code will do this:

package chemaxon.examples.calcintegration;

import chemaxon.struc.Molecule;

public class Hello {
public String helloMolecule(Molecule mol) {
return "Hello "+mol.toFormat("name")+"!";
    }
}

 

Having compiled this code, pack the created class file(s) into a Java Archive (JAR) file. All Java IDEs can export Java files in a JAR file. In Eclipse it can be done by the File > Export menu item. 

Fig. 1 JAR export window in Eclipse
 

After creating the JAR file, open MarvinSketch. Please note that version 5.6 or later is required. Select Edit > Preferences > Services from the menu. Then press the + button to add a new service.

Fig. 2 Adding new services via the Preferences window in MarvinSketch

 

To add a new service then proceed to press the Accept button. It will appear in the list of services.

Fig. 3 Adding new services to the list of services

 

As a result of these steps the new service will be available from the Tools > Services menu.

Fig. 4 Looking up the new services in MarvinSketch 

 

Finally, to use the new service, select the Tools > Services > Hello1 menu item with an input molecule on the canvas. The calculation result appears in a new dialog.

Fig. 5 Calculation result shown in a new dialog

 

Local Service makes good use of the Alias and Description annotations. Any method 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.

 

Now let's extend this example by addig a new method to the Hello.java class and adding Alias and Description annotations to it. Thus the new code will be:

@Alias(name="HelloMolecule", params={"molecule", "format"})
@Description("Says hello to the molecule.")
public String helloMolecule(Molecule mol, String format) {
return "Hello "+mol.toFormat(format)+"!";
} 

 

Create a JAR file as above and name it hello2.jar. Call the new method (as a local service) from this JAR file. The name of the service, the description and the parameter names will be automatically filled during the setup.

Fig. 6 Setting us the new service from the JAR source

 

A result example in this case is:

Fig. 7 Result window showing the new service in action

 

Note that the result molecule format can be modified on the result display panel. Try it !

 

Example #2

 

The next example will be a bit more complicated as we will implement some microspecies related calculations. We want to 

  • calculate the major microspecies at a given pH;
  • calculate microspecies count at a given pH;
  • generate HTML report about the calculations.
     

The following code will do that:

package chemaxon.examples.calcintegration;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import chemaxon.marvin.calculations.MajorMicrospeciesPlugin;
import chemaxon.marvin.plugin.PluginException;
import chemaxon.marvin.services.localservice.Alias;
import chemaxon.marvin.services.localservice.Description;
import chemaxon.struc.Molecule;

public class MicrospeciesCalculator {

private static MajorMicrospeciesPlugin createPlugin(Molecule mol, Double pH) {

MajorMicrospeciesPlugin mmsp = new MajorMicrospeciesPlugin();
boolean valid = false;

try {
mmsp.setMolecule(mol);
mmsp.setpH(pH);
valid = mmsp.run();
} catch (PluginException e) {
// error, valid == false
}
return valid ? mmsp : null;
}

@Alias(name="MMS", params={"mol", "pH"})
@Description("Calculates major microspecies of the given " +
"molecule at given pH.")
public Molecule getMajorMicrospecies(Molecule mol, Double pH)
throws PluginException {
MajorMicrospeciesPlugin plugin = createPlugin(mol, pH);
return plugin == null ? null : plugin.getMajorMicrospecies();
}

@Alias(name="MSCount", params={"mol", "pH"})
@Description("Counts microspecies of the given molecule at " +
"given pH.")
public Integer getMicrospeciesCount(Molecule mol, Double pH)
throws PluginException {
MajorMicrospeciesPlugin plugin = createPlugin(mol, pH);
return plugin == null ? null : plugin.getMicrospeciesCount();
}

@Alias(name="MSReport",
params={"mol", "pHLower", "pHUpper", "displayImage"})
 
@Description("Creates HTML report about microspecies.")
public String getMicrospeciesHtmlReport(Molecule mol,
Double pHLower, Double pHUpper, Boolean displayImage)
throws PluginException, IOException {
MajorMicrospeciesPlugin plugin = createPlugin(mol, pHLower);
if (plugin == null) {
return null;
}

// calculate, build and return the result string
StringBuilder rbuilder = new StringBuilder("<html><body>");
rbuilder.append("<table border=\"1\">");
rbuilder.append("<tr>");
rbuilder.append("<th><b>pH</b></th>");
rbuilder.append("<th><b>Major microspecies</b></th>");
rbuilder.append("<th><b>Charge</b></th>");
rbuilder.append("</tr>");

for (int i=0;i<=pHUpper.intValue()-pHLower.intValue();i++) {
double pH = pHLower+i;
plugin.setpH(pH);
plugin.run();
Molecule mms = plugin.getMajorMicrospecies();
rbuilder.append("<tr>");
rbuilder.append("<td>"+pH+"</td>");
if (displayImage) {
File f = File.createTempFile("image"+i, "png");
FileOutputStream fos = new FileOutputStream(f);
fos.write(mms.toBinFormat("png"));
rbuilder.append("<td><img src=\""
+f.toURI()+"\"></td>");
} else {
rbuilder.append("<td><font color='blue'><i>"
+mms.toFormat("smiles")+"</i></font></td>");
}
rbuilder.append("<td>"
+mms.getFormalCharge()+"</b></td>");
rbuilder.append("</tr>");
}
rbuilder.append("</body></html>");
return rbuilder.toString();
}
}

 

Pack the MicrospeciesCalculator class into a JAR file as already described (mscalc.jar). Add all methods as local services to the service list. Finally you should have a list like follows:

 

The result of running the MMS calculation is the major microspecies molecule at a given pH. 

Fig. 8. Calculated major microspecies as a result of the MMS calculated 

 

The MSReport calculation generates the source code of an HTML page and returns it as a string. The HTML page is then rendered on the fly by the result display component.

Fig. 8 The MS report calculation results

 

Changes made on the molecule in MarvinSketch will be automatically updated on the results display (both structure and results), if the Calculate Automatically option is enabled.

 

Calling Local Services from API

 

The following code snippet calls the Integer countAtoms(Molecule) function of the 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

 

A Local Service can look up default service and argument names, as well as description information from annotations. These values are used in MarvinSketch when adding the Local Service to the list of services 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 that can be used as a Local Service below. To download the sample service jar file with source, click here.

*
 * Copyright (c) 1998-2014 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();
    }
    
}