com.esri.gpt.control.georss.JsonSearchEngine.java Source code

Java tutorial

Introduction

Here is the source code for com.esri.gpt.control.georss.JsonSearchEngine.java

Source

/*
 * Copyright 2012 Esri.
 *
 * 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.0
 *
 * Unless 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 com.esri.gpt.control.georss;

import com.esri.gpt.catalog.context.CatalogIndexException;
import com.esri.gpt.catalog.discovery.DiscoveredRecord;
import com.esri.gpt.catalog.discovery.rest.RestQuery;
import com.esri.gpt.catalog.lucene.LuceneIndexAdapter;
import com.esri.gpt.catalog.lucene.LuceneQueryAdapter;
import com.esri.gpt.catalog.search.OpenSearchProperties;
import com.esri.gpt.framework.context.ApplicationConfiguration;
import com.esri.gpt.framework.context.ApplicationContext;
import com.esri.gpt.framework.context.RequestContext;
import com.esri.gpt.framework.jsf.FacesContextBroker;
import com.esri.gpt.framework.jsf.MessageBroker;
import com.esri.gpt.framework.sql.ConnectionBroker;
import com.esri.gpt.framework.sql.ManagedConnection;
import com.esri.gpt.framework.util.Val;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Fieldable;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.IndexSearcher;

/**
 * JSON search engine.
   * <p/>
   * Uses <i>json.engine.className</i> parameter from <b>gpt.xml</b> to create instance.
   * Creates default instance if parameter is empty.
   * <p/>
   * Two parameters: <i>json.meta.lucene</i> and <i>json.meta.catalog</i> from gpt.xml
   * configuration file determines whether certain family of output parameters 
   * is allowed to be print to the output or not. Default: <code>true</code>.
 * @see ExtJsonFeedWriter
 */
public abstract class JsonSearchEngine {

    /**
     * Logger.
     */
    private static final Logger LOG = Logger.getLogger(JsonSearchEngine.class.getCanonicalName());

    /**
     * Default search engine.
     */
    private static final JsonSearchEngine defaultEngine = new JsonSearchEngine() {
        @Override
        public IFeedRecords search(HttpServletRequest request, HttpServletResponse response, RequestContext context,
                RestQuery query) throws Exception {
            return doSearch(request, response, context, query);
        }
    };

    /**
     * Creates instance of the search engine. 
     * @return instance of {@link JsonSearchEngine}
     */
    public static JsonSearchEngine createInstance() {
        String className = getConfigParam("json.engine.className");
        if (className.isEmpty()) {
            return defaultEngine;
        } else {
            try {
                Class engineClass = Class.forName(className);
                return (JsonSearchEngine) engineClass.newInstance();
            } catch (Exception ex) {
                LOG.log(Level.INFO, "Error creating JSON search engine: " + className
                        + ". Using default JSON search engine instead.", ex);
                return defaultEngine;
            }
        }
    }

    /**
     * Performs search operation.
     *
     * @param request HTTP servlet request
     * @param response HTTP servlet response
     * @param context request context
     * @param query query
     * @return records
     * @throws Exception if searching fails
     */
    public abstract IFeedRecords search(HttpServletRequest request, HttpServletResponse response,
            RequestContext context, RestQuery query) throws Exception;

    /**
     * Loads standard field meta info.
     * @param fields list of fields
     */
    protected void loadStdFieldMeta(List<IFeedRecords.FieldMeta> fields) {
        fields.add(new IFeedRecords.FieldMeta("objectid", "esriFieldTypeOID", "OBJECTID"));
        fields.add(new IFeedRecords.FieldMeta("title", "esriFieldTypeString", "Title", 256));
        fields.add(new IFeedRecords.FieldMeta("id", "esriFieldTypeGUID", "UUID", 38));
        fields.add(new IFeedRecords.FieldMeta("updated", "esriFieldTypeDate", "Updated", 20));
        fields.add(new IFeedRecords.FieldMeta("contentType", "esriFieldTypeString", "Content Type", 64));
        fields.add(new IFeedRecords.FieldMeta("summary", "esriFieldTypeString", "Summary", 256));
        fields.add(new IFeedRecords.FieldMeta("shape", "esriFieldTypeGeometry", "Shape"));
    }

