gov.nih.nci.cabio.portal.portlet.ObjectDetailsJSONServlet.java Source code

Java tutorial

Introduction

Here is the source code for gov.nih.nci.cabio.portal.portlet.ObjectDetailsJSONServlet.java

Source

/*L
 *  Copyright SAIC
 *
 *  Distributed under the OSI-approved BSD 3-Clause License.
 *  See http://ncip.github.com/cabio/LICENSE.txt for details.
 */

package gov.nih.nci.cabio.portal.portlet;

import gov.nih.nci.cabio.domain.Gene;
import gov.nih.nci.cabio.portal.portlet.canned.CannedObjectConfig;
import gov.nih.nci.cabio.portal.portlet.canned.ClassObject;
import gov.nih.nci.cabio.portal.portlet.canned.LabeledObject;
import gov.nih.nci.common.util.ReflectionUtils;
import gov.nih.nci.system.applicationservice.CaBioApplicationService;
import gov.nih.nci.system.client.ApplicationServiceProvider;

import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * Servlet for AJAX which outputs caBIO objects in JSON format. 
 * 
 * @author <a href="mailto:rokickik@mail.nih.gov">Konrad Rokicki</a>
 */
public class ObjectDetailsJSONServlet extends HttpServlet {

    protected final int MAX_CHILD_ITEMS = 100;

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

    private CannedObjectConfig objectConfig;
    private CaBioApplicationService as;
    private ReportService rs;

    public void init() throws ServletException {
        try {
            this.objectConfig = (CannedObjectConfig) getServletContext().getAttribute("objectConfig");

            this.as = (CaBioApplicationService) ApplicationServiceProvider.getApplicationService();

            this.rs = new ReportService(as);
        } catch (Exception e) {
            throw new ServletException(e);
        }
    }

    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

        String className = req.getParameter("className");
        String id = req.getParameter("id");
        String path = req.getParameter("path");
        String assocClass = req.getParameter("assocClass");

        res.setContentType("text/plain");
        PrintWriter out = res.getWriter();

