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

Last change on this file since 6935 was 6935, checked in by Oliver Schonefeld, 8 years ago
  • update/add copyright
  • fix typo in class name
  • minor changes
  • Property svn:eol-style set to native
File size: 16.6 KB
Line 
1/**
2 * This software is copyright (c) 2013-2016 by
3 *  - Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
4 * This is free software. You can redistribute it
5 * and/or modify it under the terms described in
6 * the GNU General Public License v3 of which you
7 * should have received a copy. Otherwise you can download
8 * it from
9 *
10 *   http://www.gnu.org/licenses/gpl-3.0.txt
11 *
12 * @copyright Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
13 *
14 * @license http://www.gnu.org/licenses/gpl-3.0.txt
15 *  GNU General Public License v3
16 */
17package eu.clarin.sru.server.fcs;
18
19import java.net.URI;
20import java.util.List;
21import java.util.Map;
22
23import javax.servlet.ServletContext;
24import javax.xml.XMLConstants;
25import javax.xml.stream.XMLStreamException;
26import javax.xml.stream.XMLStreamWriter;
27
28import org.slf4j.Logger;
29import org.slf4j.LoggerFactory;
30import org.z3950.zing.cql.CQLNode;
31import org.z3950.zing.cql.CQLTermNode;
32import eu.clarin.sru.server.SRUConfigException;
33import eu.clarin.sru.server.SRUConstants;
34import eu.clarin.sru.server.SRUDiagnosticList;
35import eu.clarin.sru.server.SRUException;
36import eu.clarin.sru.server.SRUExplainResult;
37import eu.clarin.sru.server.SRUQueryParserRegistry;
38import eu.clarin.sru.server.SRURequest;
39import eu.clarin.sru.server.SRUScanResultSet;
40import eu.clarin.sru.server.SRUSearchEngine;
41import eu.clarin.sru.server.SRUServer;
42import eu.clarin.sru.server.SRUServerConfig;
43import eu.clarin.sru.server.utils.SRUSearchEngineBase;
44
45
46/**
47 * A base class for implementing a simple search engine to be used as a
48 * CLARIN-FCS endpoint.
49 *
50 */
51public abstract class SimpleEndpointSearchEngineBase extends
52        SRUSearchEngineBase {
53    private static final String X_FCS_ENDPOINT_DESCRIPTION =
54            "x-fcs-endpoint-description";
55    private static final String ED_NS =
56            "http://clarin.eu/fcs/endpoint-description";
57    private static final String ED_PREFIX = "ed";
58    private static final int ED_VERSION = 2;
59    private static final Logger logger =
60            LoggerFactory.getLogger(SimpleEndpointSearchEngineBase.class);
61    protected EndpointDescription endpointDescription;
62
63
64    /**
65     * This method should not be overridden. Perform your custom initialization
66     * in the {@link #doInit(ServletContext, SRUServerConfig, Map)} method
67     * Instead.
68     *
69     * @see #doInit(ServletContext, SRUServerConfig, Map)
70     */
71    @Override
72    public final void init(ServletContext context,
73            SRUServerConfig config,
74            SRUQueryParserRegistry.Builder parserReqistryBuilder,
75            Map<String, String> params) throws SRUConfigException {
76        logger.debug("initializing");
77        super.init(context, config, parserReqistryBuilder, params);
78
79        parserReqistryBuilder.register(new FCSQueryParser());
80
81        logger.debug("initializing search engine implementation");
82        doInit(context, config, parserReqistryBuilder, params);
83
84        logger.debug("initizalizing endpoint description");
85        this.endpointDescription =
86                createEndpointDescription(context, config, params);
87        if (this.endpointDescription == null) {
88            logger.error("SimpleEndpointSearchEngineBase implementation " +
89                    "error: createEndpointDescription() returned null");
90            throw new SRUConfigException("createEndpointDescription() " +
91                    "returned no valid implementation of an EndpointDescription");
92        }
93    }
94
95
96    /**
97     * This method should not be overridden. Perform you custom cleanup in the
98     * {@link #doDestroy()} method.
99     *
100     * @see #doDestroy()
101     */
102    @Override
103    public final void destroy() {
104        logger.debug("performing cleanup of endpoint description");
105        endpointDescription.destroy();
106        logger.debug("performing cleanup of search engine");
107        doDestroy();
108        super.destroy();
109    }
110
111
112    @Override
113    public final SRUExplainResult explain(SRUServerConfig config,
114            SRURequest request, SRUDiagnosticList diagnostics)
115            throws SRUException {
116
117        final boolean provideEndpointDescription =
118                parseBoolean(request.getExtraRequestData(
119                        X_FCS_ENDPOINT_DESCRIPTION));
120
121        if (provideEndpointDescription) {
122            return new SRUExplainResult(diagnostics) {
123                @Override
124                public boolean hasExtraResponseData() {
125                    return provideEndpointDescription;
126                }
127
128
129                @Override
130                public void writeExtraResponseData(XMLStreamWriter writer)
131                        throws XMLStreamException {
132                    writeEndpointDescription(writer);
133                }
134            };
135        } else {
136            return null;
137        }
138    }
139
140
141    /**
142     * Handle a <em>scan</em> operation. This implementation provides support to
143     * CLARIN FCS resource enumeration. If you want to provide custom scan
144     * behavior for a different index, override the
145     * {@link #doScan(SRUServerConfig, SRURequest, SRUDiagnosticList)} method.
146     *
147     * @see #doScan(SRUServerConfig, SRURequest, SRUDiagnosticList)
148     */
149    @Override
150    public final SRUScanResultSet scan(SRUServerConfig config,
151            SRURequest request, SRUDiagnosticList diagnostics)
152            throws SRUException {
153        return doScan(config, request, diagnostics);
154    }
155
156
157    protected abstract EndpointDescription createEndpointDescription(
158            ServletContext context, SRUServerConfig config,
159            Map<String, String> params) throws SRUConfigException;
160
161
162    /**
163     * Initialize the search engine. This initialization should be tailed
164     * towards your environment and needs.
165     *
166     * @param context
167     *            the {@link ServletContext} for the Servlet
168     * @param config
169     *            the {@link SRUServerConfig} object for this search engine
170     * @param parsersRegistryBuilder
171     *            the {@link SRUQueryParserRegistry.Builder} object to be used
172     *            for this search engine. Use to register additional query
173     *            parsers with the {@link SRUServer}.
174     * @param params
175     *            additional parameters gathered from the Servlet configuration
176     *            and Servlet context.
177     * @throws SRUConfigException
178     *             if an error occurred
179     */
180    protected abstract void doInit(ServletContext context,
181            SRUServerConfig config,
182            SRUQueryParserRegistry.Builder queryParsersBuilder,
183            Map<String, String> params) throws SRUConfigException;
184
185
186    /**
187     * Destroy the search engine. Override this method for any cleanup the
188     * search engine needs to perform upon termination.
189     */
190    protected void doDestroy() {
191    }
192
193
194    /**
195     * Handle a <em>scan</em> operation. The default implementation is a no-op.
196     * Override this method, if you want to provide a custom behavior.
197     *
198     * @param config
199     *            the <code>SRUEndpointConfig</code> object that contains the
200     *            endpoint configuration
201     * @param request
202     *            the <code>SRURequest</code> object that contains the request
203     *            made to the endpoint
204     * @param diagnostics
205     *            the <code>SRUDiagnosticList</code> object for storing
206     *            non-fatal diagnostics
207     * @return a <code>SRUScanResultSet</code> object or <code>null</code> if
208     *         this operation is not supported by this search engine
209     * @throws SRUException
210     *             if an fatal error occurred
211     * @see SRUSearchEngine#scan(SRUServerConfig, SRURequest, SRUDiagnosticList)
212     * @deprecated override
213     *             {@link #scan(SRUServerConfig, SRURequest, SRUDiagnosticList)}
214     */
215    @Deprecated
216    protected SRUScanResultSet doScan(SRUServerConfig config,
217            SRURequest request, SRUDiagnosticList diagnostics)
218            throws SRUException {
219        final CQLNode scanClause = request.getScanClause();
220        if (scanClause instanceof CQLTermNode) {
221            final CQLTermNode root = (CQLTermNode) scanClause;
222            final String index = root.getIndex();
223            throw new SRUException(SRUConstants.SRU_UNSUPPORTED_INDEX, index,
224                    "scan operation on index '" + index + "' is not supported");
225        } else {
226            throw new SRUException(SRUConstants.SRU_QUERY_FEATURE_UNSUPPORTED,
227                    "Scan clause too complex.");
228        }
229    }
230
231
232    /**
233     * Convince method for parsing a string to boolean. Values <code>1</code>,
234     * <code>true</code>, <code>yes</code> yield a <em>true</em> boolean value
235     * as a result, all others (including <code>null</code>) a <em>false</em>
236     * boolean value.
237     *
238     * @param value
239     *            the string to parse
240     * @return <code>true</code> if the supplied string was considered something
241     *         representing a <em>true</em> boolean value, <code>false</code>
242     *         otherwise
243     */
244    protected static boolean parseBoolean(String value) {
245        if (value != null) {
246            return value.equals("1") || Boolean.parseBoolean(value);
247        }
248        return false;
249    }
250
251
252    private void writeEndpointDescription(XMLStreamWriter writer)
253            throws XMLStreamException {
254        writer.setPrefix(ED_PREFIX, ED_NS);
255        writer.writeStartElement(ED_NS, "EndpointDescription");
256        writer.writeNamespace(ED_PREFIX, ED_NS);
257        writer.writeAttribute("version", Integer.toString(ED_VERSION));
258
259        // Capabilities
260        writer.writeStartElement(ED_NS, "Capabilities");
261        for (URI capability : endpointDescription.getCapabilities()) {
262            writer.writeStartElement(ED_NS, "Capability");
263            writer.writeCharacters(capability.toString());
264            writer.writeEndElement(); // "Capability" element
265        }
266        writer.writeEndElement(); // "Capabilities" element
267
268        // SupportedDataViews
269        writer.writeStartElement(ED_NS, "SupportedDataViews");
270        for (DataView dataView : endpointDescription.getSupportedDataViews()) {
271            writer.writeStartElement(ED_NS, "SupportedDataView");
272            writer.writeAttribute("id", dataView.getIdentifier());
273            String s;
274            switch (dataView.getDeliveryPolicy()) {
275            case SEND_BY_DEFAULT:
276                s = "send-by-default";
277                break;
278            case NEED_TO_REQUEST:
279                s = "need-to-request";
280                break;
281            default:
282                throw new XMLStreamException(
283                        "invalid value for payload delivery policy: " +
284                                dataView.getDeliveryPolicy());
285            } // switch
286            writer.writeAttribute("delivery-policy", s);
287            writer.writeCharacters(dataView.getMimeType());
288            writer.writeEndElement(); // "SupportedDataView" element
289        }
290        writer.writeEndElement(); // "SupportedDataViews" element
291
292        // SupportedLayers
293        final List<Layer> layers = endpointDescription.getSupportedLayers();
294        if (layers != null) {
295            writer.writeStartElement(ED_NS, "SupportedLayers");
296            for (Layer layer : layers) {
297                writer.writeStartElement(ED_NS, "SupportedLayer");
298                writer.writeAttribute("id", layer.getId());
299                writer.writeAttribute("result-id",
300                        layer.getResultId().toString());
301                if (layer.getContentEncoding() == Layer.ContentEncoding.EMPTY) {
302                    writer.writeAttribute("type", "empty");
303                }
304                if (layer.getQualifier() != null) {
305                    writer.writeAttribute("qualifier", layer.getQualifier());
306                }
307                if (layer.getAltValueInfo() != null) {
308                    writer.writeAttribute("alt-value-info",
309                            layer.getAltValueInfo());
310                    if (layer.getAltValueInfoURI() != null) {
311                        writer.writeAttribute("alt-value-info-uri",
312                                layer.getAltValueInfoURI().toString());
313                    }
314                }
315                writer.writeCharacters(layer.getType());
316                writer.writeEndElement(); // "SupportedLayer" element
317            }
318            writer.writeEndElement(); // "SupportedLayers" element
319
320        }
321
322        // Resources
323        try {
324            List<ResourceInfo> resources =
325                    endpointDescription.getResourceList(
326                            EndpointDescription.PID_ROOT);
327            writeResourceInfos(writer, resources);
328        } catch (SRUException e) {
329            throw new XMLStreamException("error retriving top-level resources",
330                    e);
331        }
332        writer.writeEndElement(); // "EndpointDescription" element
333    }
334
335
336    private void writeResourceInfos(XMLStreamWriter writer,
337            List<ResourceInfo> resources) throws XMLStreamException {
338        if (resources == null) {
339            throw new NullPointerException("resources == null");
340        }
341        if (!resources.isEmpty()) {
342            writer.writeStartElement(ED_NS, "Resources");
343
344            for (ResourceInfo resource : resources) {
345                writer.writeStartElement(ED_NS, "Resource");
346                writer.writeAttribute("pid", resource.getPid());
347
348                // title
349                final Map<String, String> title = resource.getTitle();
350                for (Map.Entry<String, String> i : title.entrySet()) {
351                    writer.setPrefix(XMLConstants.XML_NS_PREFIX,
352                            XMLConstants.XML_NS_URI);
353                    writer.writeStartElement(ED_NS, "Title");
354                    writer.writeAttribute(XMLConstants.XML_NS_URI, "lang", i.getKey());
355                    writer.writeCharacters(i.getValue());
356                    writer.writeEndElement(); // "title" element
357                }
358
359                // description
360                final Map<String, String> description = resource.getDescription();
361                if (description != null) {
362                    for (Map.Entry<String, String> i : description.entrySet()) {
363                        writer.writeStartElement(ED_NS, "Description");
364                        writer.writeAttribute(XMLConstants.XML_NS_URI, "lang",
365                                i.getKey());
366                        writer.writeCharacters(i.getValue());
367                        writer.writeEndElement(); // "Description" element
368                    }
369                }
370
371                // landing page
372                final String landingPageURI = resource.getLandingPageURI();
373                if (landingPageURI != null) {
374                    writer.writeStartElement(ED_NS, "LandingPageURI");
375                    writer.writeCharacters(landingPageURI);
376                    writer.writeEndElement(); // "LandingPageURI" element
377                }
378
379                // languages
380                final List<String> languages = resource.getLanguages();
381                writer.writeStartElement(ED_NS, "Languages");
382                for (String i : languages) {
383                    writer.writeStartElement(ED_NS, "Language");
384                    writer.writeCharacters(i);
385                    writer.writeEndElement(); // "Language" element
386
387                }
388                writer.writeEndElement(); // "Languages" element
389
390                // available data views
391                StringBuilder sb = new StringBuilder();
392                for (DataView dataview : resource.getAvailableDataViews()) {
393                    if (sb.length() > 0) {
394                        sb.append(" ");
395                    }
396                    sb.append(dataview.getIdentifier());
397                }
398                writer.writeEmptyElement(ED_NS, "AvailableDataViews");
399                writer.writeAttribute("ref", sb.toString());
400
401                final List<Layer> layers = resource.getAvailableLayers();
402                if (layers != null) {
403                    sb = new StringBuilder();
404                    for (Layer layer : resource.getAvailableLayers()) {
405                        if (sb.length() > 0) {
406                            sb.append(" ");
407                        }
408                        sb.append(layer.getId());
409                    }
410                    writer.writeEmptyElement(ED_NS, "AvailableLayers");
411                    writer.writeAttribute("ref", sb.toString());
412                }
413
414                // child resources
415                List<ResourceInfo> subs = resource.getSubResources();
416                if ((subs != null) && !subs.isEmpty()) {
417                    writeResourceInfos(writer, subs);
418                }
419
420                writer.writeEndElement(); // "Resource" element
421            }
422            writer.writeEndElement(); // "Resources" element
423        }
424    }
425
426} // class SimpleEndpointSearchEngineBase
Note: See TracBrowser for help on using the repository browser.