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

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