Changeset 5546
- Timestamp:
- 08/09/14 20:45:17 (10 years ago)
- Location:
- FCSSimpleEndpoint/trunk
- Files:
-
- 2 added
- 4 deleted
- 5 edited
- 2 moved
Legend:
- Unmodified
- Added
- Removed
-
FCSSimpleEndpoint/trunk/pom.xml
r5477 r5546 4 4 <groupId>eu.clarin.sru.fcs</groupId> 5 5 <artifactId>fcs-simple-endpoint</artifactId> 6 <version> 2.0-SNAPSHOT</version>6 <version>1.3.0-SNAPSHOT</version> 7 7 <packaging>jar</packaging> 8 8 <name>A simple CLARIN FCS endpoint</name> … … 18 18 <maven.compiler.target>1.6</maven.compiler.target> 19 19 <!-- versions of common dependencies --> 20 <slf4j.version>1.7. 2</slf4j.version>20 <slf4j.version>1.7.7</slf4j.version> 21 21 </properties> 22 22 … … 45 45 <artifactId>woodstox-core-lgpl</artifactId> 46 46 <version>4.1.3</version> 47 <exclusions> 48 <!-- StAX comes with Java 1.6 --> 49 <exclusion> 50 <artifactId>stax-api</artifactId> 51 <groupId>javax.xml.stream</groupId> 52 </exclusion> 53 <exclusion> 54 <artifactId>stax-api</artifactId> 55 <groupId>stax</groupId> 56 </exclusion> 57 </exclusions> 47 58 </dependency> 48 59 </dependencies> -
FCSSimpleEndpoint/trunk/src/main/java/eu/clarin/sru/server/fcs/DataView.java
r5477 r5546 1 1 package eu.clarin.sru.server.fcs; 2 2 3 /** 4 * This class is used to hold information about a data view that is implemented 5 * by the endpoint. 6 */ 3 7 public class DataView { 8 /** 9 * Enumeration to indicate the delivery policy of a data view. 10 */ 11 public enum DeliveryPolicy { 12 /** 13 * The data view is sent automatically by the endpoint. 14 */ 15 SEND_BY_DEFAULT, 16 /** 17 * A client must explicitly request the endpoint. 18 */ 19 NEED_TO_REQUEST; 20 } // enum PayloadDelivery 21 private final String identifier; 22 private final String mimeType; 23 private final DeliveryPolicy deliveryPolicy; 4 24 5 public enum PayloadDelivery {6 SEND_BY_DEFAULT, NEED_TO_REQUEST;7 8 @Override9 public String toString(){10 String str = super.toString().toLowerCase();11 return str.replace("_", "-");12 }13 }14 25 15 public enum PayloadDisposition { 16 INLINE, REFERENCE; 17 18 @Override 19 public String toString(){ 20 return super.toString().toLowerCase(); 21 } 22 } 23 24 private String description; 25 private String mimeType; 26 private String payloadDisposition; 27 private String payloadDelivery; 28 private String shortIdentifier; 29 30 public DataView() { } 31 public DataView(String description, String mimeType, 32 PayloadDisposition payloadDisposition, PayloadDelivery payloadDelivery, 33 String shortId){ 34 this.description = description; 35 this.mimeType = mimeType; 36 this.payloadDisposition = payloadDisposition.toString(); 37 this.payloadDelivery = payloadDelivery.toString(); 38 this.shortIdentifier = shortId; 39 } 40 41 42 public String getDescription() { 43 return description; 44 } 45 public void setDescription(String description) { 46 this.description = description; 47 } 48 public String getMimeType() { 49 return mimeType; 50 } 51 public void setMimeType(String mimeType) { 52 this.mimeType = mimeType; 53 } 54 public String getPayloadDisposition() { 55 return payloadDisposition; 56 } 57 public void setPayloadDisposition(String payloadDisposition) { 58 this.payloadDisposition = payloadDisposition; 59 } 60 public String getPayloadDelivery() { 61 return payloadDelivery; 62 } 63 public void setPayloadDelivery(String payloadDelivery) { 64 this.payloadDelivery = payloadDelivery; 65 } 66 public String getShortIdentifier() { 67 return shortIdentifier; 68 } 69 public void setShortIdentifier(String shortIdentifier) { 70 this.shortIdentifier = shortIdentifier; 71 } 72 } 26 /** 27 * Constructor. 28 * 29 * @param identifier 30 * a unique short identifier for the data view 31 * @param mimeType 32 * the MIME type of the data view 33 * @param deliveryPolicy 34 * the delivery policy for this data view 35 */ 36 public DataView(String identifier, String mimeType, 37 DeliveryPolicy deliveryPolicy) { 38 if (identifier == null) { 39 throw new NullPointerException("identifier == null"); 40 } 41 if (identifier.isEmpty()) { 42 throw new IllegalArgumentException("identifier is empty"); 43 } 44 this.identifier = identifier; 45 46 if (mimeType == null) { 47 throw new NullPointerException("mimeType == null"); 48 } 49 if (mimeType.isEmpty()) { 50 throw new IllegalArgumentException("mimeType is empty"); 51 } 52 this.mimeType = mimeType; 53 54 if (deliveryPolicy == null) { 55 throw new NullPointerException("deliveryPolicy == null"); 56 } 57 this.deliveryPolicy = deliveryPolicy; 58 } 59 60 61 public String getIdentifier() { 62 return identifier; 63 } 64 65 66 public String getMimeType() { 67 return mimeType; 68 } 69 70 71 public DeliveryPolicy getDeliveryPolicy() { 72 return deliveryPolicy; 73 } 74 75 76 @Override 77 public String toString() { 78 StringBuilder sb = new StringBuilder(); 79 sb.append(getClass().getSimpleName()); 80 sb.append("["); 81 sb.append("identifier=").append(identifier); 82 sb.append(", mimeType=").append(mimeType); 83 sb.append("]"); 84 return sb.toString(); 85 } 86 } // class DataView -
FCSSimpleEndpoint/trunk/src/main/java/eu/clarin/sru/server/fcs/ResourceInfo.java
r5485 r5546 9 9 * This class implements a resource info record, which provides supplementary 10 10 * information about a resource that is available at the endpoint. 11 * 12 * @see ResourceInfoInventory11 * 12 * @see EndpointDescription 13 13 */ 14 14 public class ResourceInfo { 15 15 private final String pid; 16 @Deprecated17 private final int resourceCount;18 16 private final Map<String, String> title; 19 17 private final Map<String, String> description; 20 18 private final String landingPageURI; 21 19 private final List<String> languages; 22 private List<String> availableDataViews;23 private List<ResourceInfo> subResources;24 20 private final List<DataView> availableDataViews; 21 private final List<ResourceInfo> subResources; 22 25 23 26 24 /** … … 29 27 * @param pid 30 28 * the persistent identifier of the resource 31 * @param resourceCount32 * the number of items within the resource or <code>-1</code> if33 * not applicable34 29 * @param title 35 30 * the title of the resource represented as a map with pairs of … … 45 40 * the languages represented within this resource represented as 46 41 * a list of ISO-632-3 three letter language codes 42 * @param availableDataViews 43 * the list of available data views for this resource 47 44 * @param subResources 48 45 * a list of resource sub-ordinate to this resource or 49 46 * <code>null</code> if not applicable 50 47 */ 51 public ResourceInfo(String pid, int resourceCount,52 Map<String, String> title, Map<String, String> description,53 String landingPageURI, List<String> languages,48 public ResourceInfo(String pid, Map<String, String> title, 49 Map<String, String> description, String landingPageURI, 50 List<String> languages, List<DataView> availableDataViews, 54 51 List<ResourceInfo> subResources) { 55 52 if (pid == null) { 56 throw new NullPointerException(" id == null");53 throw new NullPointerException("pid == null"); 57 54 } 58 55 this.pid = pid; 59 this.resourceCount = (resourceCount > 0) ? resourceCount : -1; 56 60 57 if (title == null) { 61 58 throw new NullPointerException("title == null"); … … 70 67 this.description = null; 71 68 } 69 72 70 this.landingPageURI = landingPageURI; 73 71 if (languages == null) { … … 78 76 } 79 77 this.languages = languages; 80 78 79 if (availableDataViews == null) { 80 throw new IllegalArgumentException("availableDataViews == null"); 81 } 82 this.availableDataViews = 83 Collections.unmodifiableList(availableDataViews); 84 81 85 if ((subResources != null) && !subResources.isEmpty()) { 82 86 this.subResources = Collections.unmodifiableList(subResources); 83 87 } else { 84 88 this.subResources = null; 85 } 89 } 86 90 } 87 91 88 public ResourceInfo(String pid, int resourceCount,89 Map<String, String> title, Map<String, String> description,90 String landingPageURI, List<String> languages,91 List<String> availableDataViews,92 List<ResourceInfo> subResources) {93 this(pid,resourceCount, title, description, landingPageURI, languages, subResources);94 95 if (availableDataViews == null){96 throw new IllegalArgumentException("available data views == null");97 }98 if (availableDataViews.isEmpty()){99 throw new IllegalArgumentException("available data views are empty");100 }101 this.availableDataViews = availableDataViews;102 }103 104 92 105 93 /** … … 110 98 public String getPid() { 111 99 return pid; 112 }113 114 115 /**116 * Get the number of resources within this resource. If this resource has117 * sub-ordinate resources, this number should be the sum of all items within118 * the sub-ordinate resources plus the number of items within this resource.119 *120 * @return a number of items or <code>-1</code> if not applicable121 */122 @Deprecated123 public int getResourceCount() {124 return resourceCount;125 100 } 126 101 … … 217 192 218 193 219 public List< String> getAvailableDataViews() {194 public List<DataView> getAvailableDataViews() { 220 195 return availableDataViews; 221 196 } 222 197 223 224 198 } // class ResourceInfo -
FCSSimpleEndpoint/trunk/src/main/java/eu/clarin/sru/server/fcs/SimpleEndpointSearchEngineBase.java
r5485 r5546 1 1 package eu.clarin.sru.server.fcs; 2 2 3 import java. util.ArrayList;3 import java.net.URI; 4 4 import java.util.Collections; 5 5 import java.util.List; … … 7 7 8 8 import javax.servlet.ServletContext; 9 import javax.xml.XMLConstants; 9 10 import javax.xml.stream.XMLStreamException; 10 11 import javax.xml.stream.XMLStreamWriter; … … 30 31 31 32 /** 32 * A base class for implementing a simple search engine to be used as a CLARIN33 * FCS endpoint.33 * A base class for implementing a simple search engine to be used as a 34 * CLARIN-FCS endpoint. 34 35 * 35 36 */ 36 37 public abstract class SimpleEndpointSearchEngineBase extends 37 38 SRUSearchEngineBase { 38 39 private static final String X_FCS_ENDPOINT_DESCRIPTION = "x-fcs-endpoint-description"; 39 40 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 41 private static final String ED_NS = "http://clarin.eu/fcs/endpoint-description"; 42 private static final String ED_PREFIX = "ed"; 43 private static final int ED_VERSION = 1; 44 private static final String FCS_RESOURCE_INFO_NS = "http://clarin.eu/fcs/1.0/resource-info"; 42 45 private static final String FCS_SCAN_INDEX_FCS_RESOURCE = "fcs.resource"; 43 46 private static final String FCS_SCAN_INDEX_CQL_SERVERCHOICE = "cql.serverChoice"; … … 47 50 private static final Logger logger = 48 51 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 /** 52 protected EndpointDescription endpointDescription; 53 54 55 /** 81 56 * This method should not be overridden. Perform your custom initialization 82 57 * in the {@link #doInit(ServletContext, SRUServerConfig, Map)} method … … 89 64 Map<String, String> params) throws SRUConfigException { 90 65 logger.debug("initializing"); 91 super.init(context, config, params); 92 66 super.init(context, config, params); 67 93 68 logger.debug("initializing search engine implementation"); 94 69 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"); 70 71 logger.debug("initizalizing endpoint description"); 72 this.endpointDescription = 73 createEndpointDescription(context, config, params); 74 if (this.endpointDescription == null) { 75 logger.error("SimpleEndpointSearchEngineBase implementation " + 76 "error: createEndpointDescription() returned null"); 77 throw new SRUConfigException("createEndpointDescription() " + 78 "returned no valid implementation of an EndpointDescription"); 103 79 } 104 80 } … … 113 89 @Override 114 90 public final void destroy() { 115 logger.debug("performing cleanup of resource info inventory");116 resourceInfoInventory.destroy();91 logger.debug("performing cleanup of endpoint description"); 92 endpointDescription.destroy(); 117 93 logger.debug("performing cleanup of search engine"); 118 94 doDestroy(); … … 125 101 SRURequest request, SRUDiagnosticList diagnostics) 126 102 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 103 104 final boolean provideEndpointDescription = 105 parseBoolean(request.getExtraRequestData( 106 X_FCS_ENDPOINT_DESCRIPTION)); 107 108 if (provideEndpointDescription) { 138 109 return new SRUExplainResult(diagnostics) { 139 110 @Override 140 111 public boolean hasExtraResponseData() { 141 return provideCmdResourceInfo; 142 } 112 return provideEndpointDescription; 113 } 114 115 143 116 @Override 144 117 public void writeExtraResponseData(XMLStreamWriter writer) 145 118 throws XMLStreamException { 146 ResourceInfoWriter.writeFullResourceInfo(writer, null, resourceInfoList);119 writeEndpointDescription(writer); 147 120 } 148 121 }; 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 { 122 } else { 179 123 return null; 180 124 } … … 191 135 */ 192 136 @Override 193 @Deprecated194 137 public final SRUScanResultSet scan(SRUServerConfig config, 195 138 SRURequest request, SRUDiagnosticList diagnostics) … … 215 158 */ 216 159 final boolean provideResourceInfo = parseBoolean( 217 request.getExtraRequestData(X_ FCS_ENDPOINT_DESCRIPTION));160 request.getExtraRequestData(X_CMD_RESOURCE_INFO)); 218 161 219 162 return new SRUScanResultSet(diagnostics) { … … 234 177 @Override 235 178 public int getNumberOfRecords() { 236 return result.get(idx).getResourceCount();179 return -1; 237 180 } 238 181 … … 260 203 throws XMLStreamException { 261 204 if (provideResourceInfo) { 262 ResourceInfoWriter.writeResourceInfo(writer, null, result.get(idx));205 writeLegacyResourceInfo(writer, result.get(idx)); 263 206 } 264 207 } … … 270 213 271 214 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( 215 protected abstract EndpointDescription createEndpointDescription( 292 216 ServletContext context, SRUServerConfig config, 293 217 Map<String, String> params) throws SRUConfigException; … … 328 252 * SRUDiagnosticList) 329 253 */ 330 @Deprecated331 254 protected SRUScanResultSet doScan(SRUServerConfig config, 332 255 SRURequest request, SRUDiagnosticList diagnostics) … … 364 287 } 365 288 366 @Deprecated 289 367 290 private List<ResourceInfo> translateFcsScanResource(CQLNode scanClause) 368 291 throws SRUException { … … 386 309 } 387 310 311 logger.warn("scan on 'fcs.resource' for endpoint resource " + 312 "enumeration is deprecated."); 388 313 389 314 // only allow "=" relation without any modifiers … … 422 347 FCS_SCAN_INDEX_FCS_RESOURCE.equals(term)) || 423 348 (FCS_SCAN_INDEX_FCS_RESOURCE.equals(index))) { 424 results = resourceInfoInventory.getResourceInfoList(term);349 results = endpointDescription.getResourceList(term); 425 350 } 426 351 if ((results == null) || results.isEmpty()) { … … 436 361 437 362 438 363 private void writeEndpointDescription(XMLStreamWriter writer) 364 throws XMLStreamException { 365 writer.setPrefix(ED_PREFIX, ED_NS); 366 writer.writeStartElement(ED_NS, "EndpointDescription"); 367 writer.writeNamespace(ED_PREFIX, ED_NS); 368 writer.writeAttribute("version", Integer.toString(ED_VERSION)); 369 370 // capabilities 371 writer.writeStartElement(ED_NS, "Capabilities"); 372 for (URI capability : endpointDescription.getCapabilities()) { 373 writer.writeStartElement(ED_NS, "Capability"); 374 writer.writeCharacters(capability.toString()); 375 writer.writeEndElement(); // "Capability" element 376 } 377 writer.writeEndElement(); // "Capabilities" element 378 379 // supported data views 380 writer.writeStartElement(ED_NS, "SupportedDataViews"); 381 for (DataView dataView : endpointDescription.getSupportedDataViews()) { 382 writer.writeStartElement(ED_NS, "SupportedDataView"); 383 writer.writeAttribute("id", dataView.getIdentifier()); 384 String s; 385 switch (dataView.getDeliveryPolicy()) { 386 case SEND_BY_DEFAULT: 387 s = "send-by-default"; 388 break; 389 case NEED_TO_REQUEST: 390 s = "need-to-request"; 391 break; 392 default: 393 throw new XMLStreamException( 394 "invalid value for payload delivery policy: " + 395 dataView.getDeliveryPolicy()); 396 } // switch 397 writer.writeAttribute("delivery-policy", s); 398 writer.writeCharacters(dataView.getMimeType()); 399 writer.writeEndElement(); // "SupportedDataView" element 400 } 401 writer.writeEndElement(); // "SupportedDataViews" element 402 403 try { 404 // resources 405 List<ResourceInfo> resources = 406 endpointDescription.getResourceList( 407 EndpointDescription.PID_ROOT); 408 writeResourceInfos(writer, resources); 409 } catch (SRUException e) { 410 throw new XMLStreamException("error retriving top-level resources", 411 e); 412 } 413 writer.writeEndElement(); // "EndpointDescription" element 414 } 415 416 417 private void writeResourceInfos(XMLStreamWriter writer, 418 List<ResourceInfo> resources) throws XMLStreamException { 419 if (resources == null) { 420 throw new NullPointerException("resources == null"); 421 } 422 if (!resources.isEmpty()) { 423 writer.writeStartElement(ED_NS, "Resources"); 424 425 for (ResourceInfo resource : resources) { 426 writer.writeStartElement(ED_NS, "Resource"); 427 writer.writeAttribute("pid", resource.getPid()); 428 429 // title 430 final Map<String, String> title = resource.getTitle(); 431 for (Map.Entry<String, String> i : title.entrySet()) { 432 writer.setPrefix(XMLConstants.XML_NS_PREFIX, 433 XMLConstants.XML_NS_URI); 434 writer.writeStartElement(ED_NS, "Title"); 435 writer.writeAttribute(XMLConstants.XML_NS_URI, "lang", i.getKey()); 436 writer.writeCharacters(i.getValue()); 437 writer.writeEndElement(); // "title" element 438 } 439 440 // description 441 final Map<String, String> description = resource.getDescription(); 442 if (description != null) { 443 for (Map.Entry<String, String> i : description.entrySet()) { 444 writer.writeStartElement(ED_NS, "Description"); 445 writer.writeAttribute(XMLConstants.XML_NS_URI, "lang", 446 i.getKey()); 447 writer.writeCharacters(i.getValue()); 448 writer.writeEndElement(); // "Description" element 449 } 450 } 451 452 // landing page 453 final String landingPageURI = resource.getLandingPageURI(); 454 if (landingPageURI != null) { 455 writer.writeStartElement(ED_NS, "LandingPageURI"); 456 writer.writeCharacters(landingPageURI); 457 writer.writeEndElement(); // "LandingPageURI" element 458 } 459 460 // languages 461 final List<String> languages = resource.getLanguages(); 462 writer.writeStartElement(ED_NS, "Languages"); 463 for (String i : languages) { 464 writer.writeStartElement(ED_NS, "Language"); 465 writer.writeCharacters(i); 466 writer.writeEndElement(); // "Language" element 467 468 } 469 writer.writeEndElement(); // "Languages" element 470 471 // available data views 472 StringBuilder sb = new StringBuilder(); 473 for (DataView dataview : resource.getAvailableDataViews()) { 474 if (sb.length() > 0) { 475 sb.append(" "); 476 } 477 sb.append(dataview.getIdentifier()); 478 } 479 writer.writeEmptyElement(ED_NS, "AvailableDataViews"); 480 writer.writeAttribute("ref", sb.toString()); 481 482 // child resources 483 List<ResourceInfo> subs = resource.getSubResources(); 484 if ((subs != null) && !subs.isEmpty()) { 485 writeResourceInfos(writer, subs); 486 } 487 488 writer.writeEndElement(); // "Resource" element 489 } 490 writer.writeEndElement(); // "Resources" element 491 } 492 } 493 494 495 private void writeLegacyResourceInfo(XMLStreamWriter writer, 496 ResourceInfo resourceInfo) throws XMLStreamException { 497 writer.setDefaultNamespace(FCS_RESOURCE_INFO_NS); 498 writer.writeStartElement(FCS_RESOURCE_INFO_NS, "ResourceInfo"); 499 writer.writeDefaultNamespace(FCS_RESOURCE_INFO_NS); 500 501 // title 502 final Map<String, String> title = resourceInfo.getTitle(); 503 for (Map.Entry<String, String> i : title.entrySet()) { 504 writer.setPrefix(XMLConstants.XML_NS_PREFIX, 505 XMLConstants.XML_NS_URI); 506 writer.writeStartElement(FCS_RESOURCE_INFO_NS, "Title"); 507 writer.writeAttribute(XMLConstants.XML_NS_URI, "lang", i.getKey()); 508 writer.writeCharacters(i.getValue()); 509 writer.writeEndElement(); // "title" element 510 } 511 512 // description 513 final Map<String, String> description = resourceInfo.getDescription(); 514 if (description != null) { 515 for (Map.Entry<String, String> i : description.entrySet()) { 516 writer.writeStartElement(FCS_RESOURCE_INFO_NS, "Description"); 517 writer.writeAttribute(XMLConstants.XML_NS_URI, "lang", 518 i.getKey()); 519 writer.writeCharacters(i.getValue()); 520 writer.writeEndElement(); // "Description" element 521 } 522 } 523 524 // landing page 525 final String landingPageURI = resourceInfo.getLandingPageURI(); 526 if (landingPageURI != null) { 527 writer.writeStartElement(FCS_RESOURCE_INFO_NS, "LandingPageURI"); 528 writer.writeCharacters(landingPageURI); 529 writer.writeEndElement(); // "LandingPageURI" element 530 } 531 532 // languages 533 final List<String> languages = resourceInfo.getLanguages(); 534 writer.writeStartElement(FCS_RESOURCE_INFO_NS, "Languages"); 535 for (String i : languages) { 536 writer.writeStartElement(FCS_RESOURCE_INFO_NS, "Language"); 537 writer.writeCharacters(i); 538 writer.writeEndElement(); // "Language" element 539 540 } 541 writer.writeEndElement(); // "Languages" element 542 writer.writeEndElement(); // "ResourceInfo" element 543 } 544 545 546 // final boolean defaultNS = ((prefix == null) || prefix.isEmpty()); 547 // if (writeNS) { 548 // if (defaultNS) { 549 // writer.setDefaultNamespace(FCS_RESOURCE_INFO_NS); 550 // } else { 551 // writer.setPrefix(prefix, FCS_RESOURCE_INFO_NS); 552 // } 553 // } 554 // writer.writeStartElement(FCS_RESOURCE_INFO_NS, "ResourceInfo"); 555 // if (writeNS) { 556 // if (defaultNS) { 557 // writer.writeDefaultNamespace(FCS_RESOURCE_INFO_NS); 558 // } else { 559 // writer.writeNamespace(prefix, FCS_RESOURCE_INFO_NS); 560 // } 561 // } ) 562 //} 563 564 // public static void XwriteResourceInfo(XMLStreamWriter writer, String prefix, 565 // ResourceInfo resourceInfo) throws XMLStreamException { 566 // doWriteResourceInfo(writer, prefix, resourceInfo, true, false); 567 // } 568 // 569 // 570 // private static void XdoWriteResourceInfo(XMLStreamWriter writer, 571 // String prefix, ResourceInfo resourceInfo, boolean writeNS, 572 // boolean recursive) throws XMLStreamException { 573 // 574 // if (writer == null) { 575 // throw new NullPointerException("writer == null"); 576 // } 577 // if (resourceInfo == null) { 578 // throw new NullPointerException("resourceInfo == null"); 579 // } 580 // 581 // final boolean defaultNS = ((prefix == null) || prefix.isEmpty()); 582 // if (writeNS) { 583 // if (defaultNS) { 584 // writer.setDefaultNamespace(FCS_RESOURCE_INFO_NS); 585 // } else { 586 // writer.setPrefix(prefix, FCS_RESOURCE_INFO_NS); 587 // } 588 // } 589 // writer.writeStartElement(FCS_RESOURCE_INFO_NS, "Resource"); 590 // if (writeNS) { 591 // if (defaultNS) { 592 // writer.writeDefaultNamespace(FCS_RESOURCE_INFO_NS); 593 // } else { 594 // writer.writeNamespace(prefix, FCS_RESOURCE_INFO_NS); 595 // } 596 // } 597 // if (recursive) { 598 // /* 599 // * HACK: only output @pid for recursive (= explain) requests. 600 // * This should be revisited, if we decide to go for the explain 601 // * style enumeration of resources. 602 // */ 603 // writer.writeAttribute("pid", resourceInfo.getPid()); 604 // } 605 // if (resourceInfo.hasSubResources()) { 606 // writer.writeAttribute("hasSubResources", "true"); 607 // } 608 // 609 // 610 // if (recursive && resourceInfo.hasSubResources()) { 611 // writer.writeStartElement(FCS_RESOURCE_INFO_NS, 612 // "ResourceInfoCollection"); 613 // for (ResourceInfo r : resourceInfo.getSubResources()) { 614 // doWriteResourceInfo(writer, prefix, r, writeNS, recursive); 615 // } 616 // writer.writeEndElement(); // "ResourceCollection" element 617 // } 618 // writer.writeEndElement(); // "ResourceInfo" element 619 // } 439 620 440 621 } // class SimpleEndpointSearchEngineBase -
FCSSimpleEndpoint/trunk/src/main/java/eu/clarin/sru/server/fcs/XMLStreamWriterHelper.java
r2751 r5546 8 8 * This class provides several helper methods for writing records in the 9 9 * CLARIN-FCS record schema. These methods <em>do not</em> cover the full 10 * spectrum of all variations of records that are permitted by the CLARIN 10 * spectrum of all variations of records that are permitted by the CLARIN-FCS 11 11 * specification. 12 12 * 13 13 * @see <a 14 * href="https://trac.clarin.eu/wiki/FCS -specification#SearchRetrieveOperation">15 * CLARIN FCS specification, section " SearchRetrieve Operation"</a>14 * href="https://trac.clarin.eu/wiki/FCS/Specification"> 15 * CLARIN FCS specification, section "Operation searchRetrieve"</a> 16 16 */ 17 17 public final class XMLStreamWriterHelper { … … 23 23 private static final String FCS_KWIC_MIMETYPE = 24 24 "application/x-clarin-fcs-kwic+xml"; 25 private static final String FCS_HITS_NS = 26 "http://clarin.eu/fcs/dataview/hits"; 27 private static final String FCS_HITS_PREFIX = "hits"; 28 private static final String FCS_HITS_MIMETYPE = 29 "application/x-clarin-fcs-hits+xml"; 25 30 26 31 … … 205 210 * @throws XMLStreamException 206 211 * if an error occurred 207 */ 212 * @deprecated The the HITS data view instead. 213 */ 214 @Deprecated 208 215 public static void writeKWICDataView(XMLStreamWriter writer, String left, 209 216 String keyword, String right) throws XMLStreamException { 210 217 if (writer == null) { 211 218 throw new NullPointerException("writer == null"); 219 } 220 if (keyword == null) { 221 throw new NullPointerException("keyword == null"); 212 222 } 213 223 … … 246 256 * Convince method for writing a record with a KWIC data view. The following 247 257 * code (arguments omitted) would accomplish the same result: 258 * 248 259 * <pre> 249 260 * ... … … 272 283 * @throws XMLStreamException 273 284 * if an error occurred 274 */ 285 * @deprecated The the HITS data view instead. 286 */ 287 @Deprecated 275 288 public static void writeResourceWithKWICDataView(XMLStreamWriter writer, 276 289 String pid, String ref, String left, String keyword, String right) … … 285 298 } 286 299 300 301 302 /** 303 * Convince method to write a simple HITS data view. It automatically 304 * performs the calls to 305 * {@link #writeStartDataView(XMLStreamWriter, String)} and 306 * {@link #writeEndDataView(XMLStreamWriter)}. 307 * 308 * @param writer 309 * the {@link XMLStreamWriter} to be used 310 * @param left 311 * the left context of the hit or <code>null</code> if not 312 * applicable 313 * @param hit 314 * the actual hit, that will be highlighted 315 * @param right 316 * the right context of the hit or <code>null</code> if not 317 * applicable 318 * @throws XMLStreamException 319 * if an error occurred 320 */ 321 public static void writeHitsDataView(XMLStreamWriter writer, String left, 322 String hit, String right) throws XMLStreamException { 323 if (writer == null) { 324 throw new NullPointerException("writer == null"); 325 } 326 if (hit == null) { 327 throw new NullPointerException("hit == null"); 328 } 329 330 writeStartDataView(writer, FCS_HITS_MIMETYPE); 331 332 // actual "hits" data view 333 writer.setPrefix(FCS_HITS_PREFIX, FCS_HITS_NS); 334 writer.writeStartElement(FCS_HITS_NS, "Result"); 335 writer.writeNamespace(FCS_HITS_PREFIX, FCS_HITS_NS); 336 337 if ((left != null) && !left.isEmpty()) { 338 writer.writeCharacters(left); 339 } 340 341 writer.writeStartElement(FCS_HITS_NS, "Hit"); 342 writer.writeCharacters(hit); 343 writer.writeEndElement(); // "Hit" element 344 345 if ((right != null) && !right.isEmpty()) { 346 writer.writeCharacters(right); 347 } 348 349 writer.writeEndElement(); // "Result" element 350 351 writeEndDataView(writer); 352 } 353 354 355 /** 356 * Convince method for writing a record with a KWIC data view. The following 357 * code (arguments omitted) would accomplish the same result: 358 * 359 * <pre> 360 * ... 361 * writeStartResource(...); 362 * writeHitsDataView(...); 363 * writeEndResource(...); 364 * ... 365 * </pre> 366 * 367 * @param writer 368 * the {@link XMLStreamWriter} to be used 369 * @param pid 370 * the persistent identifier of this resource or 371 * <code>null</code>, if not applicable 372 * @param ref 373 * the reference of this resource or <code>null</code>, if not 374 * applicable 375 * @param left 376 * the left context of the hit or <code>null</code> if not 377 * applicable 378 * @param hit 379 * the actual hit, that will be highlighted 380 * @param right 381 * the right context of the hit or <code>null</code> if not 382 * applicable 383 * @throws XMLStreamException 384 * if an error occurred 385 */ 386 public static void writeResourceWithHitsDataView(XMLStreamWriter writer, 387 String pid, String ref, String left, String hit, String right) 388 throws XMLStreamException { 389 if (writer == null) { 390 throw new NullPointerException("writer == null"); 391 } 392 393 writeStartResource(writer, pid, ref); 394 writeHitsDataView(writer, left, hit, right); 395 writeEndResource(writer); 396 } 397 398 399 /** 400 * Convince method to write a simple HITS data view. It automatically 401 * performs the calls to 402 * {@link #writeStartDataView(XMLStreamWriter, String)} and 403 * {@link #writeEndDataView(XMLStreamWriter)}. 404 * 405 * @param writer 406 * the {@link XMLStreamWriter} to be used 407 * @param text 408 * the text content of the hit 409 * @param hits 410 * an even-element array containing tuples for the hit markers in 411 * the text content 412 * @param secondIsLength 413 * if <code>true</code> the second element of each tuple in this 414 * <code>hits</code> array is interpreted as an length; if 415 * <code>false</code> it is interpreted as an end-offset 416 * @throws XMLStreamException 417 * if an error occurred 418 */ 419 public static void writeHitsDataView(XMLStreamWriter writer, String text, 420 int[] hits, boolean secondIsLength) throws XMLStreamException { 421 if (writer == null) { 422 throw new NullPointerException("writer == null"); 423 } 424 if (text == null) { 425 throw new NullPointerException("text == null"); 426 } 427 if (hits == null) { 428 throw new NullPointerException("text == null"); 429 } 430 if ((hits.length == 0) || ((hits.length % 2) != 0)) { 431 throw new NullPointerException("length of hits array must " + 432 "contain an even number of elements"); 433 } 434 435 writeStartDataView(writer, FCS_HITS_MIMETYPE); 436 437 // actual "hits" data view 438 writer.setPrefix(FCS_HITS_PREFIX, FCS_HITS_NS); 439 writer.writeStartElement(FCS_HITS_NS, "Result"); 440 writer.writeNamespace(FCS_HITS_PREFIX, FCS_HITS_NS); 441 442 int pos = 0; 443 for (int i = 0; i < hits.length; i += 2) { 444 int start = hits[i]; 445 int end = hits[i + 1]; 446 447 if ((start < 0) && (start > text.length())) { 448 throw new IllegalArgumentException("start index out of " + 449 "bounds: start=" + start); 450 } 451 if (secondIsLength) { 452 if (end < 1) { 453 throw new IllegalArgumentException( 454 "length must be larger than 0: length = " + end); 455 } 456 end += start; 457 } 458 if (start >= end) { 459 throw new IllegalArgumentException("end offset must be " + 460 "larger then start offset: start=" + start + 461 ", end="+ end); 462 } 463 464 if (start > pos) { 465 String s = text.substring(pos, start); 466 writer.writeCharacters(s); 467 } 468 writer.writeStartElement(FCS_HITS_NS, "Hit"); 469 writer.writeCharacters(text.substring(start, end)); 470 writer.writeEndElement(); // "Hits" element 471 pos = end; 472 } 473 if (pos < text.length() - 1) { 474 writer.writeCharacters(text.substring(pos)); 475 } 476 477 writer.writeEndElement(); // "Result" element 478 479 writeEndDataView(writer); 480 } 481 482 483 /** 484 * Convince method to write a simple HITS data view. It automatically 485 * performs the calls to 486 * {@link #writeStartDataView(XMLStreamWriter, String)} and 487 * {@link #writeEndDataView(XMLStreamWriter)}. 488 * 489 * <pre> 490 * ... 491 * writeStartResource(...); 492 * writeHitsDataView(...); 493 * writeEndResource(...); 494 * ... 495 * </pre> 496 * 497 * @param writer 498 * the {@link XMLStreamWriter} to be used 499 * @param pid 500 * the persistent identifier of this resource or 501 * <code>null</code>, if not applicable 502 * @param ref 503 * the reference of this resource or <code>null</code>, if not 504 * applicable 505 * @param text 506 * the text content of the hit 507 * @param hits 508 * an even-element array containing tuples for the hit markers in 509 * the text content 510 * @param secondIsLength 511 * if <code>true</code> the second element of each tuple in this 512 * <code>hits</code> array is interpreted as an length; if 513 * <code>false</code> it is interpreted as an end-offset 514 * @throws XMLStreamException 515 * if an error occurred 516 */ 517 public static void writeResourceWithHitsDataView(XMLStreamWriter writer, 518 String pid, String ref, String text, 519 int[] hits, boolean secondIsLength) 520 throws XMLStreamException { 521 if (writer == null) { 522 throw new NullPointerException("writer == null"); 523 } 524 525 writeStartResource(writer, pid, ref); 526 writeHitsDataView(writer, text, hits, secondIsLength); 527 writeEndResource(writer); 528 } 529 287 530 } // class XMLStreamWriterHelper -
FCSSimpleEndpoint/trunk/src/main/java/eu/clarin/sru/server/fcs/utils/SimpleEndpointDescription.java
r5485 r5546 1 1 package eu.clarin.sru.server.fcs.utils; 2 2 3 import java.net.URI; 3 4 import java.util.Collections; 4 5 import java.util.List; 5 6 6 import eu.clarin.sru.server.fcs.ResourceInfoInventory; 7 import eu.clarin.sru.server.SRUException; 8 import eu.clarin.sru.server.fcs.DataView; 9 import eu.clarin.sru.server.fcs.EndpointDescription; 7 10 import eu.clarin.sru.server.fcs.ResourceInfo; 8 11 9 12 10 13 /** 11 * A very simple resource info inventory that is initialized with a static list12 * of resource info records. Mostly used together with13 * {@link SimpleResourceInfoInventoryParser}, but it is agnostic how the static14 * list of resource info records is generated.15 * 16 * @see ResourceInfoInventory17 * @see Simple ResourceInfoInventoryParser14 * A very simple implementation of an endpoint description that is initialized 15 * from static information supplied at construction time. Mostly used together 16 * with {@link SimpleEndpointDescriptionParser}, but it is agnostic how the 17 * static list of resource info records is generated. 18 * 19 * @see EndpointDescription 20 * @see SimpleEndpointDescriptionParser 18 21 */ 19 public class Simple ResourceInfoInventory implements ResourceInfoInventory{22 public class SimpleEndpointDescription extends AbstractEndpointDescriptionBase { 20 23 private final boolean pidCaseSensitive; 21 24 private final List<ResourceInfo> entries; … … 25 28 * Constructor. 26 29 * 27 * @param entries 30 * @param capabilities 31 * a list of capabilities supported by this endpoint 32 * @param supportedDataViews 33 * a list of data views that are supported by this endpoint 34 * @param resources 28 35 * a static list of resource info records 29 36 * @param pidCaseSensitive 30 37 * <code>true</code> if comparison of persistent identifiers 31 * should be performed case-sensitive, <code>false</code> otherwise 38 * should be performed case-sensitive, <code>false</code> 39 * otherwise 32 40 */ 33 public SimpleResourceInfoInventory(List<ResourceInfo> entries, 41 public SimpleEndpointDescription(List<URI> capabilities, 42 List<DataView> supportedDataViews, List<ResourceInfo> resources, 34 43 boolean pidCaseSensitive) { 35 if (entries == null) { 44 super(capabilities, supportedDataViews); 45 46 if (resources == null) { 36 47 throw new NullPointerException("entries == null"); 37 48 } 38 this.entries = Collections.unmodifiableList( entries);49 this.entries = Collections.unmodifiableList(resources); 39 50 this.pidCaseSensitive = pidCaseSensitive; 40 51 } … … 47 58 48 59 @Override 49 public List<ResourceInfo> getResource InfoList(String id){50 if ( id == null) {51 throw new NullPointerException(" id == null");60 public List<ResourceInfo> getResourceList(String pid) throws SRUException { 61 if (pid == null) { 62 throw new NullPointerException("pid == null"); 52 63 } 53 if ( id.isEmpty()) {54 throw new IllegalArgumentException(" id is empty");64 if (pid.isEmpty()) { 65 throw new IllegalArgumentException("pid is empty"); 55 66 } 56 67 if (!pidCaseSensitive) { 57 id =id.toLowerCase();68 pid = pid.toLowerCase(); 58 69 } 59 if ( id.equals(PID_ROOT)) {70 if (pid.equals(PID_ROOT)) { 60 71 return entries; 61 72 } else { 62 ResourceInfo ri = findRecursive(entries, id);73 ResourceInfo ri = findRecursive(entries, pid); 63 74 if (ri != null) { 64 75 return ri.getSubResources(); … … 93 104 } 94 105 95 } // class Simple ResourceInfoInventory106 } // class SimpleEndpointDescription -
FCSSimpleEndpoint/trunk/src/main/java/eu/clarin/sru/server/fcs/utils/SimpleEndpointDescriptionParser.java
r5485 r5546 2 2 3 3 import java.io.IOException; 4 import java.net.URI; 5 import java.net.URISyntaxException; 4 6 import java.net.URL; 5 7 import java.util.ArrayList; 6 import java.util.Arrays;7 8 import java.util.HashMap; 8 9 import java.util.HashSet; 9 10 import java.util.Iterator; 10 import java.util.LinkedList;11 11 import java.util.List; 12 12 import java.util.Map; … … 33 33 34 34 import eu.clarin.sru.server.SRUConfigException; 35 import eu.clarin.sru.server.fcs.ResourceInfoInventory; 35 import eu.clarin.sru.server.fcs.DataView; 36 import eu.clarin.sru.server.fcs.DataView.DeliveryPolicy; 37 import eu.clarin.sru.server.fcs.EndpointDescription; 36 38 import eu.clarin.sru.server.fcs.ResourceInfo; 37 39 38 40 39 41 /** 40 * A parser, that parses an XML file and produces a static list of resource info 41 * records. The resulting list can be used to construct a 42 * {@link SimpleResourceInfoInventory} instance. 43 * 44 * @see ResourceInfo 45 * @see SimpleResourceInfoInventory 42 * A parser, that parses an XML file and produces a endpoint description with 43 * static list of resource info records. The XML file has the same format as the 44 * result format defined for endpoint description of the CLARIN-FCS 45 * specification. The {@link #parse(URL)} returns a 46 * {@link SimpleEndpointDescription} instance. 47 * 48 * @see EndpointDescription 49 * @see SimpleEndpointDescription 46 50 */ 47 public class SimpleResourceInfoInventoryParser { 48 private static final String NS = "http://clarin.eu/fcs/endpoint-description"; 51 public class SimpleEndpointDescriptionParser { 52 private static final String NS = 53 "http://clarin.eu/fcs/endpoint-description"; 54 private static final String NS_LEGACY = 55 "http://clarin.eu/fcs/1.0/resource-info"; 56 private static final String CAP_BASIC_SEARCH = 57 "http://clarin.eu/fcs/capability/basic-search"; 49 58 private static final String LANG_EN = "en"; 59 private static final String POLICY_SEND_DEFAULT = "send-by-default"; 60 private static final String POLICY_NEED_REQUEST = "need-to-request"; 50 61 private static final Logger logger = 51 LoggerFactory.getLogger(Simple ResourceInfoInventoryParser.class);62 LoggerFactory.getLogger(SimpleEndpointDescriptionParser.class); 52 63 53 64 54 65 /** 55 66 * Parse an XML file and return a static list of resource info records. 56 * 67 * 57 68 * @param url 58 69 * the URI pointing to the file to be parsed 59 * @return a list of resource info records represented as 60 * {@link ResourceInfo} instances 70 * @return an {@link EndpointDescription} instance 61 71 * @throws SRUConfigException 62 72 * if an error occurred 63 73 */ 64 public static ResourceInfoInventoryparse(URL url) throws SRUConfigException {74 public static EndpointDescription parse(URL url) throws SRUConfigException { 65 75 if (url == null) { 66 76 throw new NullPointerException("url == null"); 67 77 } 68 78 69 logger.debug("parsing resource-info from: {}", url); 70 71 final Set<String> ids = new HashSet<String>(); 79 logger.debug("parsing endpoint description from: {}", url); 80 72 81 try { 73 82 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); … … 77 86 Document doc = db.parse(url.openStream()); 78 87 79 XPathFactory factory = XPathFactory.newInstance(); 80 XPath xpath = factory.newXPath(); 81 xpath.setNamespaceContext(new NamespaceContext() { 82 @Override 83 public Iterator<?> getPrefixes(String namespaceURI) { 84 throw new UnsupportedOperationException(); 85 } 86 87 @Override 88 public String getPrefix(String namespaceURI) { 89 throw new UnsupportedOperationException(); 90 } 91 92 @Override 93 public String getNamespaceURI(String prefix) { 94 if (prefix == null) { 95 throw new NullPointerException("prefix == null"); 88 /* 89 * Detect for deprecated resource-info catalog files and bail, if necessary 90 */ 91 checkLegacyMode(doc, url); 92 93 /* 94 * Parse on and create endpoint description ... 95 */ 96 return parseEndpointDescription(doc); 97 } catch (ParserConfigurationException e) { 98 throw new SRUConfigException("internal error", e); 99 } catch (SAXException e) { 100 throw new SRUConfigException("parsing error", e); 101 } catch (IOException e) { 102 throw new SRUConfigException("error reading file", e); 103 } catch (XPathExpressionException e) { 104 throw new SRUConfigException("internal error", e); 105 } 106 } 107 108 109 private static EndpointDescription parseEndpointDescription(Document doc) 110 throws SRUConfigException, XPathExpressionException { 111 XPathFactory factory = XPathFactory.newInstance(); 112 XPath xpath = factory.newXPath(); 113 114 xpath.setNamespaceContext(new NamespaceContext() { 115 @Override 116 public Iterator<?> getPrefixes(String namespaceURI) { 117 throw new UnsupportedOperationException(); 118 } 119 120 @Override 121 public String getPrefix(String namespaceURI) { 122 throw new UnsupportedOperationException(); 123 } 124 125 @Override 126 public String getNamespaceURI(String prefix) { 127 if (prefix == null) { 128 throw new NullPointerException("prefix == null"); 129 } 130 if (prefix.equals("ed")) { 131 return NS; 132 } else if (prefix.equals(XMLConstants.XML_NS_PREFIX)) { 133 return XMLConstants.XML_NS_URI; 134 } else { 135 return XMLConstants.NULL_NS_URI; 136 } 137 } 138 }); 139 140 // capabilities 141 List<URI> capabilities = new ArrayList<URI>(); 142 XPathExpression exp1 = 143 xpath.compile("//ed:Capabilities/ed:Capability"); 144 NodeList list1 = (NodeList) exp1.evaluate(doc, XPathConstants.NODESET); 145 if ((list1 != null) && (list1.getLength() > 0)) { 146 logger.debug("parsing capabilities"); 147 for (int i = 0; i < list1.getLength(); i++) { 148 String s = list1.item(i).getTextContent().trim(); 149 try { 150 URI uri = new URI(s); 151 if (capabilities.contains(uri)) { 152 logger.warn("ignoring duplicate capability " + 153 "entry for '{}'", uri); 96 154 } 97 if (prefix.equals("ri")) { 98 return NS; 99 } else if (prefix.equals(XMLConstants.XML_NS_PREFIX)) { 100 return XMLConstants.XML_NS_URI; 101 } else { 102 return XMLConstants.NULL_NS_URI; 155 capabilities.add(uri); 156 } catch (URISyntaxException e) { 157 throw new SRUConfigException("capability is not encoded " + 158 "as proper URI: " + s); 159 } 160 } 161 } else { 162 logger.warn("No capabilities where defined in " + 163 "endpoint configuration"); 164 } 165 URI cap = URI.create(CAP_BASIC_SEARCH); 166 if (!capabilities.contains(cap)) { 167 logger.warn("capability '{}' was not defined in endpoint " + 168 "description; added it to meet specification. Please " + 169 "update your endpoint description!", CAP_BASIC_SEARCH); 170 capabilities.add(cap); 171 } 172 logger.debug("CAPS:'{}'", capabilities); 173 174 // supported data views 175 List<DataView> supportedDataViews = new ArrayList<DataView>(); 176 XPathExpression exp2 = 177 xpath.compile("//ed:SupportedDataViews/ed:SupportedDataView"); 178 NodeList list2 = (NodeList) exp2.evaluate(doc, XPathConstants.NODESET); 179 if ((list2 != null) && (list2.getLength() > 0)) { 180 logger.debug("parsing supported data views"); 181 for (int i = 0; i < list2.getLength(); i++) { 182 Element item = (Element) list2.item(i); 183 String id = getAttribute(item, "id"); 184 if (id == null) { 185 throw new SRUConfigException("Element <SupportedDataView> " 186 + "must carry a proper 'id' attribute"); 187 } 188 String p = getAttribute(item, "delivery-policy"); 189 if (p == null) { 190 throw new SRUConfigException("Element <SupportedDataView> " 191 + "must carry a 'delivery-policy' attribute"); 192 } 193 DeliveryPolicy policy = null; 194 if (POLICY_SEND_DEFAULT.equals(p)) { 195 policy = DeliveryPolicy.SEND_BY_DEFAULT; 196 } else if (POLICY_NEED_REQUEST.equals(p)) { 197 policy = DeliveryPolicy.NEED_TO_REQUEST; 198 } else { 199 throw new SRUConfigException("Invalid value '" + p + 200 "' for attribute 'delivery-policy' on element " + 201 "<SupportedDataView>"); 202 } 203 String mimeType = item.getTextContent(); 204 if (mimeType != null) { 205 mimeType = mimeType.trim(); 206 if (mimeType.isEmpty()) { 207 mimeType = null; 103 208 } 104 209 } 105 }); 106 XPathExpression expression = 107 xpath.compile("/ri:Resources/ri:Resource"); 108 NodeList list = 109 (NodeList) expression.evaluate(doc, XPathConstants.NODESET); 110 111 List<ResourceInfo> entries = parseResourceInfo(xpath, list, ids); 112 return new SimpleResourceInfoInventory(entries, false); 113 } catch (ParserConfigurationException e) { 114 e.printStackTrace(); 115 } catch (SAXException e) { 116 e.printStackTrace(); 117 } catch (IOException e) { 118 e.printStackTrace(); 119 } catch (XPathExpressionException e) { 120 e.printStackTrace(); 210 if (mimeType == null) { 211 throw new SRUConfigException("Element <SupportedDataView> " 212 + "must contain a MIME-type as content"); 213 } 214 // check for duplicate entries ... 215 for (DataView dataView : supportedDataViews) { 216 if (id.equals(dataView.getIdentifier())) { 217 throw new SRUConfigException( 218 "A <SupportedDataView> with " + "the id '" + 219 id + "' is already defined!"); 220 } 221 if (mimeType.equals(dataView.getMimeType())) { 222 throw new SRUConfigException( 223 "A <SupportedDataView> with " + 224 "the MIME-type '" + mimeType + 225 "' is already defined!"); 226 } 227 } 228 supportedDataViews.add(new DataView(id, mimeType, policy)); 229 } 230 } else { 231 logger.error("Endpoint configuration contains no valid " + 232 "information about supported data views"); 233 throw new SRUConfigException("Endpoint configuration contains " + 234 "no valid information about supported data views"); 235 } 236 237 logger.debug("DV: {}", supportedDataViews); 238 239 240 // resources 241 XPathExpression x3 = 242 xpath.compile("/ed:EndpointDescription/ed:Resources/ed:Resource"); 243 NodeList l3 = (NodeList) x3.evaluate(doc, XPathConstants.NODESET); 244 final Set<String> ids = new HashSet<String>(); 245 List<ResourceInfo> resources = 246 parseRessources(xpath, l3, ids, supportedDataViews); 247 if ((resources == null) || resources.isEmpty()) { 248 throw new SRUConfigException("No resources where " + 249 "defined in endpoint description"); 250 } 251 252 return new SimpleEndpointDescription(capabilities, 253 supportedDataViews, 254 resources, 255 false); 256 } 257 258 259 private static List<ResourceInfo> parseRessources(XPath xpath, 260 NodeList nodes, Set<String> ids, List<DataView> supportedDataViews) 261 throws SRUConfigException, XPathExpressionException { 262 List<ResourceInfo> ris = null; 263 for (int k = 0; k < nodes.getLength(); k++) { 264 final Element node = (Element) nodes.item(k); 265 String pid = null; 266 Map<String, String> titles = null; 267 Map<String, String> descrs = null; 268 String link = null; 269 List<String> langs = null; 270 List<DataView> availableDataViews = null; 271 List<ResourceInfo> sub = null; 272 273 pid = getAttribute(node, "pid"); 274 if (pid == null) { 275 throw new SRUConfigException("Element <ResourceInfo> " + 276 "must carry a proper 'pid' attribute"); 277 } 278 if (ids.contains(pid)) { 279 throw new SRUConfigException("Another element <Resource> " + 280 "with pid '" + pid + "' already exists"); 281 } 282 ids.add(pid); 283 284 XPathExpression x1 = xpath.compile("ed:Title"); 285 NodeList l1 = (NodeList) x1.evaluate(node, XPathConstants.NODESET); 286 if ((l1 != null) && (l1.getLength() > 0)) { 287 for (int i = 0; i < l1.getLength(); i++) { 288 final Element n = (Element) l1.item(i); 289 290 final String lang = getLangAttribute(n); 291 if (lang == null) { 292 throw new SRUConfigException("Element <Title> must " + 293 "carry a proper 'xml:lang' attribute"); 294 } 295 296 final String title = cleanString(n.getTextContent()); 297 if (title == null) { 298 throw new SRUConfigException("Element <Title> must " + 299 "carry a non-empty 'xml:lang' attribute"); 300 } 301 302 if (titles == null) { 303 titles = new HashMap<String, String>(); 304 } 305 if (titles.containsKey(lang)) { 306 logger.warn("title with language '{}' already exists", 307 lang); 308 } else { 309 logger.debug("title: '{}' '{}'", lang, title); 310 titles.put(lang, title); 311 } 312 } 313 if ((titles != null) && !titles.containsKey(LANG_EN)) { 314 throw new SRUConfigException( 315 "A <Title> with language 'en' is mandatory"); 316 } 317 } 318 319 XPathExpression x2 = xpath.compile("ed:Description"); 320 NodeList l2 = (NodeList) x2.evaluate(node, XPathConstants.NODESET); 321 if ((l2 != null) && (l2.getLength() > 0)) { 322 for (int i = 0; i < l2.getLength(); i++) { 323 Element n = (Element) l2.item(i); 324 325 String lang = getLangAttribute(n); 326 if (lang == null) { 327 throw new SRUConfigException("Element <Description> " + 328 "must carry a proper 'xml:lang' attribute"); 329 330 } 331 String desc = cleanString(n.getTextContent()); 332 333 if (descrs == null) { 334 descrs = new HashMap<String, String>(); 335 } 336 337 if (descrs.containsKey(lang)) { 338 logger.warn("description with language '{}' " 339 + "already exists", lang); 340 } else { 341 logger.debug("description: '{}' '{}'", lang, desc); 342 descrs.put(lang, desc); 343 } 344 } 345 if ((descrs != null) && !descrs.containsKey(LANG_EN)) { 346 throw new SRUConfigException( 347 "A <Description> with language 'en' is mandatory"); 348 } 349 } 350 351 XPathExpression x3 = xpath.compile("ed:LandingPageURI"); 352 NodeList l3 = (NodeList) x3.evaluate(node, XPathConstants.NODESET); 353 if ((l3 != null) && (l3.getLength() > 0)) { 354 for (int i = 0; i < l3.getLength(); i++) { 355 Element n = (Element) l3.item(i); 356 link = cleanString(n.getTextContent()); 357 } 358 } 359 360 XPathExpression x4 = xpath.compile("ed:Languages/ed:Language"); 361 NodeList l4 = (NodeList) x4.evaluate(node, XPathConstants.NODESET); 362 if ((l4 != null) && (l4.getLength() > 0)) { 363 for (int i = 0; i < l4.getLength(); i++) { 364 Element n = (Element) l4.item(i); 365 366 String s = n.getTextContent(); 367 if (s != null) { 368 s = s.trim(); 369 if (s.isEmpty()) { 370 s = null; 371 } 372 } 373 374 /* 375 * enforce three letter codes 376 */ 377 if ((s == null) || (s.length() != 3)) { 378 throw new SRUConfigException("Element <Language> " + 379 "must use ISO-632-3 three letter " + 380 "language codes"); 381 } 382 383 if (langs == null) { 384 langs = new ArrayList<String>(); 385 } 386 langs.add(s); 387 } 388 } 389 390 XPathExpression x5 = xpath.compile("ed:AvailableDataViews"); 391 Node n = (Node) x5.evaluate(node, XPathConstants.NODE); 392 if ((n != null) && (n instanceof Element)) { 393 String ref = getAttribute((Element) n, "ref"); 394 if (ref == null) { 395 throw new SRUConfigException("Element <AvailableDataViews> " + 396 "must carry a 'ref' attribute"); 397 } 398 String[] refs = ref.split("\\s+"); 399 if ((refs == null) || (refs.length < 1)) { 400 throw new SRUConfigException("Attribute 'ref' on element " + 401 "<AvailableDataViews> must contain a whitespace " + 402 "seperated list of data view references"); 403 } 404 405 406 for (int i = 0; i < refs.length; i++) { 407 DataView dataview = null; 408 for (DataView dv : supportedDataViews) { 409 if (refs[i].equals(dv.getIdentifier())) { 410 dataview = dv; 411 break; 412 } 413 } 414 if (dataview != null) { 415 if (availableDataViews == null) { 416 availableDataViews = new ArrayList<DataView>(); 417 } 418 availableDataViews.add(dataview); 419 } else { 420 throw new SRUConfigException("A data view with " + 421 "identifier '" + refs[i] + "' was not defined " + 422 "in <SupportedDataViews>"); 423 } 424 } 425 } else { 426 throw new SRUConfigException( 427 "missing element <ed:AvailableDataViews>"); 428 } 429 if (availableDataViews == null) { 430 throw new SRUConfigException("No available data views where " + 431 "defined for resource with PID '" + pid + "'"); 432 } 433 434 XPathExpression x6 = xpath.compile("ed:Resources/ed:Resource"); 435 NodeList l6 = (NodeList) x6.evaluate(node, XPathConstants.NODESET); 436 if ((l6 != null) && (l6.getLength() > 0)) { 437 sub = parseRessources(xpath, l6, ids, supportedDataViews); 438 } 439 440 if (ris == null) { 441 ris = new ArrayList<ResourceInfo>(); 442 } 443 ris.add(new ResourceInfo(pid, 444 titles, 445 descrs, 446 link, 447 langs, 448 availableDataViews, 449 sub)); 450 } 451 return ris; 452 } 453 454 455 private static String getAttribute(Element el, String localName) { 456 String lang = el.getAttribute(localName); 457 if (lang != null) { 458 lang = lang.trim(); 459 if (!lang.isEmpty()) { 460 return lang; 461 } 121 462 } 122 463 return null; 123 }124 125 126 private static List<ResourceInfo> parseResourceInfo(XPath xpath,127 NodeList nodes, Set<String> ids) throws SRUConfigException,128 XPathExpressionException {129 logger.debug("parsing 'ResourceInfo' ({} nodes) ...",130 nodes.getLength());131 132 List<ResourceInfo> ris = null;133 for (int k = 0; k < nodes.getLength(); k++) {134 final Element node = (Element) nodes.item(k);135 String pid = null;136 int resourceCount = -1;137 Map<String, String> titles = null;138 Map<String, String> descrs = null;139 String link = null;140 List<String> langs = null;141 String[] availableDataViews = null;142 List<ResourceInfo> sub = null;143 144 pid = node.getAttribute("pid");145 if (pid != null) {146 pid = pid.trim();147 if (pid.isEmpty()) {148 pid = null;149 }150 }151 if (pid == null) {152 throw new SRUConfigException("Element <ResourceInfo> " +153 "must carry a proper 'pid' attribute");154 }155 if (ids.contains(pid)) {156 throw new SRUConfigException("Another element <ResourceInfo> " +157 "with pid '" + pid + "' already exists");158 }159 ids.add(pid);160 161 XPathExpression x1 = xpath.compile("ri:Title");162 NodeList l1 = (NodeList) x1.evaluate(node, XPathConstants.NODESET);163 if (l1 != null) {164 for (int i = 0; i < l1.getLength(); i++) {165 final Element n = (Element) l1.item(i);166 167 final String lang = getLangAttribute(n);168 if (lang == null) {169 throw new SRUConfigException("Element <Title> must " +170 "carry a proper 'xml:lang' attribute");171 }172 173 final String title = cleanString(n.getTextContent());174 if (title == null) {175 throw new SRUConfigException("Element <Title> must " +176 "carry a non-empty 'xml:lang' attribute");177 }178 179 if (titles == null) {180 titles = new HashMap<String, String>();181 }182 if (titles.containsKey(lang)) {183 logger.warn("title with language '{}' already exists",184 lang);185 } else {186 logger.debug("title: '{}' '{}'", lang, title);187 titles.put(lang, title);188 }189 }190 if ((titles != null) && !titles.containsKey(LANG_EN)) {191 throw new SRUConfigException(192 "A <Title> with language 'en' is mandatory");193 }194 }195 XPathExpression x2 = xpath.compile("ri:Description");196 NodeList l2 = (NodeList) x2.evaluate(node, XPathConstants.NODESET);197 if (l2 != null) {198 for (int i = 0; i < l2.getLength(); i++) {199 Element n = (Element) l2.item(i);200 201 String lang = getLangAttribute(n);202 if (lang == null) {203 throw new SRUConfigException("Element <Description> " +204 "must carry a proper 'xml:lang' attribute");205 206 }207 String desc = cleanString(n.getTextContent());208 209 if (descrs == null) {210 descrs = new HashMap<String, String>();211 }212 213 if (descrs.containsKey(lang)) {214 logger.warn("description with language '{}' "215 + "already exists", lang);216 } else {217 logger.debug("description: '{}' '{}'", lang, desc);218 descrs.put(lang, desc);219 }220 }221 if ((descrs != null) && !descrs.containsKey(LANG_EN)) {222 throw new SRUConfigException(223 "A <Description> with language 'en' is mandatory");224 }225 }226 227 XPathExpression x3 = xpath.compile("ri:LandingPageURI");228 NodeList l3 = (NodeList) x3.evaluate(node, XPathConstants.NODESET);229 if (l3 != null) {230 for (int i = 0; i < l3.getLength(); i++) {231 Element n = (Element) l3.item(i);232 link = cleanString(n.getTextContent());233 logger.debug("link: \"{}\"", n.getTextContent());234 }235 }236 237 XPathExpression x4 = xpath.compile("ri:Languages/ri:Language");238 NodeList l4 = (NodeList) x4.evaluate(node, XPathConstants.NODESET);239 if (l4 != null) {240 for (int i = 0; i < l4.getLength(); i++) {241 Element n = (Element) l4.item(i);242 243 String s = n.getTextContent();244 if (s != null) {245 s = s.trim();246 if (s.isEmpty()) {247 s = null;248 }249 }250 251 /*252 * enforce three letter codes253 */254 if ((s == null) || (s.length() != 3)) {255 throw new SRUConfigException("Element <Language> " +256 "must use ISO-632-3 three letter " +257 "language codes");258 }259 260 if (langs == null) {261 langs = new ArrayList<String>();262 }263 logger.debug("language: '{}'", n.getTextContent());264 langs.add(s);265 }266 }267 268 XPathExpression x6 = xpath.compile("ri:AvailableDataViews/@ref");269 String ref = (String) x6.evaluate(node, XPathConstants.STRING);270 271 if (ref == null || ref.isEmpty()){272 throw new SRUConfigException("Element <AvailableDataViews> " +273 "must have a non-empty attribute ref.");274 }275 276 availableDataViews = ref.split("\\s+");277 278 279 XPathExpression x5 =280 xpath.compile("ri:Resources/ri:Resource");281 NodeList l5 = (NodeList) x5.evaluate(node, XPathConstants.NODESET);282 if ((l5 != null) && (l5.getLength() > 0)) {283 sub = parseResourceInfo(xpath, l5, ids);284 }285 286 if (ris == null) {287 ris = new LinkedList<ResourceInfo>();288 }289 ris.add(new ResourceInfo(pid, resourceCount, titles, descrs, link,290 langs, Arrays.asList(availableDataViews),sub));291 }292 return ris;293 464 } 294 465 … … 328 499 } 329 500 501 502 private static void checkLegacyMode(Document doc, URL url) 503 throws SRUConfigException { 504 Element root = doc.getDocumentElement(); 505 if (root != null) { 506 String ns = root.getNamespaceURI(); 507 if (ns != null) { 508 if (ns.equals(NS_LEGACY)) { 509 logger.error("Detected out-dated " + 510 "resource info catalog file '" + url + 511 "'. Please update to the " + 512 "current version"); 513 throw new SRUConfigException("unsupport file format: " + ns); 514 } else if (!ns.equals(NS)) { 515 logger.error("Detected unsupported resource info " + 516 "catalog file '" + url + "' with namespace '" + ns + '"'); 517 throw new SRUConfigException("unsupport file format: " + ns); 518 } 519 } else { 520 throw new SRUConfigException("No namespace URI was detected " + 521 "for resource info catalog file '" + url +"'!"); 522 } 523 } else { 524 throw new SRUConfigException("Error retrieving root element"); 525 } 526 } 527 528 529 // private static List<ResourceInfo> parseLegacy(Document doc) 530 // throws SRUConfigException, XPathExpressionException { 531 330 532 } // class SimpleResourceInfoInventoryParser
Note: See TracChangeset
for help on using the changeset viewer.