    /**
     * Performs search operation.
     * @param request HTTP servlet request
     * @param response HTTP servlet response
     * @param context request context
     * @param query query
     * @return records
     * @throws Exception if searching fails
     */
    protected IFeedRecords doSearch(HttpServletRequest request, HttpServletResponse response,
            RequestContext context, RestQuery query) throws Exception {
        MessageBroker msgBroker = new FacesContextBroker(request, response).extractMessageBroker();
        final Map<DiscoveredRecord, Map<String, List<String>>> mapping = new HashMap<DiscoveredRecord, Map<String, List<String>>>();

        List<IFeedRecords.FieldMeta> fields = new ArrayList<IFeedRecords.FieldMeta>();
        loadStdFieldMeta(fields);

        int startRecord = query.getFilter().getStartRecord();
        boolean returnIdsOnly = Val.chkBool(Val.chkStr(request.getParameter("returnIdsOnly")), false);
        if (returnIdsOnly) {
            startRecord = 1;
            query.getFilter().setMaxRecords(1);
            LuceneQueryAdapter tmp = new LuceneQueryAdapter();
            tmp.execute(context, query);
            query.getFilter().setMaxRecords(query.getResult().getNumberOfHits());
        }

        query.getFilter().setStartRecord(startRecord);
        LuceneQueryAdapter lqa = new LuceneQueryAdapter() {
            @Override
            protected void onRecord(DiscoveredRecord record, Document document) {
                Map<String, List<String>> fieldMap = new HashMap<String, List<String>>();
                for (Fieldable field : document.getFields()) {
                    String name = field.name();
                    List<String> fieldValues = fieldMap.get(name);
                    if (fieldValues == null) {
                        fieldValues = new ArrayList<String>();
                        fieldMap.put(name, fieldValues);
                    }
                    fieldValues.add(field.stringValue());
                }
                mapping.put(record, fieldMap);
            }
        };
        lqa.execute(context, query);
        startRecord += query.getFilter().getMaxRecords();

        loadLuceneMeta(context, fields);

        OpenSearchProperties osProps = new OpenSearchProperties();
        osProps.setShortName(msgBroker.retrieveMessage("catalog.openSearch.shortName"));
        osProps.setNumberOfHits(query.getResult().getNumberOfHits());
        osProps.setStartRecord(query.getFilter().getStartRecord());
        osProps.setRecordsPerPage(query.getFilter().getMaxRecords());

        DiscoveredRecordsAdapter discoveredRecordsAdapter = new DiscoveredRecordsAdapter(osProps, fields,
                query.getResult().getRecords(), mapping);

        loadCatalog(context, discoveredRecordsAdapter);

        FeedLinkBuilder linkBuilder = new FeedLinkBuilder(RequestContext.resolveBaseContextPath(request),
                msgBroker);
        for (IFeedRecord record : discoveredRecordsAdapter) {
            linkBuilder.build(record);
        }

        return discoveredRecordsAdapter;
    }

    /**
     * Loads Lucene index metadata.
     * @param context request context
     * @param fields list of fields
     * @throws CatalogIndexException if accessing index fails
     */
    protected void loadLuceneMeta(RequestContext context, List<IFeedRecords.FieldMeta> fields)
            throws CatalogIndexException {
        if (!isLuceneMetaAllowed()) {
            return;
        }

        LuceneIndexAdapter indexAdapter = new LuceneIndexAdapter(context);
        IndexSearcher searcher = null;
        try {
            searcher = indexAdapter.newSearcher();
            IndexReader indexReader = searcher.getIndexReader();
            for (String fieldName : indexReader.getFieldNames(IndexReader.FieldOption.ALL)) {
                fields.add(new IFeedRecords.FieldMeta(IFeedRecord.STD_COLLECTION_INDEX + "." + fieldName,
                        "esriFieldTypeString", fieldName));
            }
        } catch (Exception e) {
            String sMsg = "Error accessing index:\n " + Val.chkStr(e.getMessage());
            throw new CatalogIndexException(sMsg, e);
        } finally {
            indexAdapter.closeSearcher(searcher);
        }
    }

    /**
     * Loads catalog meta.
     * @param context request context
     * @param records records
     * @throws SQLException if accessing catalog database fails
     */
    protected void loadCatalog(RequestContext context, IFeedRecords records) throws SQLException {
        if (!isCatalogMetaAllowed()) {
            return;
        }

        ConnectionBroker connectionBroker = context.getConnectionBroker();
        ManagedConnection managedConnection = connectionBroker.returnConnection("");
        Connection conn = managedConnection.getJdbcConnection();

        boolean firstRecord = true;

        for (IFeedRecord record : records) {
            PreparedStatement st = null;
            ResultSet rs = null;
            try {
                st = conn.prepareStatement("SELECT * FROM GPT_RESOURCE WHERE DOCUUID=?");
                st.setString(1, record.getUuid());

                rs = st.executeQuery();
                if (rs.next()) {
                    ResultSetMetaData metaData = rs.getMetaData();
                    for (int i = 1; i <= metaData.getColumnCount(); i++) {
                        String name = metaData.getColumnName(i);
                        Object value = rs.getObject(i);
                        IFeedAttribute attribute = IFeedAttribute.Factory.create(value,
                                metaData.getColumnDisplaySize(i));
                        if (firstRecord) {
                            records.getMetaData()
                                    .add(new IFeedRecords.FieldMeta(IFeedRecord.STD_COLLECTION_CATALOG + "." + name,
                                            attribute.getEsriType(), name, attribute.getLength()));
                        }
                        record.getData(IFeedRecord.STD_COLLECTION_CATALOG).put(name, attribute);
                    }
                }
                firstRecord = false;
            } finally {
                if (rs != null) {
                    try {
                        rs.close();
                    } catch (SQLException e) {
                    }
                }
                if (st != null) {
                    try {
                        st.close();
                    } catch (SQLException e) {
                    }
                }
            }
        }
    }

    /**
     * Gets gpt.xml configuration parameter.
     * @param paramName parameter name
     * @return parameter value
     */
    protected static String getConfigParam(String paramName) {
        ApplicationContext appCtx = ApplicationContext.getInstance();
        ApplicationConfiguration appCfg = appCtx.getConfiguration();
        return Val.chkStr(appCfg.getCatalogConfiguration().getParameters().getValue(paramName));
    }

    /**
     * Checks if Lucene data is allowed.
     * Reads "json.meta.lucene" configuration parameter.
     * @return <code>true</code> if data is allowed
     */
    protected boolean isLuceneMetaAllowed() {
        String sMeta = getConfigParam("json.meta.lucene");
        return Val.chkBool(sMeta, true);
    }

    /**
     * Checks if catalog data is allowed.
     * Reads "json.meta.catalog" configuration parameter.
     * @return <code>true</code> if data is allowed
     */
    protected boolean isCatalogMetaAllowed() {
        String sMeta = getConfigParam("json.meta.catalog");
        return Val.chkBool(sMeta, true);
    }
}