org.carrot2.webapp.model.WebappConfig.java Source code

Java tutorial

Introduction

Here is the source code for org.carrot2.webapp.model.WebappConfig.java

Source

/*
 * Carrot2 project.
 *
 * Copyright (C) 2002-2015, Dawid Weiss, Stanisaw Osiski.
 * All rights reserved.
 *
 * Refer to the full license file "carrot2.LICENSE"
 * in the root folder of the repository checkout or at:
 * http://www.carrot2.org/carrot2.LICENSE
 */

package org.carrot2.webapp.model;

import java.io.InputStream;
import java.util.*;

import javax.servlet.ServletContext;

import org.carrot2.core.*;
import org.carrot2.core.attribute.AttributeNames;
import org.carrot2.core.attribute.InternalAttributePredicate;
import org.carrot2.util.CloseableUtils;
import org.carrot2.util.attribute.*;
import org.carrot2.util.resource.*;
import org.carrot2.util.resource.ResourceLookup.Location;
import org.carrot2.webapp.QueryProcessorServlet;
import org.carrot2.webapp.filter.QueryWordHighlighter;
import org.simpleframework.xml.*;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.core.Persister;
import org.slf4j.Logger;

import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.*;

/**
 * The application-wide configuration.
 */
public class WebappConfig {
    private final static Logger log = org.slf4j.LoggerFactory.getLogger(WebappConfig.class);

    @Element(required = false)
    public ProcessingComponentSuite components;

    /**
     * Descriptors of attributes to display in the advanced document source options view,
     * keyed by document source id.
     */
    public Map<String, List<AttributeDescriptor>> sourceAttributeMetadata;

    /**
     * Values of document source attributes set at component initialization time, keyed by
     * document source id. We need these to show proper default values in advanced options
     * for those sources for which the default attribute values have been overridden in
     * the component suite.
     */
    public Map<String, Map<String, Object>> sourceInitializationAttributes;

    /**
     * A set of keys of all internal attributes of all components. We need this to prevent
     * these attributes to be bound from the HTTP request parameters.
     */
    public Set<String> componentInternalAttributeKeys;

    @ElementList(entry = "skin")
    public ArrayList<SkinModel> skins;

    @ElementList(entry = "size")
    public ArrayList<ResultsSizeModel> sizes;

    @ElementList(entry = "view")
    public ArrayList<ResultsViewModel> views;

    @ElementList(entry = "cache", required = false)
    public ArrayList<ResultsCacheModel> caches = Lists.newArrayList();

    @Attribute(name = "skins-folder")
    public String skinsFolder;

    @Attribute(name = "component-suite")
    public String componentSuite = "suites/suite-webapp.xml";

    @Attribute(name = "search-url", required = false)
    public String SEARCH_URL = "search";

    @Attribute(name = "xml-url", required = false)
    public String XML_URL = "xml";

    @Attribute(name = "query-highlighter", required = false)
    public String QUERY_HIGHLIGHTER_ID = QueryWordHighlighter.class.getName();

    @Attribute(name = "max-carrot2-results", required = false)
    public Integer maxCarrot2Results = null;

    // These are for output serialization only, this is somewhat clumsy. 

    @Attribute(name = "query-param", required = false)
    public final static String QUERY_PARAM = AttributeNames.QUERY;
    public final static String QUERY_PARAM_ALIAS = "q";

    @Attribute(name = "results-param", required = false)
    public final static String RESULTS_PARAM = AttributeNames.RESULTS;

    @Attribute(name = "source-param", required = false)
    public final static String SOURCE_PARAM = "source";

    @Attribute(name = "algorithm-param", required = false)
    public final static String ALGORITHM_PARAM = "algorithm";

    @Attribute(name = "type-param", required = false)
    public final static String TYPE_PARAM = "type";

    @Attribute(name = "view-param", required = false)
    public final static String VIEW_PARAM = "view";

    @Attribute(name = "skin-param", required = false)
    public final static String SKIN_PARAM = "skin";

    @Attribute(name = "stylesheet-param", required = false)
    public final static String STYLESHEET_PARAM = "stylesheet";

    /**
     * @return Initialize the global configuration and return it.
     */
    public final static WebappConfig getSingleton(ServletContext context) {
        try {
            // Load web application configuration.
            final IResource webappConfig = new ResourceLookup(
                    new PrefixDecoratorLocator(new ServletContextLocator(context), "/WEB-INF/"))
                            .getFirst("webapp-config.xml");

            if (webappConfig == null)
                throw new RuntimeException("Could not find WEB-INF/webapp-config.xml.");

            final WebappConfig conf = deserialize(webappConfig);

            if (conf.skins.size() == 0) {
                throw new RuntimeException("Configuration must contain at least one skin");
            }

            if (conf.views.size() == 0) {
                throw new RuntimeException("Configuration must contain at least one view");
            }

            if (conf.sizes.size() == 0) {
                throw new RuntimeException("Configuration must contain at least one result list size");
            }

            // Load component suite.
            List<IResourceLocator> resourceLocators = Lists.newArrayList();
            resourceLocators
                    .add(new PrefixDecoratorLocator(new ServletContextLocator(context), "/WEB-INF/suites/"));

            if (Boolean.getBoolean(QueryProcessorServlet.ENABLE_CLASSPATH_LOCATOR))
                resourceLocators.add(Location.CONTEXT_CLASS_LOADER.locator);

            ResourceLookup suitesLookup = new ResourceLookup(resourceLocators);
            IResource suite = suitesLookup.getFirst(conf.componentSuite);

            if (suite == null) {
                throw new Exception(
                        "Suite file not found in servlet context's /WEB-INF/suites: " + conf.componentSuite);
            }

            conf.components = ProcessingComponentSuite.deserialize(suite, suitesLookup);

            log.info("Loaded " + conf.components.getSources().size() + " sources and "
                    + conf.components.getAlgorithms().size() + " algorithms");

            // Prepare attribute descriptors for document sources
            conf.sourceAttributeMetadata = prepareSourceAttributeMetadata(conf.components);
            conf.sourceInitializationAttributes = prepareSourceInitializationAttributes(conf.components);
            conf.componentInternalAttributeKeys = prepareComponentInternalAttributeKeys(conf.components);

            return conf;
        } catch (Exception e) {
            log.error("Could not load application config.", e);
            throw new RuntimeException("Could not load application config.", e);
        }
    }

