org.ambraproject.category.CategoryServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.ambraproject.category.CategoryServiceImpl.java

Source

/*
 * $HeadURL$
 * $Id$
 * Copyright (c) 2006-2013 by Public Library of Science http://plos.org http://ambraproject.org
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.ambraproject.category;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

/**
 * {@inheritDoc}
 */
public class CategoryServiceImpl implements CategoryService {

    private static final Logger log = LoggerFactory.getLogger(CategoryServiceImpl.class);

    private String ambraServer;

    public void setAmbraServer(String ambraServer) {
        this.ambraServer = ambraServer;
    }

    /**
     * Extracts the categories from the JSON returned by the ambra server.
     *
     * @param json response from the ambra server to a get categories call
     * @return List of category Strings
     */
    List<String> parseJsonFromAmbra(String json, String doi) {

        // The ambra action returns JSON wrapped in javascript comments, but Gson does not
        // like this.
        if (json.startsWith("/*")) {
            json = json.substring(2);
        }
        if (json.endsWith("*/")) {
            json = json.substring(0, json.length() - 2);
        }

        JsonParser parser = new JsonParser();
        JsonObject obj = parser.parse(json).getAsJsonObject();
        JsonArray categories;
        try {
            categories = obj.getAsJsonArray("categories");
        } catch (ClassCastException cce) {
            log.error(String.format("Exception processing category JSON for %s.  "
                    + "This probably means that this is not a valid DOI.", doi), cce);
            return new ArrayList<String>();
        }
        List<String> result = new ArrayList<String>(categories.size());
        for (JsonElement category : categories) {
            result.add(category.getAsString());
        }
        return result;
    }

    /**
     * {@inheritDoc}
     */
    public List<String> fetchCategoriesFromAmbra(String doi) {
        String json;
        try {
            URL url = new URL(ambraServer + "/article/categories?articleURI=" + URLEncoder.encode(doi));
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.connect();
            InputStream is = conn.getInputStream();
            try {
                json = IOUtils.toString(is).trim();
            } finally {
                is.close();
            }
        } catch (IOException ioe) {

            // Log the exception, but carry on.
            // TODO: we've seen a problem where if we throw an exception here,
            // the queue just stops and fails to make forward progress.  Look
            // into our error handling strategy in detail to make sure we're
            // doing the right thing.
            log.error("Failed to fetch categories from ambra", ioe);
            return new ArrayList<String>();
        }
        return parseJsonFromAmbra(json, doi);
    }

    /**
     * {@inheritDoc}
     */
    public List<String> getTopLevelCategories(List<String> categories) {

        // Since we want to return the top-level categories in the order they
        // are listed, we can't just use a HashSet.  Using a TreeSet with a
        // custom comparator is a possibility, but seems like more trouble
        // than it's worth for small lists.  Instead, we just use an n-squared
        // list approach here.
        List<String> results = new ArrayList<String>(categories.size());
        for (String category : categories) {
            if ('/' != category.charAt(0)) {
                throw new IllegalArgumentException("Categories must begin with '/': " + category);
            }
            category = category.split("\\/")[1];
            boolean alreadyExists = false;
            for (String existing : results) {
                if (category.equals(existing)) {
                    alreadyExists = true;
                    break;
                }
            }
            if (!alreadyExists) {
                results.add(category);
            }
        }
        return results;
    }
}