src/java/org/myorg/myscserver/StructureValidatorService.java

package org.myorg.myscserver;

import com.sun.jersey.multipart.BodyPart;
import com.sun.jersey.multipart.MultiPart;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import javax.ws.rs.Consumes;

import javax.ws.rs.Path;

import javax.ws.rs.POST;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import chemaxon.formats.MolExporter;
import chemaxon.formats.MolImporter;
import chemaxon.struc.MPropertyContainer;
import chemaxon.struc.Molecule;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
 * The RESTful webservice which receives an SDF String, processes the structures
 * contained in it and returns an output SDF String.
 */
@Path("validator")
public class StructureValidatorService {

    private static final String DB_REGID = "DB_REGID";
    private static final String TYPE_INT = "[Int]";
    private static final String TYPE_TEXT = "[Text]";

    @Path("validate")
    @POST
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @Produces(MediaType.MULTIPART_FORM_DATA)
    public Response multipartTest(MultiPart data) {
        System.out.println("StructureValidatorService started.");

        BodyPart bp = null;
        String sdfInput = null;
        String sdfOutput = null;
        try {

            bp = data.getBodyParts().get(0);
            //Getting the incomming String value
            sdfInput = getString(bp.getEntityAs(InputStream.class));
            sdfOutput = validateSdf(sdfInput);

        } catch (Exception ex) { // we may catch differet types of exceptions, not the top level
            System.out.println("StructureValidatorService failed.");
            ex.printStackTrace(System.out);

            //assumign that we faced some exception based on processing the sdfInput
            //we will ask the ErrorBuilder.buildErrorResponse to create an XML document based
            //on the exception we are passing to it.
            //we set the errorMessage which is a parsable XML as the service response to show tell the client
            //something is wrong.
            // We are using the NOT_ACCEPTABLE : 406 http status to tell the client that something went wrong in
            // the sdf file processing and not in the protocol level.
            // I chose this status code as it is very unlikely that the our server return this error code
            String errorContent = ErrorBuilder.buildErrorResponse(ex);
            return Response.status(Response.Status.NOT_ACCEPTABLE).entity(errorContent).type(MediaType.MULTIPART_FORM_DATA_TYPE).build();
        }

        System.out.println("StructureValidatorService finished.");

        //returning the same string as the response value.
        return Response.status(Response.Status.OK).entity(sdfOutput).type(MediaType.MULTIPART_FORM_DATA_TYPE).build();
    }

    /**
     * Do the actual processing of the SDF content.
     *
     * @param sdfInput the sdf file content value received from the client. s
     * @return the sdf content after it is fully processed
     */
    private static String validateSdf(String sdfInput) throws Exception {

        StringBufferOutputStream out = new StringBufferOutputStream();
        MolExporter exporter = new MolExporter(out, "sdf");

        InputStream is = new ByteArrayInputStream(sdfInput.getBytes("UTF-8"));
        MolImporter importer = new MolImporter(is, "sdf");

        Molecule mol = importer.read();
        while (mol != null) {
            System.out.println("Processing molecule: " + mol.getFormula());
            
            MPropertyContainer properties = mol.properties();
            // Server side sample changes:
            // 1. create a new integer field with random value
            properties.setObject(TYPE_INT + "INTEGER_FROM_WS", (int) (Math.random() * 1000));
            properties.setObject(TYPE_TEXT + "TEXT_FROM_WS", "Mass is " + mol.getMass());

            // 2. Add "x" to DB_REGID value
            String origValue = properties.getString(DB_REGID);
            if (origValue != null) {
                properties.setString(DB_REGID, "x" + origValue);
            }

            exporter.write(mol);
            mol = importer.read();
        }
        importer.close();
        exporter.close();
        return out.asString();

    }

    /**
     * extract the content of a inputstream and return it as an String.
     *
     * @param is the inputstream which we want to get its content as String
     * @return the String representing content of the stream.
     * @throws Exception
     */
    private static String getString(InputStream is) throws Exception {
        if (is != null) {
            Writer writer = new StringWriter();

            char[] buffer = new char[1024];
            try {
                Reader reader = new BufferedReader(
                        new InputStreamReader(is));
                int n;
                while ((n = reader.read(buffer)) != -1) {
                    writer.write(buffer, 0, n);
                }
            } finally {
                is.close();
            }
            return writer.toString();
        } else {
            return null;
        }
    }

    private static class StringBufferOutputStream extends OutputStream {

        private StringBuffer strBuffer = new StringBuffer();

        @Override
        public void write(int b) throws IOException {
            strBuffer.append((char) b);
        }

        public String asString() {
            return strBuffer.toString();
        }
    }
}