    public String getContextRelativeSkinStylesheet(RequestModel requestModel) {
        return "/" + skinsFolder + "/" + requestModel.skin + "/" + requestModel.stylesheet;
    }

    public SkinModel getSkinById(String skinId) {
        // Short list, we can afford linear search
        for (SkinModel skin : skins) {
            if (skin.id.equals(skinId)) {
                return skin;
            }
        }

        return ModelWithDefault.getDefault(skins);
    }

    private static WebappConfig deserialize(IResource resource) throws Exception {
        final InputStream inputStream = resource.open();
        final WebappConfig loaded;
        try {
            loaded = new Persister().read(WebappConfig.class, inputStream);
        } finally {
            CloseableUtils.close(inputStream);
        }

        return loaded;
    }

    @SuppressWarnings("unchecked")
    private static Map<String, List<AttributeDescriptor>> prepareSourceAttributeMetadata(
            ProcessingComponentSuite components) throws Exception {
        final List<DocumentSourceDescriptor> sources = components.getSources();
        final Map<String, List<AttributeDescriptor>> sourceDescriptors = Maps.newLinkedHashMap();

        for (DocumentSourceDescriptor documentSourceDescriptor : sources) {
            final BindableDescriptor bindableDescriptor = documentSourceDescriptor.getBindableDescriptor()
                    .only(Input.class).only(new LevelsPredicate(AttributeLevel.BASIC, AttributeLevel.MEDIUM))
                    .only(Predicates.<AttributeDescriptor>and(Predicates.not(new InternalAttributePredicate()),
                            new Predicate<AttributeDescriptor>() {
                                /** Attribute types supported in advanced source options */
                                final Set<Class<?>> ALLOWED_PLAIN_TYPES = ImmutableSet.<Class<?>>of(Byte.class,
                                        Short.class, Integer.class, Long.class, Float.class, Double.class,
                                        Boolean.class, String.class, Character.class);

                                private final Set<String> IGNORED = ImmutableSet.<String>of(AttributeNames.QUERY,
                                        AttributeNames.RESULTS);

                                public boolean apply(AttributeDescriptor d) {
                                    return (d.type.isEnum() || ALLOWED_PLAIN_TYPES.contains(d.type))
                                            && !IGNORED.contains(d.key);
                                }
                            }));

            final List<AttributeDescriptor> descriptors = Lists
                    .newArrayList(bindableDescriptor.attributeDescriptors.values());
            Collections.sort(descriptors, new Comparator<AttributeDescriptor>() {
                public int compare(AttributeDescriptor d1, AttributeDescriptor d2) {
                    return getOrder(d1) - getOrder(d2);
                }

                private int getOrder(AttributeDescriptor d) {
                    if (d.type.isEnum()) {
                        return 0;
                    } else if (d.type.equals(Boolean.class)) {
                        return 2;
                    } else {
                        return 1;
                    }
                }
            });

            sourceDescriptors.put(documentSourceDescriptor.getId(), descriptors);
        }

        return sourceDescriptors;
    }

    private static Map<String, Map<String, Object>> prepareSourceInitializationAttributes(
            ProcessingComponentSuite components) {
        final List<DocumentSourceDescriptor> sources = components.getSources();
        final Map<String, Map<String, Object>> initAttributes = Maps.newHashMap();

        for (DocumentSourceDescriptor documentSourceDescriptor : sources) {
            initAttributes.put(documentSourceDescriptor.getId(),
                    documentSourceDescriptor.getComponentConfiguration().attributes);
        }

        return initAttributes;
    }

    private static Set<String> prepareComponentInternalAttributeKeys(ProcessingComponentSuite components)
            throws Exception {
        final List<ProcessingComponentDescriptor> descriptors = components.getComponents();
        final Set<String> internalAttributeKeys = Sets.newHashSet();

        for (ProcessingComponentDescriptor descriptor : descriptors) {
            internalAttributeKeys.addAll(Lists.transform(
                    Lists.newArrayList(descriptor.getBindableDescriptor()
                            .only(new InternalAttributePredicate()).attributeDescriptors.values()),
                    AttributeDescriptor.AttributeDescriptorToKey.INSTANCE));
        }

        internalAttributeKeys.remove(AttributeNames.QUERY);

        return internalAttributeKeys;
    }
}