        try {
            if (path != null && !path.matches("^[\\w\\.]+(\\[\\w+=\\w+\\])?$")) {
                throw new IllegalArgumentException("Target must be a valid association path.");
            }
            out.print(processRequest(className, id, path, assocClass));
        } catch (Exception e) {
            log.error("Error encountered while outputting JSON object details for " + className + "#" + id + ", "
                    + path, e);

            JSONObject json = new JSONObject();
            try {
                json.put("exceptionClass", e.getClass().getName());
                json.put("exceptionMessage", e.getMessage());
            } catch (JSONException j) {
                log.error("Error returning JSON error message", j);
            }
            out.print(json.toString());
        }
    }

    protected String processRequest(String className, String id, String path, String assocClass) throws Exception {

        if (!className.startsWith("gov.nih.nci.")) {
            throw new Exception("Invalid class specified.");
        }

        log.info("Object details (" + className + "," + id + "," + path + "," + assocClass + ")");

        Class clazz = Class.forName(className);
        Long longId = Long.parseLong(id);

        String role = path == null ? "DETAIL" : "NESTED";

        if (path == null) {
            // The object configuration
            ClassObject config = objectConfig.getClasses().get(className);
            if (config == null)
                config = createAdhocConfig(className, role);

            // Query for the object
            Object obj = rs.getDetailObject(clazz, longId);
            if (obj == null) {
                throw new Exception("Cannot find " + className + " with id " + id + ".");
            }

            return objectToJSON(new ResultItem(obj), config);
        } else {
            // The object configuration
            ClassObject config = objectConfig.getClasses().get(assocClass);
            if (config == null)
                config = createAdhocConfig(assocClass, role);

            // Are there any constraints? pull them out so we can apply them later
            String rolename = path;
            String extraPath = null;
            int a = path.indexOf('.');
            int b = path.indexOf('[');
            if (a > 0) {
                rolename = path.substring(0, a);
                extraPath = path.substring(a);
            } else if (b > 0) {
                rolename = path.substring(0, b);
                extraPath = path.substring(b);
            }

            // Query for the associated objects
            Collection list = rs.getDetailObjects(clazz, longId, rolename, config);

            // Apply further path constraints
            if (extraPath != null) {

                // Create a dummy root object
                Object root = clazz.newInstance();
                ReflectionUtils.setFieldValue(root, rolename, list);

                ResultItem item = new ResultItem(root);
                list = (Collection) item.get(path);

                // Configure display according to the last class in the path
                List<Class> pathClasses = item.getClasses(path);

                String lastClassName = pathClasses.get(pathClasses.size() - 1).getName();
                config = objectConfig.getClasses().get(lastClassName);
            }

            return objectsToJSON(list, config).toString();
        }
    }

    /**
     * Converts the given item to a JSON string representation, based on the 
     * configuration provided.
     * @param item the item to render into JSON
     * @param config configuration for the class of the item
     * @return JSON string representation of item
     * @throws JSONException
     */
    private String objectToJSON(ResultItem item, ClassObject config) throws JSONException {

        JSONObject json = new JSONObject();
        json.put("className", config.getName());
        json.put("id", item.get("id").toString());
        json.put("label", config.getLabel());

        JSONArray attributes = new JSONArray();
        for (LabeledObject attr : config.getDetailAttributes()) {
            JSONObject jsonObj = new JSONObject();
            jsonObj.put("name", attr.getLabel());
            jsonObj.put("path", attr.getName());
            Object value = item.get(attr.getName());

            if (value == null) {
                // Case 1) Null
                jsonObj.put("value", "");
            } else if (value.getClass().getName().startsWith("java.lang")
                    || value.getClass().equals(java.util.Date.class)) {
                // Case 2) Normal attribute
                Object displayValue = item.getDisplayMap().get(attr.getName());
                jsonObj.put("value", displayValue);
                if (attr.getExternalLink() != null) {
                    jsonObj.put("link", attr.getExternalLink());
                }
                if (attr.getInternalLink() != null) {
                    List<Class> linkClasses = item.getClasses(attr.getInternalLink());
                    String linkClassName = linkClasses.get(linkClasses.size() - 1).getName();
                    jsonObj.put("drillClassName", ClassUtils.removeEnchancer(linkClassName));
                    jsonObj.put("drillId", item.get(attr.getInternalLink() + ".id"));
                }
            } else {
                // Case 3) Association
                List<Class> classes = item.getClasses(attr.getName());
                // The class we will display (the last one)
                String className = classes.get(classes.size() - 1).getName();

                if (className.startsWith("java.lang") || className.equals("java.util.Date")) {
                    // Usually a list of Strings 
                    String displayValue = item.getDisplayMap().get(attr.getName());
                    jsonObj.put("value", displayValue);
                    if (attr.getExternalLink() != null) {
                        jsonObj.put("link", attr.getExternalLink());
                    }
                } else {
                    // The class we will fetch (the first one)
                    jsonObj.put("className", ClassUtils.removeEnchancer(classes.get(1).getName()));
                }
            }

            attributes.put(jsonObj);
        }

        json.put("attributes", attributes);
        return json.toString();
    }

    /**
     * Converts the given collection into a JSON string representation, 
     * based on the configuration provided.
     * @param items the collection to render into JSON
     * @param config configuration for the class of the collection items
     * @return JSON string representation of item
     * @throws JSONException
     */
    private JSONObject objectsToJSON(Collection items, ClassObject config) throws JSONException {

        JSONObject json = new JSONObject();

        JSONArray cols = new JSONArray();
        for (LabeledObject attr : config.getNestedAttributes()) {
            JSONObject jsonObj = new JSONObject();
            jsonObj.put("value", attr.getLabel());
            if (attr.getExternalLink() != null) {
                jsonObj.put("link", attr.getExternalLink());
            }
            cols.put(jsonObj);
        }
        json.put("columnNames", cols);
        json.put("className", config.getName());

        JSONArray rows = new JSONArray();

        if (items == null) {
            json.put("count", "0");
        } else {
            json.put("count", items.size());
            int c = 0;
            for (Object o : items) {
                if (c++ >= MAX_CHILD_ITEMS)
                    break;

                ResultItem r = new ResultItem(o);

                JSONObject jsonRow = new JSONObject();
                for (LabeledObject attr : config.getNestedAttributes()) {
                    String displayValue = r.getDisplayMap().get(attr.getName());
                    jsonRow.put(attr.getLabel(), displayValue);
                }
                jsonRow.put("id", r.get("id"));

                rows.put(jsonRow);
            }
        }

        json.put("rows", rows);
        return json;
    }

    /**
     * Convert class name for display, i.e.
     * "gov.nih.nci.cabio.domain.ClinicalTrialProtocol" to
     * "Clinical Trial Protocol"
     * @param className
     * @return
     */
    private String getFormattedClassName(String className) {
        return className.substring(className.lastIndexOf('.') + 1).replaceAll("([A-Z])", " $1").substring(1);
    }

    /**
     * Create an Ad Hoc configuration for what to display for a class that
     * is not explicitly configured. 
     * @param className
     * @param role
     * @return
     * @throws ClassNotFoundException
     */
    private ClassObject createAdhocConfig(String className, String role) throws ClassNotFoundException {

        log.warn("No configuration for associated class " + className);
        Set<String> roles = new HashSet<String>();
        roles.add(role);
        String classLabel = getFormattedClassName(className);
        ClassObject config = new ClassObject(className, classLabel, classLabel + "s");
        Class assocClazz = Class.forName(className);
        for (Field field : ClassUtils.getFields(assocClazz)) {
            String name = field.getName();
            if (!name.equalsIgnoreCase("id"))
                config.addAttribute(name, name, null, null, roles);
        }

        return config;
    }

    /**
     * Test harness.
     */
    public static final void main(String[] args) throws Exception {

        //        String className = "gov.nih.nci.cabio.domain.GeneOntology";
        //        String id = "32869";
        //        CannedObjectConfig c = new CannedObjectConfig();
        //        ClassObject config = c.getClasses().get(className);
        //        ObjectDetailsJSONServlet s = new ObjectDetailsJSONServlet();
        //        System.out.println(s.processRequest(config, className, id));

        System.out.println(ClassUtils.getAssociationType(Gene.class, "taxon"));
        System.out.println(ClassUtils.getAssociationType(Gene.class, "geneFunctionAssociationCollection"));

    }
}