source: FCSSimpleEndpoint/trunk/src/main/java/eu/clarin/sru/server/fcs/SimpleEndpointSearchEngineBase.java @ 5477

Last change on this file since 5477 was 5477, checked in by margaretha@ids-mannheim.de, 10 years ago

Added endpoint description,
updated SimpleResourceInfoInventory? according to the new format.

  • Property svn:eol-style set to native
File size: 15.0 KB
Line 
1package eu.clarin.sru.server.fcs;
2
3import java.util.ArrayList;
4import java.util.Collections;
5import java.util.List;
6import java.util.Map;
7
8import javax.servlet.ServletContext;
9import javax.xml.stream.XMLStreamException;
10import javax.xml.stream.XMLStreamWriter;
11
12import org.slf4j.Logger;
13import org.slf4j.LoggerFactory;
14import org.z3950.zing.cql.CQLNode;
15import org.z3950.zing.cql.CQLRelation;
16import org.z3950.zing.cql.CQLTermNode;
17import org.z3950.zing.cql.Modifier;
18
19import eu.clarin.sru.server.SRUConfigException;
20import eu.clarin.sru.server.SRUConstants;
21import eu.clarin.sru.server.SRUDiagnosticList;
22import eu.clarin.sru.server.SRUException;
23import eu.clarin.sru.server.SRUExplainResult;
24import eu.clarin.sru.server.SRURequest;
25import eu.clarin.sru.server.SRUScanResultSet;
26import eu.clarin.sru.server.SRUSearchEngine;
27import eu.clarin.sru.server.SRUServerConfig;
28import eu.clarin.sru.server.utils.SRUSearchEngineBase;
29
30
31/**
32 * A base class for implementing a simple search engine to be used as a CLARIN
33 * FCS endpoint.
34 *
35 */
36public abstract class SimpleEndpointSearchEngineBase extends
37        SRUSearchEngineBase {
38   
39//    private static final String X_CMD_RESOURCE_INFO = "x-cmd-resource-info";
40    private static final String X_FCS_ENDPOINT_DESCRIPTION = "x-fcs-endpoint-description";
41   
42    private static final String FCS_SCAN_INDEX_FCS_RESOURCE = "fcs.resource";
43    private static final String FCS_SCAN_INDEX_CQL_SERVERCHOICE = "cql.serverChoice";
44    private static final String FCS_SCAN_SUPPORTED_RELATION_CQL_1_1 = "scr";
45    private static final String FCS_SCAN_SUPPORTED_RELATION_CQL_1_2 = "=";
46    private static final String FCS_SUPPORTED_RELATION_EXACT = "exact";
47    private static final Logger logger =
48            LoggerFactory.getLogger(SimpleEndpointSearchEngineBase.class);
49    protected ResourceInfoInventory resourceInfoInventory;
50   
51    public List<String> capabilities;
52    public List<DataView> supportedDataViews;
53   
54    public void addEndpointCapability(String c){
55        capabilities.add(c);
56    }
57   
58    public void addDataView(DataView d){
59        supportedDataViews.add(d);
60    }
61       
62        /**
63     * This method should not be overridden. Perform your custom initialization
64     * in the {@link #doInit(ServletContext, SRUServerConfig, Map)} method
65     * Instead.
66     *
67     * @see #doInit(ServletContext, SRUServerConfig, Map)
68     */
69    @Override
70    public final void init(ServletContext context, SRUServerConfig config,
71            Map<String, String> params) throws SRUConfigException {
72        logger.debug("initializing");
73        super.init(context, config, params);
74       
75        logger.debug("Setting basic capability");
76        capabilities = new ArrayList<String>();
77        capabilities.add("http://clarin.eu/fcs/capability/basic-search");
78       
79        logger.debug("Setting Generic Hits dataview");
80        supportedDataViews = new ArrayList<DataView>();
81        supportedDataViews.add(
82                        new DataView("The representation of the hit", 
83                                "application/x-clarin-fcs-hits+xml", 
84                                DataView.PayloadDisposition.INLINE, 
85                                DataView.PayloadDelivery.SEND_BY_DEFAULT, 
86                                "hits")
87        );
88       
89        logger.debug("initializing search engine implementation");
90        doInit(context, config, params);
91               
92        logger.debug("initizalizing resource info inventory");
93        this.resourceInfoInventory = createResourceInfoInventory(context, config, params);
94        if (this.resourceInfoInventory == null) {
95            logger.error("ClarinFCSSearchEngineBase implementation error: " +
96                    "initResourceCatalog() returned null");
97            throw new SRUConfigException("initResourceCatalog() returned no " +
98                    "valid implementation of a ResourceCatalog");
99        }
100    }
101
102
103    /**
104     * This method should not be overridden. Perform you custom cleanup in the
105     * {@link #doDestroy()} method.
106     *
107     * @see #doDestroy()
108     */
109    @Override
110    public final void destroy() {
111        logger.debug("performing cleanup of resource info inventory");
112        resourceInfoInventory.destroy();
113        logger.debug("performing cleanup of search engine");
114        doDestroy();
115        super.destroy();
116    }
117
118
119    @Override
120    public final SRUExplainResult explain(SRUServerConfig config,
121            SRURequest request, SRUDiagnosticList diagnostics)
122            throws SRUException {
123        final boolean provideResourceInfo =
124                parseBoolean(request.getExtraRequestData(X_FCS_ENDPOINT_DESCRIPTION));
125        if (provideResourceInfo) {
126            final List<ResourceInfo> resourceInfoList =
127                    resourceInfoInventory.getResourceInfoList(
128                            ResourceInfoInventory.PID_ROOT);
129            return new SRUExplainResult(diagnostics) {
130
131                @Override
132                public boolean hasExtraResponseData() {
133                    return provideResourceInfo;
134                }
135
136                @Override
137                public void writeExtraResponseData(XMLStreamWriter writer)
138                        throws XMLStreamException {
139                        EndpointDescriptionWriter.writeEndpointDescription(writer, 
140                                        capabilities, supportedDataViews, resourceInfoList);
141                    //ResourceInfoWriter.writeFullResourceInfo(writer, null, resourceInfoList);
142                }
143            };
144        } else {
145            return null;
146        }
147    }
148
149
150    /**
151     * Handle a <em>scan</em> operation. This implementation provides support to
152     * CLARIN FCS resource enumeration. If you want to provide custom scan
153     * behavior for a different index, override the
154     * {@link #doScan(SRUServerConfig, SRURequest, SRUDiagnosticList)} method.
155     *
156     * @see #doScan(SRUServerConfig, SRURequest, SRUDiagnosticList)
157     */
158    @Override
159    @Deprecated
160    public final SRUScanResultSet scan(SRUServerConfig config,
161            SRURequest request, SRUDiagnosticList diagnostics)
162            throws SRUException {
163        /*
164         * Check if we got a scan on fcs.resource. If yes, handle it
165         * accordingly, otherwise delegate to user-provided implementation.
166         */
167        final List<ResourceInfo> result =
168                translateFcsScanResource(request.getScanClause());
169        if (result != null) {
170            /*
171             * Make sure, we honor the maximumTerms limit, of the client
172             * requests it ...
173             */
174            final int maxTerms
175                = ((result.size() > 0) && (request.getMaximumTerms() > 0))
176                ? Math.min(result.size(), request.getMaximumTerms())
177                : result.size();
178
179            /*
180             * Shall we provide extended resource information ... ?
181             */
182            final boolean provideResourceInfo = parseBoolean(
183                    request.getExtraRequestData(X_FCS_ENDPOINT_DESCRIPTION));
184
185            return new SRUScanResultSet(diagnostics) {
186                private int idx = -1;
187
188                @Override
189                public boolean nextTerm() {
190                    return (result != null) && (++idx < maxTerms);
191                }
192
193
194                @Override
195                public String getValue() {
196                    return result.get(idx).getPid();
197                }
198
199
200                @Override
201                public int getNumberOfRecords() {
202                    return result.get(idx).getResourceCount();
203                }
204
205
206                @Override
207                public String getDisplayTerm() {
208                    return result.get(idx).getTitle("en");
209                }
210
211
212                @Override
213                public WhereInList getWhereInList() {
214                    return null;
215                }
216
217
218                @Override
219                public boolean hasExtraTermData() {
220                    return provideResourceInfo;
221                }
222
223
224                @Override
225                public void writeExtraTermData(XMLStreamWriter writer)
226                        throws XMLStreamException {
227                    if (provideResourceInfo) {
228                        ResourceInfoWriter.writeResourceInfo(writer, null, result.get(idx));
229                    }
230                }
231            };
232        } else {
233            return doScan(config, request, diagnostics);
234        }
235    }
236
237
238
239    /**
240     * Create the resource info inventory to be used with this endpoint.
241     * Implement this method to provide an implementation of a
242     * {@link ResourceInfoInventory} that is tailored towards your environment
243     * and needs.
244     *
245     * @param context
246     *            the {@link ServletContext} for the Servlet
247     * @param config
248     *            the {@link SRUServerConfig} object for this search engine
249     * @param params
250     *            additional parameters gathered from the Servlet configuration
251     *            and Servlet context.
252     * @return an instance of a {@link ResourceInfoInventory} used by this
253     *         search engine
254     * @throws SRUConfigException
255     *             if an error occurred
256     */
257    protected abstract ResourceInfoInventory createResourceInfoInventory(
258            ServletContext context, SRUServerConfig config,
259            Map<String, String> params) throws SRUConfigException;
260
261
262    /**
263     * Initialize the search engine. This initialization should be tailed
264     * towards your environment and needs.
265     *
266     * @param context
267     *            the {@link ServletContext} for the Servlet
268     * @param config
269     *            the {@link SRUServerConfig} object for this search engine
270     * @param params
271     *            additional parameters gathered from the Servlet configuration
272     *            and Servlet context.
273     * @throws SRUConfigException
274     *             if an error occurred
275     */
276    protected abstract void doInit(ServletContext context,
277            SRUServerConfig config, Map<String, String> params)
278            throws SRUConfigException;
279
280
281    /**
282     * Destroy the search engine. Override this method for any cleanup the
283     * search engine needs to perform upon termination.
284     */
285    protected void doDestroy() {
286    }
287
288
289    /**
290     * Handle a <em>explain</em> operation. The default implementation is a
291     * no-op. Override this method, if you want to provide a custom behavior.
292     *
293     * @see SRUSearchEngine#explain(SRUServerConfig, SRURequest,
294     *      SRUDiagnosticList)
295     */
296    @Deprecated
297    protected SRUScanResultSet doScan(SRUServerConfig config,
298            SRURequest request, SRUDiagnosticList diagnostics)
299            throws SRUException {
300        final CQLNode scanClause = request.getScanClause();
301        if (scanClause instanceof CQLTermNode) {
302            final CQLTermNode root = (CQLTermNode) scanClause;
303            final String index = root.getIndex();
304            throw new SRUException(SRUConstants.SRU_UNSUPPORTED_INDEX, index,
305                    "scan operation on index '" + index + "' is not supported");
306        } else {
307            throw new SRUException(SRUConstants.SRU_QUERY_FEATURE_UNSUPPORTED,
308                    "Scan clause too complex.");
309        }
310    }
311
312
313    /**
314     * Convince method for parsing a string to boolean. Values <code>1</code>,
315     * <code>true</code>, <code>yes</code> yield a <em>true</em> boolean value
316     * as a result, all others (including <code>null</code>) a <em>false</em>
317     * boolean value.
318     *
319     * @param value
320     *            the string to parse
321     * @return <code>true</code> if the supplied string was considered something
322     *         representing a <em>true</em> boolean value, <code>false</code>
323     *         otherwise
324     */
325    protected static boolean parseBoolean(String value) {
326        if (value != null) {
327            return value.equals("1") || Boolean.parseBoolean(value);
328        }
329        return false;
330    }
331
332    @Deprecated
333    private List<ResourceInfo> translateFcsScanResource(CQLNode scanClause)
334            throws SRUException {
335        if (scanClause instanceof CQLTermNode) {
336            final CQLTermNode root = (CQLTermNode) scanClause;
337            logger.debug("index = '{}', relation = '{}', term = '{}'",
338                    new Object[] { root.getIndex(),
339                            root.getRelation().getBase(), root.getTerm() });
340
341            String index = root.getIndex();
342            if (FCS_SCAN_INDEX_CQL_SERVERCHOICE.equals(index) &&
343                    FCS_SCAN_INDEX_FCS_RESOURCE.equals(root.getTerm())) {
344                throw new SRUException(SRUConstants.SRU_UNSUPPORTED_INDEX,
345                        "scan operation with 'scanClause' with value " +
346                        "'fcs.resource' is deprecated within CLARIN-FCS");
347            }
348            if (!(FCS_SCAN_INDEX_FCS_RESOURCE.equals(index))) {
349                logger.debug("got scan operation on index '{}', bailing ...",
350                        index);
351                return null;
352            }
353
354
355            // only allow "=" relation without any modifiers
356            final CQLRelation relationNode = root.getRelation();
357            String relation = relationNode.getBase();
358            if (!(FCS_SCAN_SUPPORTED_RELATION_CQL_1_1.equals(relation) ||
359                    FCS_SCAN_SUPPORTED_RELATION_CQL_1_2.equals(relation) ||
360                    FCS_SUPPORTED_RELATION_EXACT.equals(relation))) {
361                throw new SRUException(SRUConstants.SRU_UNSUPPORTED_RELATION,
362                        relationNode.getBase(), "Relation \"" +
363                                relationNode.getBase() +
364                                "\" is not supported in scan operation.");
365            }
366            final List<Modifier> modifiers = relationNode.getModifiers();
367            if ((modifiers != null) && !modifiers.isEmpty()) {
368                Modifier modifier = modifiers.get(0);
369                throw new SRUException(
370                        SRUConstants.SRU_UNSUPPORTED_RELATION_MODIFIER,
371                        modifier.getValue(), "Relation modifier \"" +
372                                modifier.getValue() +
373                                "\" is not supported in scan operation.");
374            }
375
376            final String term = root.getTerm();
377            if ((term == null) || term.isEmpty()) {
378                throw new SRUException(SRUConstants.SRU_EMPTY_TERM_UNSUPPORTED,
379                        "An empty term is not supported in scan operation.");
380            }
381
382            /*
383             * generate result: currently we only have a flat hierarchy, so
384             * return an empty result on any attempt to do a recursive scan ...
385             */
386            List<ResourceInfo> results = null;
387            if ((FCS_SCAN_INDEX_CQL_SERVERCHOICE.equals(index) &&
388                    FCS_SCAN_INDEX_FCS_RESOURCE.equals(term)) ||
389                    (FCS_SCAN_INDEX_FCS_RESOURCE.equals(index))) {
390                results = resourceInfoInventory.getResourceInfoList(term);
391            }
392            if ((results == null) || results.isEmpty()) {
393                return Collections.emptyList();
394            } else {
395                return results;
396            }
397        } else {
398            throw new SRUException(SRUConstants.SRU_QUERY_FEATURE_UNSUPPORTED,
399                    "Scan clause too complex.");
400        }
401    }
402
403
404   
405
406} // class SimpleEndpointSearchEngineBase
Note: See TracBrowser for help on using the repository browser.