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

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

Enabled x-cmd-resource-info

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