Index: build.xml =================================================================== --- build.xml (revision 5690) +++ build.xml (working copy) @@ -58,7 +58,7 @@ @@ -72,7 +72,7 @@ + includes="org/fao/geonet/**, org/fao/gast/**, org/wmo/**" /> 0) { - RecordFormatSpecification rfs = new RecordFormatSpecification("xml", null, syntax); - InformationFragment frags[] = _st.getTaskResultSet().getFragment(getFrom(), (getTo() - getFrom() + 1), rfs); + ExplicitRecordFormatSpecification rfs = new ExplicitRecordFormatSpecification("xml", null, syntax); + + StatelessQueryService sqs = getQueryService(srvContext); + + + StatelessSearchResultsPageDTO res = sqs.getResultsPageFor(this.zinfo.getSetname(), + this.zinfo.getQueryModel(), + this.zinfo.getLandscape(), + getFrom(), + getTo(), + def_request_spec, + rfs, + null); + + + + //InformationFragment frags[] = _st.getTaskResultSet().getFragment(getFrom(), (getTo() - getFrom() + 1), rfs); + + InformationFragment frags[] = res.records; + for (int i = 0; i < frags.length; i++) { InformationFragment frag = frags[i]; @@ -167,7 +235,7 @@ // System.out.println("ORIGINAL OBJECT IN FRAGMENT: " + frag.getOriginalObject()); // DEBUG - org.w3c.dom.Document doc = frag.getDocument(); + org.w3c.dom.Document doc = (Document)frag.getOriginalObject() ; org.w3c.dom.Element el = doc.getDocumentElement(); Element md = builder.build(el); md.detach(); @@ -199,7 +267,8 @@ public int getSize() { - return _st.getTaskResultSet().getFragmentCount(); + return this.size; + //return _st.getTaskResultSet().getFragmentCount(); } //----------------------------------------------------------------------------- @@ -213,14 +282,14 @@ //----------------------------------------------------------------------------- /** closes the connection(s) - */ + */ public void close() { - if (_st != null) - { - _st.destroyTask(); - _st = null; - } +// if (_st != null) +// { +// _st.destroyTask(); +// _st = null; +// } } //-------------------------------------------------------------------------------- @@ -228,7 +297,7 @@ // makes a new query private String newQuery(Element xmlQuery) - throws Exception + throws Exception { String name = xmlQuery.getName(); if (name.equals("query")) @@ -280,6 +349,7 @@ private Element makeSummary() { + Log.debug(Geonet.SEARCH_ENGINE, "z3590 searcher: makeSummary with: size:" + this.size + " status: "+this.status + "\n"); Element summary = new Element("summary"); summary.setAttribute("count", getSize()+""); summary.setAttribute("status", getStatus()); @@ -287,20 +357,16 @@ return summary; } - + //----------------------------------------------------------------------------- /** returns the current status - */ + */ private String getStatus() { - switch (_st.getTaskStatusCode()) - { - case SearchTask.TASK_COMPLETE: return "complete"; - case SearchTask.TASK_EXECUTING: return "executing"; - case SearchTask.TASK_FAILURE: return "failure"; - case SearchTask.TASK_IDLE: return "idle"; - } - return null; + + return IRResultSetStatus.getCode(this.status); } + + } Index: src/org/fao/geonet/services/util/z3950/GNSearchTask.java =================================================================== --- src/org/fao/geonet/services/util/z3950/GNSearchTask.java (revision 5692) +++ src/org/fao/geonet/services/util/z3950/GNSearchTask.java (working copy) @@ -1,4 +1,4 @@ -//============================================================================= +/*//============================================================================= //=== Copyright (C) 2001-2007 Food and Agriculture Organization of the //=== United Nations (FAO-UN), United Nations World Food Programme (WFP) //=== and United Nations Environment Programme (UNEP) @@ -103,13 +103,13 @@ //-------------------------------------------------------------------------- - /* TODO: not used, remove + TODO: not used, remove / * * from SearchTask abstract base class * / public void destroyTask() { super.destroyTask(); } - */ + //-------------------------------------------------------------------------- @@ -406,3 +406,4 @@ } } +*/ \ No newline at end of file Index: src/org/fao/geonet/services/util/z3950/GNSearchable.java =================================================================== --- src/org/fao/geonet/services/util/z3950/GNSearchable.java (revision 5692) +++ src/org/fao/geonet/services/util/z3950/GNSearchable.java (working copy) @@ -1,4 +1,4 @@ -//============================================================================= +/*//============================================================================= //=== Copyright (C) 2001-2007 Food and Agriculture Organization of the //=== United Nations (FAO-UN), United Nations World Food Programme (WFP) //=== and United Nations Environment Programme (UNEP) @@ -119,3 +119,4 @@ } //============================================================================= +*/ \ No newline at end of file Index: src/org/fao/geonet/services/util/z3950/Server.java =================================================================== --- src/org/fao/geonet/services/util/z3950/Server.java (revision 5692) +++ src/org/fao/geonet/services/util/z3950/Server.java (working copy) @@ -24,6 +24,7 @@ package org.fao.geonet.services.util.z3950; import java.io.File; + import java.io.FileOutputStream; import java.util.Properties; @@ -34,7 +35,8 @@ import org.jdom.Document; import org.jdom.Element; -import com.k_int.z3950.server.ZServer; +import org.jzkit.z3950.server.Z3950Listener; +import org.springframework.context.ApplicationContext; //============================================================================= @@ -43,14 +45,14 @@ public class Server { - private static ZServer _server; + private static Z3950Listener _listener; //-------------------------------------------------------------------------- /** initializes the server */ public static void init(String host, String port, String appPath, - String schemaMappings, ServiceContext context) + String schemaMappings, ServiceContext context, ApplicationContext app_context) { try { @@ -72,16 +74,19 @@ String evaluator = "org.fao.geonet.services.util.z3950.GNSearchable"; String configurator = "com.k_int.IR.Syntaxes.Conversion.XMLConfigurator"; - + /* Properties props = new Properties(); props.setProperty("port", port); props.setProperty("evaluator", evaluator); props.setProperty("XSLConverterConfiguratorClassName", configurator); props.setProperty("ConvertorConfigFile", realSchema); props.put("srvContext", context); - - _server = new ZServer(props); - _server.start(); + */ + _listener = (Z3950Listener)app_context.getBean("Z3950Listener",Z3950Listener.class); + + _listener.setPort(Integer.parseInt(port)); + _listener.start(); + } catch (Exception e) { @@ -96,8 +101,8 @@ */ public static void end() { - if (_server != null) - _server.shutdown(0); // shutdown type is not used in current implementation + if (_listener != null) + _listener.shutdown(0); } //-------------------------------------------------------------------------- Index: src/org/wmo/geonet/ContextContainer.java =================================================================== --- src/org/wmo/geonet/ContextContainer.java (revision 0) +++ src/org/wmo/geonet/ContextContainer.java (revision 0) @@ -0,0 +1,67 @@ +package org.wmo.geonet; + +import jeeves.server.context.ServiceContext; + +import org.fao.geonet.GeonetContext; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + + +/** + * + * This class is a gateway between the Spring Application + * Context and the other contexts of Geonetwork + * It is used to store references to the + * different contexts used in geonetwork. + * A reference to it will be distributed via + * the Spring ApplicationContext + * @author 'Timo Proescholdt ' + */ + +public class ContextContainer implements ApplicationContextAware { + + //private GeonetContext geoctx; + private ServiceContext srvctx; + private ApplicationContext ctx; + + public ContextContainer() { + + + } + + /* + public GeonetContext getGeoctx() { + return geoctx; + } + + public void setGeoctx(GeonetContext geoctx) { + + this.geoctx = geoctx; + } + */ + + public ServiceContext getSrvctx() { + return srvctx; + } + + public void setSrvctx(ServiceContext srvctx) { + this.srvctx = srvctx; + } + + + public void setApplicationContext(ApplicationContext arg0) + throws BeansException { + + ctx=arg0; + } + + + public ApplicationContext getApplicationContext() { + if (ctx==null) throw new RuntimeException("applicationcontext not yet initialized"); + return ctx; + } + + +} + Index: src/org/wmo/geonet/DateRangeQuery.java =================================================================== --- src/org/wmo/geonet/DateRangeQuery.java (revision 0) +++ src/org/wmo/geonet/DateRangeQuery.java (revision 0) @@ -0,0 +1,35 @@ +package org.wmo.geonet; + +import org.apache.lucene.index.Term; +import org.apache.lucene.search.RangeQuery; + + +/** + * @author 'Timo Proescholdt ' + * provisional class to have a DateRangeQuery interface to the lucene XML query + * format. Could (should) be more sophisticated. To be improved when more efficient + * indexing structures are implemented + */ +public class DateRangeQuery extends RangeQuery { + + public DateRangeQuery(String fld, String lowerString, String upperString, String inclusive) { + super(new Term(fld, formatDate(lowerString)), new Term(fld, formatDate(upperString)), inclusive.equalsIgnoreCase("true") ); + } + + private static String formatDate(String s) { + + String ret = ""; + + if (s==null) return null; + + ret=s.trim(); + ret=ret.replaceAll("\'", ""); + ret=ret.toUpperCase(); + + + return ret; + + } + + +} Index: src/org/wmo/geonet/DefaultContextSetCQLString.java =================================================================== --- src/org/wmo/geonet/DefaultContextSetCQLString.java (revision 0) +++ src/org/wmo/geonet/DefaultContextSetCQLString.java (revision 0) @@ -0,0 +1,179 @@ +package org.wmo.geonet; + +import jeeves.utils.Log; + +import org.fao.geonet.constants.Geonet; +import org.jzkit.search.util.QueryModel.InvalidQueryException; +import org.jzkit.search.util.QueryModel.CQLString.CQLString; +import org.jzkit.search.util.QueryModel.Internal.AttrPlusTermNode; +import org.jzkit.search.util.QueryModel.Internal.AttrValue; +import org.jzkit.search.util.QueryModel.Internal.ComplexNode; +import org.jzkit.search.util.QueryModel.Internal.InternalModelNamespaceNode; +import org.jzkit.search.util.QueryModel.Internal.InternalModelRootNode; +import org.jzkit.search.util.QueryModel.Internal.QueryNode; +import org.springframework.context.ApplicationContext; +import org.wmo.geonet.utils.jzkitextensions.GNCQLString; +import org.z3950.zing.cql.CQLNode; +import org.z3950.zing.cql.CQLTermNode; + + +/** + * @author 'Timo Proescholdt ' + * adds default namespaces to Attributes, Relations and Structures + * FIXME: this is a huge mess that just tries to address some of the inconsistencies of + * JZKit. Should better be addressed by re-implementing the CQLString in JZKit. + */ +public class DefaultContextSetCQLString extends GNCQLString { + + private String default_attr_namespace; + private String default_rel_namespace; + private String default_struct_namespace; + private boolean processed = false; + private boolean force_def = false; + + /** + * @param cqlRoot + */ + /** + * @param cqlRoot + * @param default_attr_namespace + * @param default_rel_namespace + * @param default_struct_namespace + */ + public DefaultContextSetCQLString(CQLNode cqlRoot, String default_attr_namespace, String default_rel_namespace, String default_struct_namespace) { + super(cqlRoot); + this.default_attr_namespace=default_attr_namespace; + this.default_rel_namespace=default_rel_namespace; + this.default_struct_namespace = default_struct_namespace; + } + + /** + * parse SQL string and set default context sets of attributes,relations and structures + * @param cqlString + * @param default_attr_namespace + * @param default_rel_namespace + * @param default_struct_namespace + */ + public DefaultContextSetCQLString(String cqlString, String default_attr_namespace, String default_rel_namespace, String default_struct_namespace) { + super(cqlString); + this.default_attr_namespace=default_attr_namespace; + this.default_rel_namespace=default_rel_namespace; + this.default_struct_namespace = default_struct_namespace; + } + + + /** + * @param force + * force the overwriting of attribute and relation default context sets + */ + public void setForceContextSet(boolean force) { + this.force_def = force; + } + + /* + public InternalModelRootNode toInternalQueryModel(ApplicationContext ctx) throws InvalidQueryException { + + + + InternalModelRootNode result = super.toInternalQueryModel(ctx); + + if (!processed) { + // add default context sets + Log.debug(Geonet.SRU_SEARCH," adding default context sets.."); + visitNode(result,null,null) ; + processed=true; + } + return result; + } */ + + + protected void processCQLTermNode(AttrPlusTermNode aptn, CQLTermNode cql_term_node) { + + super.processCQLTermNode(aptn,cql_term_node); + + Log.debug(Geonet.SRU_SEARCH,"Processing attrplustermnode:"+aptn); + // Look up conversion information for source node + + // set default relation context set + if ( aptn.getRelation() != null ) { + AttrValue relation = (AttrValue)aptn.getRelation(); + + if ( relation !=null && (relation.getNamespaceIdentifier() == null || force_def )) { + relation.setNamespaceIdentifier(this.default_rel_namespace); + Log.debug(Geonet.SRU_SEARCH,"Processing relation :"+relation); + } + } + + // set default attribute context set + + Object ap_node = aptn.getAccessPoint(); + if ( ap_node != null ) { + AttrValue qualifier = (AttrValue)ap_node; + + if ( qualifier != null && ( qualifier.getNamespaceIdentifier() == null || force_def )) { + qualifier.setNamespaceIdentifier(this.default_attr_namespace); + Log.debug(Geonet.SRU_SEARCH,"Processing AccessPoint :"+qualifier); + + } + + // FIX incorrect behavior of very old CQL (0.0.7) library + if ( qualifier != null && + qualifier.getNamespaceIdentifier().equalsIgnoreCase("srw") && + qualifier.getValue().equalsIgnoreCase("serverChoice")) { + Log.debug(Geonet.SRU_SEARCH,"Setting srw context set to cql for serverChoice"); + qualifier.setNamespaceIdentifier("cql"); + } + + + } + + // set default structure context set + if ( aptn.getStructure() != null ) { + AttrValue structure = (AttrValue)aptn.getStructure(); + + if ( structure !=null && (structure.getNamespaceIdentifier() == null || force_def )) { + structure.setNamespaceIdentifier(this.default_struct_namespace); + Log.debug(Geonet.SRU_SEARCH,"Processing structure :"+structure); + } + } + + + } + + /* + public void visitNode(QueryNode node, String source_ns, String target_ns) { + Log.debug(Geonet.SRU_SEARCH,"visitNode"); + + if ( node instanceof InternalModelRootNode ) { + Log.debug(Geonet.SRU_SEARCH,"Processing root"); + // No special "Root" Node in cql + visitNode(((InternalModelRootNode)node).getChild(), source_ns, target_ns); + } + else if ( node instanceof InternalModelNamespaceNode ) { + Log.debug(Geonet.SRU_SEARCH,"Processing namespace: "+node); + InternalModelNamespaceNode ns_node = (InternalModelNamespaceNode)node; + + visitNode(ns_node.getChild(), ns_node.getAttrset(), target_ns); + + } + else if ( node instanceof ComplexNode ) { + Log.debug(Geonet.SRU_SEARCH,"Processing complex"); + visitNode ( ((ComplexNode)node).getLHS(), source_ns, target_ns ) ; + visitNode ( ((ComplexNode)node).getRHS(), source_ns, target_ns ); + + } + else if ( node instanceof AttrPlusTermNode ) { + AttrPlusTermNode aptn = (AttrPlusTermNode)node; + + + + + + } + + Log.debug(Geonet.SRU_SEARCH,"visitNode"); + + } + */ + +} Index: src/org/wmo/geonet/GNExplainInfoDTO.java =================================================================== --- src/org/wmo/geonet/GNExplainInfoDTO.java (revision 0) +++ src/org/wmo/geonet/GNExplainInfoDTO.java (revision 0) @@ -0,0 +1,47 @@ +package org.wmo.geonet; + +import java.util.HashMap; +import java.util.Map; + +/** + * Data transport object for explain operation + * @author 'Timo Proescholdt ' + * + */ +public class GNExplainInfoDTO { + + String id ; + String title; + Map map = new HashMap(); + + public GNExplainInfoDTO() { + + } + + public GNExplainInfoDTO(String id, String title) { + this.id=id; + this.title=title; + } + + public String getId() { + return id; + } + + public String getTitle() { + return title; + } + + /** + * @return + * map containing indices (and their namespaces) that map to + * this index + */ + public Map getMappings() { + return map; + } + + public void addMapping(String index,String namespace) { + map.put(index, namespace); + } + +} Index: src/org/wmo/geonet/GNSearchSessionFactory.java =================================================================== --- src/org/wmo/geonet/GNSearchSessionFactory.java (revision 0) +++ src/org/wmo/geonet/GNSearchSessionFactory.java (revision 0) @@ -0,0 +1,144 @@ +package org.wmo.geonet; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +import org.jzkit.ServiceDirectory.AttributeSetDBO; +import org.jzkit.ServiceDirectory.CollectionDescriptionDBO; +import org.jzkit.configuration.api.Configuration; +import org.jzkit.configuration.api.ConfigurationException; +import org.jzkit.configuration.api.RecordMappingInformationDBO; +import org.jzkit.search.ExplainInformationDTO; +import org.jzkit.search.impl.SearchSessionFactoryImpl; +import org.jzkit.search.util.Profile.AttrMappingDBO; +import org.jzkit.search.util.Profile.CrosswalkDBO; +import org.jzkit.search.util.QueryModel.Internal.AttrValue; +import org.springframework.context.ApplicationContext; +import javax.annotation.*; + +/** + * Overloaded JZKit Search Factory with added explain operation functionality + * @author 'Timo Proescholdt ' + * + */ +public class GNSearchSessionFactory extends SearchSessionFactoryImpl { + + protected ApplicationContext appl_ctx; + + + + + public void setApplicationContext(ApplicationContext ctx) { + super.setApplicationContext(ctx); + this.appl_ctx=ctx; + } + + public ExplainInformationDTO explain() { + + ExplainInformationDTO result = new ExplainInformationDTO(); + + Configuration directory = (Configuration) appl_ctx.getBean("JZKitConfig"); + try { + // Populate explain information + + // if we used ValidIndices we would use this code + + /* + + CollectionDescriptionDBO cd = directory.lookupCollectionDescription("Default"); + if (cd!= null ) { + + Map mappings = cd.getSearchServiceDescription().getServiceSpecificTranslations(); + Map reverse_mappings = new HashMap(); + for (String key: mappings.keySet()) { + reverse_mappings.put(mappings.get(key).toString(), key); + } + + AttributeSetDBO attrs = cd.getSearchServiceDescription().getValidAttrs().get("AccessPoint"); + + + + + List list = new ArrayList(); + for (AttrValue val: attrs.getAttrs() ) { + + GNExplainInfoDTO exl = new GNExplainInfoDTO(); + exl.addMapping(val.getValue(), val.getNamespaceIdentifier()); + + String reverse_key = val.getNamespaceIdentifier()+":"+val.getValue(); + if (reverse_mappings.containsKey(reverse_key)) { + String[] temp = reverse_mappings.get(reverse_key).split("\\."); + if (temp.length==3) exl.addMapping(temp[1]+"."+temp[2], temp[0]); + if (temp.length==2) exl.addMapping(temp[1], temp[0]); + } + + list.add(exl); + } + + result.setDatabaseInfo(list); + + } + + */ + + // but we use Crosswalks (the info on what we actually accept is in the geo profile, but we suppose + // that the crosswalks will eventually lead to a valid attribute) + + // this should really be done differently but unfortunately there is + // no way to know if a mapping is for an attribute (can be a relation, too) + // we take as indicator the fact that are is a "1" + Pattern p = Pattern.compile(".*\\.1\\.[0-9]+$" ); + + Iterator it = directory.enumerateCrosswalks(); + + List list = new ArrayList(); + while (it.hasNext()) { + + + CrosswalkDBO cw = it.next(); + + for ( String key : cw.getMappings().keySet() ) { + + AttrMappingDBO attrmaping = cw.getMappings().get(key); + + if (attrmaping.getTargetAttrs().isEmpty() ) { + continue; + } + + // namespace is not important, just important that it is there (for the patternmatching) + String attrString = attrmaping.getTargetAttrs().iterator().next().getWithDefaultNamespace("geo"); + + // find out which are actual attribute mappings (this is ugly) + // "geo:1.4" matches, "something:1.3443", too, but not "gagh" or "geo.2.22" + if ( p.matcher( attrString ).matches() ) { + + GNExplainInfoDTO exl = new GNExplainInfoDTO(); + exl.addMapping( attrmaping.getSourceAttrValue() , cw.getSourceNamespace() ) ; + + list.add(exl); + + } + + } + + } + + result.setDatabaseInfo(list); + + } + catch ( ConfigurationException ce) { + ce.printStackTrace(); + } + + + return result; + + } + + +} Index: src/org/wmo/geonet/GNXMLQuery.java =================================================================== --- src/org/wmo/geonet/GNXMLQuery.java (revision 0) +++ src/org/wmo/geonet/GNXMLQuery.java (revision 0) @@ -0,0 +1,184 @@ +package org.wmo.geonet; + +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Stack; + +import jeeves.utils.Log; +import jeeves.utils.Xml; + +import org.fao.geonet.constants.Geonet; +import org.jdom.Attribute; +import org.jdom.Element; +import org.jzkit.search.provider.iface.IRQuery; +import org.jzkit.search.util.QueryModel.InvalidQueryException; +import org.jzkit.search.util.QueryModel.QueryModel; +import org.jzkit.search.util.QueryModel.Internal.AttrPlusTermNode; +import org.jzkit.search.util.QueryModel.Internal.AttrValue; +import org.jzkit.search.util.QueryModel.Internal.ComplexNode; +import org.jzkit.search.util.QueryModel.Internal.InternalModelNamespaceNode; +import org.jzkit.search.util.QueryModel.Internal.InternalModelRootNode; +import org.jzkit.search.util.QueryModel.Internal.QueryNode; +import org.jzkit.search.util.QueryModel.Internal.QueryNodeVisitor; +import org.jzkit.z3950.QueryModel.Z3950AttrTriple; +import org.springframework.context.ApplicationContext; + +/** + * transforms a JZKit internal query into the GN XML query format + * @author 'Timo Proescholdt ' + * + */ +public class GNXMLQuery { + + + ApplicationContext ctx ; + QueryModel querymodel ; + + public GNXMLQuery(IRQuery q, ApplicationContext ctx) { + + this.ctx=ctx; + this.querymodel=q.getQueryModel(); + + } + + + public Element toGNXMLRep() { + + GNRemoteQueryDecoder decoder = new GNRemoteQueryDecoder(querymodel , ctx); + return decoder.getQuery(); + } + + public String toString() { + return ""; + } + +} + +//-------------------------------------------------------------------------- +//converts an internal query format query to GN xml +class GNRemoteQueryDecoder +{ + private Stack stack = new Stack(); + private String attrSet; + + public GNRemoteQueryDecoder(QueryModel qm, ApplicationContext ctx) + { + try + { + InternalModelRootNode rn = qm.toInternalQueryModel(ctx); + QueryNodeVisitor qnv = new QueryNodeVisitor() + { + public void visit(AttrPlusTermNode aptn) + { + super.visit(aptn); + Element node = new Element("term"); + + if (aptn.getAccessPoint() != null) { + node.setAttribute(new Attribute("use", getAttrVal((AttrValue)aptn.getAccessPoint())) ); + } + + if (aptn.getRelation() != null) { + node.setAttribute(new Attribute("relation", getAttrVal((AttrValue)aptn.getRelation())) ); + } + + if (aptn.getStructure() != null) { + node.setAttribute(new Attribute("structure", getAttrVal((AttrValue)aptn.getStructure()) )); + } + + if (aptn.getTruncation() != null) { + node.setAttribute(new Attribute("truncation", getAttrVal((AttrValue)aptn.getTruncation()) )); + } + + node.addContent(aptn.getTermAsString(false)); + stack.push(node); + } + public void visit(ComplexNode cn) + { + super.visit(cn); + Element rightChild = (Element)stack.pop(); + Element leftChild = (Element)stack.pop(); + Element node = new Element(getOpString(cn.getOp())); + node.addContent(leftChild); + node.addContent(rightChild); + stack.push(node); + } + + public void visit(InternalModelRootNode rn) + { + super.visit(rn); + Element query = new Element("query"); + + //QueryNode node = rn.getChild(); TODO: I dont know if this is important + //query.setAttribute("attrset", node.getAttrs().toString() ); + query.addContent((Element)stack.pop()); + stack.push(query); + } + @Override + public void onAttrPlusTermNode(AttrPlusTermNode aptn) { + Log.debug(Geonet.Z3950_SERVER, "doing nothing..."+aptn); //TODO: find out how this is supposed to be used + } + + + + /** + * @param val + * @return + * extracts the last index of an attribute (e.G 1.4 becomes 4) + */ + private String getAttrVal(AttrValue val) { + + if (val == null || val.getValue() == null ) return null; + + String value = val.getValue(); + String ret=value; + + String[] temp = value.split("\\."); + + if (temp!=null && temp.length>1 ) { + ret = temp[temp.length-1]; + } + + return ret; + } + + }; + qnv.visit(rn); + } + catch (InvalidQueryException iqe) + { + iqe.printStackTrace(); + } + } + + public Element getQuery() + { + return (Element)stack.peek(); + } + + public String getAttrSet() + { + return attrSet; + } + + public String toString() + { + return Xml.getString(getQuery()); + } + + private String getOpString(int op) + { + switch (op) + { + case ComplexNode.COMPLEX_AND: return "and"; + case ComplexNode.COMPLEX_ANDNOT: return "not"; + case ComplexNode.COMPLEX_OR: return "or"; + case ComplexNode.COMPLEX_PROX: return "prox"; + default: return op+""; + } + } + + + + +} Index: src/org/wmo/geonet/provider/GN/GNResultSet.java =================================================================== --- src/org/wmo/geonet/provider/GN/GNResultSet.java (revision 0) +++ src/org/wmo/geonet/provider/GN/GNResultSet.java (revision 0) @@ -0,0 +1,215 @@ +package org.wmo.geonet.provider.GN; + +import java.util.List; +import java.util.Observer; + +import jeeves.server.ServiceConfig; +import jeeves.server.context.ServiceContext; +import jeeves.utils.Log; +import jeeves.utils.Xml; + +import net.sf.saxon.expr.FirstItemExpression; + +import org.fao.geonet.GeonetContext; +import org.fao.geonet.constants.Geonet; +import org.fao.geonet.kernel.search.MetaSearcher; +import org.fao.geonet.kernel.search.SearchManager; +import org.jdom.Document; +import org.jdom.Element; +import org.jdom.output.DOMOutputter; +import org.jdom.output.XMLOutputter; +import org.jzkit.search.util.RecordModel.ExplicitRecordFormatSpecification; +import org.jzkit.search.util.RecordModel.InformationFragment; +import org.jzkit.search.util.RecordModel.InformationFragmentImpl; +import org.jzkit.search.util.RecordModel.RecordFormatSpecification; +import org.jzkit.search.util.ResultSet.AbstractIRResultSet; +import org.jzkit.search.util.ResultSet.IFSNotificationTarget; +import org.jzkit.search.util.ResultSet.IRResultSet; +import org.jzkit.search.util.ResultSet.IRResultSetException; +import org.jzkit.search.util.ResultSet.IRResultSetInfo; +import org.jzkit.search.util.ResultSet.IRResultSetStatus; +import org.w3c.dom.Node; +import org.wmo.geonet.GNXMLQuery; + +/** + * interface between JZKit and GN. Retrieves XML content from the GN backend and + * makes it available to JZkit + * @author 'Timo Proescholdt ' + * + */ +public class GNResultSet extends AbstractIRResultSet implements IRResultSet { + + private GNXMLQuery query; + private ServiceContext srvxtx; + private int status; + + private int fragmentcount; + + private MetaSearcher metasearcher; + + public GNResultSet(GNXMLQuery query, Object userInfo, Observer[] observers, + ServiceContext srvctx) throws Exception { + super(observers); + this.query = query; + this.srvxtx = srvctx; + + try { + + GeonetContext gc = (GeonetContext) this.srvxtx + .getHandlerContext(Geonet.CONTEXT_NAME); + SearchManager searchMan = gc.getSearchmanager(); + + metasearcher = searchMan.newSearcher(SearchManager.LUCENE, + Geonet.File.SEARCH_Z3950_SERVER); + + } catch (Exception e) { + Log.debug(Geonet.Z3950_SERVER, "error constructing GNresult set: " + + e); + e.printStackTrace(); + } + } + + public int evaluate(int timeout) { + try { + Log.debug(Geonet.Z3950_SERVER, "INCOMING XML QUERY:\n" + query); + + Element request = new Element("request"); + request.addContent(query.toGNXMLRep()); + + ServiceConfig config = new ServiceConfig(); + + // perform the search and save search results + + metasearcher.search(this.srvxtx, request, config); + + // System.out.println("summary:\n" + Xml.getString(s.getSummary())); + // // DEBUG + + // Random number of records.. Set up the result set + setFragmentCount(metasearcher.getSize()); + setTaskStatusCode(IRResultSetStatus.COMPLETE); + + this.srvxtx.getResourceManager().close(); + } catch (Throwable e) { + Log.error(Geonet.Z3950_SERVER, "error evaluating query.." + e); + e.printStackTrace(); + + try { + this.srvxtx.getResourceManager().abort(); + } catch (Exception e2) { + e2.printStackTrace(); + } + } + return (getStatus()); + } + + + + public InformationFragment[] getFragment(int startingFragment, int count, + RecordFormatSpecification spec) throws IRResultSetException { + Log.debug(Geonet.Z3950_SERVER, "Request for fragment start:" + + startingFragment + ", count:" + count); + + InformationFragment fragment[] = new InformationFragment[count]; + + ExplicitRecordFormatSpecification rec_spec = new ExplicitRecordFormatSpecification( "xml", "f", null); + + try { + // build fragment data + int from = startingFragment; + int to = startingFragment + count - 1; + + Element request = new Element("request"); + request.addContent(new Element("from").setText(from + "")); + request.addContent(new Element("to").setText(to + "")); + ServiceConfig config = new ServiceConfig(); + + Log.debug(Geonet.Z3950_SERVER, "Search request:\n" + + Xml.getString(request)); + // get result set + Element result = this.metasearcher.present(this.srvxtx, request, + config); + + Log.debug(Geonet.Z3950_SERVER, "Search result:\n" + + Xml.getString(result)); + + // remove summary + result.removeChildren("summary"); + List list = result.getChildren(); + + Log.debug(Geonet.Z3950_SERVER, "Set name asked:" + spec); + + // save other records to fragment + for (int i = 0; i < count; i++) { + Element md = (Element) list.get(0); + md.detach(); + + Log.debug(Geonet.Z3950_SERVER, "Returning fragment:\n" + + Xml.getString(md)); + + // add metadata + + //fragment[i] = new DOMTree("geonetwork", "geonetwork", null, getRecord(md),rec_spec ); + //fragment[i].setHitNo(startingFragment+i); + + DOMOutputter outputter = new DOMOutputter(); + Document doc = new Document(md); + org.w3c.dom.Document doc2 = outputter.output(doc); + + fragment[i] = new InformationFragmentImpl(startingFragment+i,"geonetwork","geonetwork",null,doc2,rec_spec); + + //System.err.println(fragment[i]); + + } + this.srvxtx.getResourceManager().close(); + Log.debug(Geonet.Z3950_SERVER, "Fragment returned"); + } catch (Throwable e) { + try { + this.srvxtx.getResourceManager().abort(); + } catch (Exception e2) { + e2.printStackTrace(); + } + + e.printStackTrace(); + } + + return fragment; + } + + public void asyncGetFragment(int starting_fragment, int count, + RecordFormatSpecification spec, IFSNotificationTarget target) throws IRResultSetException { + InformationFragment[] result = getFragment(starting_fragment, count, + spec); + target.notifyRecords(result); + + } + + public void close() { + this.metasearcher.close(); + } + + private void setTaskStatusCode(int i) { + this.status = i; + } + + private void setFragmentCount(int i) { + this.fragmentcount = i; + } + + public int getFragmentCount() { + return this.fragmentcount; + } + + public int getRecordAvailableHWM() { + return getFragmentCount(); + } + + public IRResultSetInfo getResultSetInfo() { + + return new IRResultSetInfo("GNDefault", this.fragmentcount, this.status); + + } + + + +} Index: src/org/wmo/geonet/provider/GN/GNSearchable.java =================================================================== --- src/org/wmo/geonet/provider/GN/GNSearchable.java (revision 0) +++ src/org/wmo/geonet/provider/GN/GNSearchable.java (revision 0) @@ -0,0 +1,97 @@ +package org.wmo.geonet.provider.GN; + +import java.util.Map; +import java.util.Observer; + +//import org.jzkit.search.provider.SRU.SRUResultSet; +import org.jzkit.search.provider.iface.IRQuery; +import org.jzkit.search.provider.iface.Searchable; +import org.jzkit.search.util.ResultSet.IRResultSet; +import org.jzkit.search.util.ResultSet.IRResultSetStatus; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.wmo.geonet.ContextContainer; +import org.wmo.geonet.GNXMLQuery; + +import jeeves.server.context.ServiceContext; +import jeeves.utils.Log; + +import org.fao.geonet.constants.Geonet; +//import org.fao.geonet.services.util.z3950.GNSearchTask; + +/** + * @author 'Timo Proescholdt ' + * interface between JZkit and GN. not currently used + */ +public class GNSearchable implements Searchable { + + + private Map recordArchetypes ; + private int timeout; + + private ApplicationContext ctx; + + + public GNSearchable() { + Log.debug(Geonet.Z3950_SERVER, "creating GNSearchable"); + } + + public void close() { + + } + + public void setTimeout(int i) { + this.timeout=i; + } + + public IRResultSet evaluate(IRQuery q) { + + return this.evaluate(q, null, null); + + } + + public IRResultSet evaluate(IRQuery q, Object userInfo) { + return this.evaluate(q, userInfo, null); + } + + public IRResultSet evaluate(IRQuery q, Object userInfo, Observer[] observers) { + + Log.debug(Geonet.Z3950_SERVER, "evaluating..."); + + ContextContainer cnt = (ContextContainer)ctx.getBean("ContextGateway"); + + + GNResultSet result = null; + + try { + result = new GNResultSet( new GNXMLQuery(q, ctx) , userInfo , observers ,cnt.getSrvctx()); // SRUResultSet(observers, base_url, getCQLString(q), code); + result.evaluate(timeout); + result.setStatus(IRResultSetStatus.COMPLETE); + } + catch ( Exception e ) { + result.setStatus(IRResultSetStatus.FAILURE); + e.printStackTrace(); + } + + return result; + } + + + public Map getRecordArchetypes() { + return this.recordArchetypes; + } + + public void setRecordArchetypes(Map recordSyntaxArchetypes) { + this.recordArchetypes=recordSyntaxArchetypes; + + } + + public void setApplicationContext(ApplicationContext ctx) + throws BeansException { + this.ctx=ctx; + + } + + +} Index: src/org/wmo/geonet/services/main/SRUSearch.java =================================================================== --- src/org/wmo/geonet/services/main/SRUSearch.java (revision 0) +++ src/org/wmo/geonet/services/main/SRUSearch.java (revision 0) @@ -0,0 +1,542 @@ +//============================================================================= +//=== Copyright (C) 2009 World Meteorological Organization +//=== This program is free software; you can redistribute it and/or modify +//=== it under the terms of the GNU General Public License as published by +//=== the Free Software Foundation; either version 2 of the License, or (at +//=== your option) any later version. +//=== +//=== This program is distributed in the hope that it will be useful, but +//=== WITHOUT ANY WARRANTY; without even the implied warranty of +//=== MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +//=== General Public License for more details. +//=== +//=== You should have received a copy of the GNU General Public License +//=== along with this program; if not, write to the Free Software +//=== Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +//=== +//=== Contact: Timo Proescholdt +//=== email: tproescholdt_at_wmo.int +//============================================================================== + +package org.wmo.geonet.services.main; + + +import java.util.Collection; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; + +import org.jdom.*; +import org.jdom.input.DOMBuilder; +import org.jzkit.search.ExplainInformationDTO; +import org.jzkit.search.LandscapeSpecification; +import org.jzkit.search.SearchSessionFactory; +import org.jzkit.search.StatelessSearchResultsPageDTO; +import org.jzkit.search.landscape.SimpleLandscapeSpecification; +import org.jzkit.search.util.QueryModel.QueryModel; +import org.jzkit.search.util.RecordModel.ArchetypeRecordFormatSpecification; +import org.jzkit.search.util.RecordModel.ExplicitRecordFormatSpecification; +import org.jzkit.search.util.RecordModel.RecordFormatSpecification; +import org.springframework.context.ApplicationContext; +import org.w3c.dom.Document; +import org.wmo.geonet.DefaultContextSetCQLString; +import org.wmo.geonet.GNExplainInfoDTO; +import org.wmo.geonet.utils.RepositoryInfo; + +import jeeves.constants.Jeeves; +import jeeves.exceptions.MissingParameterEx; +import jeeves.interfaces.*; +import jeeves.server.*; +import jeeves.server.context.*; +import jeeves.utils.Log; + +import org.fao.geonet.GeonetContext; +import org.fao.geonet.constants.*; +import org.fao.geonet.kernel.SelectionManager; +import org.fao.geonet.kernel.search.MetaSearcher; +import org.fao.geonet.kernel.search.SearchManager; +import org.fao.geonet.services.util.MainUtil; + +//============================================================================= + +/** SRU service. Perform a SRU websearch via JZkit + * implements rearchAndRetrieve and Explain operations + * @author 'Timo Proescholdt ' + * + */ +public class SRUSearch implements Service +{ + private ServiceConfig _config; + + private static RecordFormatSpecification request_spec = new ArchetypeRecordFormatSpecification("F"); + + public static final int SRU_records_per_page = 10; + + public static final String OP = "operation"; + + public static final String OP_SR_QUERY = "query"; + public static final String OP_SR_VERSION = "version"; + public static final String OP_SR_STYLESH = "stylesheet"; + public static final String OP_SR_STARTREC = "startrecord"; + public static final String OP_SR_MAXREC = "maximumrecords"; + public static final String OP_SR_RECPACK = "recordpacking"; + public static final String OP_SR_RECSCHEMA = "recordschema"; + public static final String OP_SR_RECXPATH = "recordxpath"; + public static final String OP_SR_SORTKEYS = "sortkeys"; + public static final String OP_SR_EXTRADATA = "extrarequestdata"; + + public static final String OP_EXPL_RECPACK = "query"; + public static final String OP_EXPL_VERSION = "version"; + public static final String OP_EXPL_STYLESH = "stylesheet"; + + + private Hashtable mandatorySR ; + private Hashtable mandatoryEXPL ; + + private SearchSessionFactory searchsessionfact ; + + private Hashtable contextSets ; + + //-------------------------------------------------------------------------- + //--- + //--- Init + //--- + //-------------------------------------------------------------------------- + + public void init(String appPath, ServiceConfig config) throws Exception + { + Log.debug(Geonet.SRU,"SRUsearch::init"); + + _config = config; + + // to parse the SRU parameters + mandatorySR = new Hashtable(); + mandatorySR.put(OP, true); + mandatorySR.put(OP_SR_VERSION, true); + mandatorySR.put(OP_SR_QUERY, true); + + mandatoryEXPL = new Hashtable(); + mandatoryEXPL.put(OP, true); + mandatoryEXPL.put(OP_EXPL_VERSION, true); + + + contextSets = new Hashtable(); + + contextSets.put("dc", "info:srw/cql-context-set/1/dc-v1.1"); + contextSets.put("gils", "info:srw/cql-context-set/14/gils-v1.0"); + contextSets.put("geo", "http://??"); + contextSets.put("cql", "info:srw/cql-context-set/1/cql-v1.2"); + contextSets.put("rec", "info:srw/cql-context-set/2/rec-1.1"); + + + } + + + //-------------------------------------------------------------------------- + //--- + //--- Service + //--- + //-------------------------------------------------------------------------- + + public Element exec(Element params, ServiceContext context) throws Exception + { + + + Hashtable myparams = parseArgs(params.getChildren()); + + Log.debug(Geonet.SRU,"SRUsearch::exec op:"+myparams.get("operation")); + + + + String op = myparams.get("operation"); + Element ret = null; + + if ( op.equalsIgnoreCase("searchretrieve") ) + ret = processSearchRetrieve(myparams,context); + else if ( op.equalsIgnoreCase("explain") ) + ret = processExplain(myparams,context); + else if ( op.equalsIgnoreCase("testListRemoteRepositories")) { + ret = processListRemoteReps(myparams,context); + } + else if ( op.equalsIgnoreCase("testRemoteSearch")) { + ret = processTestRemoteSearch(myparams,context); + } + else + throw new Exception("operation "+op+" not supported"); + return ret; + + } + + private Element processTestRemoteSearch(Hashtable params, ServiceContext context) throws Exception { + + // simulate incoming remote search request + + /* + * + * + + + + overlaps + on + 10 + 180 + + <southBL>-90</southBL> + <servers>127.0.0.1:7002/marlin</servers> + <any>test</any> + <northBL>90</northBL> + <serverhtml>off</serverhtml> + <westBL>-180</westBL> + <timeout>20</timeout> + <attrset>geo</attrset> +</request> + * + */ + + Log.debug(Geonet.SRU,"SRUsearch::processTestRemoteSearch"); + + + Element req = new Element("request"); + + req.addContent( new Element("abstract")); + req.addContent( new Element("region")); + req.addContent( new Element("title")); + + req.addContent( new Element("relation").setText("overlaps") ); + req.addContent( new Element("hitsPerPage").setText("10")); + req.addContent( new Element("any").setText("test")); + req.addContent( new Element("timeout").setText("20")); + req.addContent( new Element("attrset").setText("geo")); + + req.addContent( new Element("servers").setText("127.0.0.1:7001/nrdd")); + req.addContent( new Element("servers").setText("127.0.0.1:7002/marlin")); + + req.addContent( new Element("westBL").setText("-180")); + req.addContent( new Element("eastBL").setText("180")); + req.addContent( new Element("southBL").setText("-90")); + req.addContent( new Element("westBL").setText("-190")); + + GeonetContext gc = (GeonetContext) context.getHandlerContext(Geonet.CONTEXT_NAME); + + SearchManager searchMan = gc.getSearchmanager(); + + + // possibly close old searcher + UserSession session = context.getUserSession(); + MetaSearcher oldSearcher = (MetaSearcher)session.getProperty(Geonet.Session.SEARCH_RESULT); + + if (oldSearcher != null) + oldSearcher.close(); + + // possibly close old selection + SelectionManager oldSelection = (SelectionManager)session.getProperty(Geonet.Session.SELECTED_RESULT); + + if (oldSelection != null){ + oldSelection.close(); + oldSelection = null; + } + + // perform the search and save search result into session + MetaSearcher searcher; + + context.info("Creating searchers"); + + searcher = searchMan.newSearcher(SearchManager.Z3950, Geonet.File.SEARCH_Z3950_CLIENT); + + searcher.search(context, req, _config); + session.setProperty(Geonet.Session.SEARCH_RESULT, searcher); + + context.info("Getting summary"); + + Element summary = searcher.getSummary(); + + // loop through results + + + Element pres_req = new Element("request"); + + pres_req.addContent( new Element("syntax").setText("f") ); + pres_req.addContent( new Element("from").setText("1") ); + pres_req.addContent( new Element("to").setText("4") ); + + + searcher.present(context, pres_req, _config); + + //searcher.p + + return summary; + + + //return response; + } + + private Element processListRemoteReps(Hashtable<String,String> params, ServiceContext context) throws Exception { + + Log.debug(Geonet.SRU,"SRUsearch::processListRemoteReps"); + + Element response = new Element(Jeeves.Elem.RESPONSE); + + Collection<RepositoryInfo> col = RepositoryInfo.getRepositories(context); + + for ( RepositoryInfo ri : col ) { + + Log.debug(Geonet.SRU,"SRUsearch::processListRemoteReps: add rep:"+ri); + + if (ri.getName() != null && ri.getDn() != null) { + Element rep = new Element("repository"); + rep.setAttribute( new Attribute("name",ri.getName())); + rep.setAttribute( new Attribute("dn",ri.getDn())); + response.addContent(rep); + } + + + } + + + return response; + } + + + private Element processExplain(Hashtable<String,String> params, ServiceContext context) throws Exception { + + Log.debug(Geonet.SRU,"processExplain"); + + checkMandatoryParams(params, mandatoryEXPL); + + + SearchSessionFactory search_session_factory = getSearchSession(context); + + ExplainInformationDTO explain = search_session_factory.explain(); + + + Hashtable<String, Boolean> seenContextSets = new Hashtable<String, Boolean>(); + + + Element response = new Element(Jeeves.Elem.RESPONSE); + + response.setAttribute(new Attribute("servername", context.getIpAddress() )); + response.setAttribute(new Attribute("port", "????")); //FIXME: dont know where I should get that info from + response.setAttribute(new Attribute("sruuri", context.getBaseUrl()+"/srv/"+context.getLanguage()+"/"+context.getService() )); //FIXME: can I get the query string from somewhere? + response.setAttribute(new Attribute("records_per_page",SRU_records_per_page+"")); + + Element indices = new Element("indices"); + for (Object o : explain.getDatabaseInfo()) { + GNExplainInfoDTO ex = (GNExplainInfoDTO)o; + + Element index = new Element("index"); + + for (String key : ex.getMappings().keySet()) { + Element map = new Element("map"); + + String contextSet = ex.getMappings().get(key); + + map.setAttribute("set", contextSet ); + map.setAttribute("text",key); + + seenContextSets.put(ex.getMappings().get(key), true); + + index.addContent(map); + } + + indices.addContent(index); + + } + + Element sets = new Element("sets"); + + Enumeration<String> enu = seenContextSets.keys(); + + while ( enu.hasMoreElements() ) { + + Element set = new Element("set"); + + String namespace = enu.nextElement(); + String url = "http://???"; + if ( this.contextSets.containsKey(namespace)) { + url = this.contextSets.get(namespace); + } + + + set.setAttribute(new Attribute("namespace",namespace )); + set.setAttribute(new Attribute("url",url)); + + sets.addContent(set); + } + + response.addContent(sets); + response.addContent(indices); + + return response; + } + + + + private Element processSearchRetrieve(Hashtable<String,String> params, ServiceContext context) throws Exception { + + Log.debug(Geonet.SRU,"processSearchRetrieve"); + + checkMandatoryParams(params, mandatorySR); + + Element response = null; + + + int num_hits_per_page = SRU_records_per_page; + int first_record = 1; + + if ( params.get(OP_SR_MAXREC) != null ) + num_hits_per_page = Integer.parseInt(params.get(OP_SR_MAXREC)); + + if ( params.get(OP_SR_STARTREC) != null ) + first_record = Integer.parseInt(params.get(OP_SR_STARTREC)); + + String record_schema = params.get(OP_SR_RECSCHEMA); + if ( record_schema == null ) + record_schema = "meta" ; + + String query = params.get(OP_SR_QUERY); + + + ExplicitRecordFormatSpecification display_spec = new ExplicitRecordFormatSpecification("xml","f",null); + + try { + Log.debug(Geonet.SRU,"getting reference to search session factory"); + + + // TODO: would be nice to move this to init method but I dont know where to get the context from there.. + + // not supported by Geonetwork modules URL layout schema + LandscapeSpecification landscape = new SimpleLandscapeSpecification("Default"); + + DefaultContextSetCQLString model = new DefaultContextSetCQLString(query, "geo", "cql", "geo"); + // we assume that all incoming queries are from the geo (attributes,structure) and cql (relation) context sets + // if we set this to false we have to write a crosswalk for bib-1,dc.... + //model.setForceContextSet(true); + + SearchSessionFactory search_session_factory = getSearchSession(context); + + + Log.debug(Geonet.SRU,"Calling search_session_factory.getResultsPageFor"); + StatelessSearchResultsPageDTO result = search_session_factory.getResultsPageFor(null, + model, + landscape, + first_record, + num_hits_per_page, + request_spec, + display_spec, + null); + Log.debug(Geonet.SRU,"Call to getResultsPageFor completed : "+result); + + + response = new Element(Jeeves.Elem.RESPONSE); + + int last_record = 0; + + if ( ( result.records != null ) && ( result.records.length > 0 ) ) { + DOMBuilder builder = new DOMBuilder(); + + for ( int i=0; i<result.records.length;i++ ) { + + + // check if the format corresponds to what we are requesting + if ( ! result.records[i].getFormatSpecification().equals(display_spec) ) { + + Log.error(Geonet.CSW_SEARCH, "error, format specification does not correspond :"+result.records[i].getOriginalObject() ); + + throw new Exception("SRU error:"+result.records[i].getOriginalObject()); + + } + + else if (result.records[i].getOriginalObject() instanceof Document ) + { + Document d = (Document)result.records[i].getOriginalObject(); + + Element elem = new Element("record"); + elem.setAttribute( new Attribute("recordPosition",""+first_record+i)); + response.addContent( elem ); + + // FIXME: ARHHHHH!!!!! this is inefficient... there must be another way of doing this + org.jdom.Document doc = builder.build(d); + + Element e = doc.getRootElement(); + e.detach(); + + elem.addContent( e ) ; + + } + else { + String errormsg="error: could not decode reponse object of type: "+result.records[i].getOriginalObject().getClass().getName(); + Log.error(Geonet.CSW_SEARCH, errormsg ); + throw new Exception(errormsg); + } + } + last_record = first_record+result.records.length-1; + } + + + } catch (Exception e) { + Log.error(Geonet.SRU, "problem at backend interaction" + e); + e.printStackTrace(); + throw e; + } + + + return response; + } + + + private SearchSessionFactory getSearchSession(ServiceContext context) { + + if (searchsessionfact == null) { + + GeonetContext gc = (GeonetContext) context.getHandlerContext(Geonet.CONTEXT_NAME); + ApplicationContext app_context = gc.getApplicationContext(); + this.searchsessionfact = (SearchSessionFactory)app_context.getBean("SearchSessionFactory"); + + } + + return searchsessionfact; + + } + + + private Hashtable<String, String> parseArgs(List<Element> params) throws MissingParameterEx { + + + Hashtable<String, String> res = new Hashtable<String, String>(); + + for (Iterator<Element> it = params.listIterator(); it.hasNext() ; ) { + + Element e = it.next(); + String name = e.getName().toLowerCase(); + String val = e.getText().toLowerCase(); + + res.put(name,val); + } + + Hashtable<String,Boolean> opht = new Hashtable<String, Boolean>(); + opht.put("operation", true); + + checkMandatoryParams(res, opht); + + return res; + } + + private void checkMandatoryParams(Hashtable<String,String> h1, Hashtable<String,Boolean> h2) throws MissingParameterEx { + + for (Enumeration<String> enu = h2.keys() ; enu.hasMoreElements() ; ) { + + String key = enu.nextElement(); + + if (h2.contains(key) & !h1.containsKey(key) ) + throw new MissingParameterEx(key,null); + } + + } + + + + +} + Index: src/org/wmo/geonet/utils/RepositoryInfo.java =================================================================== --- src/org/wmo/geonet/utils/RepositoryInfo.java (revision 0) +++ src/org/wmo/geonet/utils/RepositoryInfo.java (revision 0) @@ -0,0 +1,88 @@ +package org.wmo.geonet.utils; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Vector; + +import org.fao.geonet.GeonetContext; +import org.fao.geonet.constants.Geonet; +import org.jzkit.ServiceDirectory.CollectionDescriptionDBO; +import org.jzkit.ServiceDirectory.SearchServiceDescriptionDBO; +import org.jzkit.configuration.api.Configuration; +import org.jzkit.configuration.api.ConfigurationException; +import org.springframework.context.ApplicationContext; + +import jeeves.server.context.ServiceContext; + +/** + * helperclass to get a list of remote searchable repositories from + * the JZkit configuration + * + * @author 'Timo Proescholdt <tproescholdt@wmo.int>' + * + */ +public class RepositoryInfo { + + private String dn; + private String name; + + private RepositoryInfo(String dn, String name) { + this.name=name; + this.dn=dn; + } + + public String getDn() { + return dn; + } + + public String getName() { + return name; + } + + public String toString() { + return getName()+":"+getDn(); + } + + + /** + * returns the list of repositories that are configured in JZkit + * @param srvContext + * @return + * @throws ConfigurationException + */ + public static Collection<RepositoryInfo> getRepositories(ServiceContext srvContext) throws ConfigurationException { + + GeonetContext gc = (GeonetContext) srvContext.getHandlerContext(Geonet.CONTEXT_NAME); + ApplicationContext app_context = gc.getApplicationContext(); + + Configuration conf = (Configuration)app_context.getBean("JZKitConfig"); + + Vector<RepositoryInfo> ret = new Vector<RepositoryInfo>(); + + Iterator<SearchServiceDescriptionDBO> it = conf.enumerateRepositories(); + + while (it.hasNext()) { + + SearchServiceDescriptionDBO ssd = it.next(); + + Collection<CollectionDescriptionDBO> col = ssd.getCollections(); + + if (col.size()>0) { + + if (col.size()>1) { + srvContext.getLogger().info("Service "+ssd.getServiceName()+" has more than one repository.. taking only the first"); + } + + Iterator<CollectionDescriptionDBO> colit = col.iterator(); + + // TODO: add all collections of a service + ret.add( new RepositoryInfo( colit.next().getCode() , ssd.getServiceName()) ) ; + } + } + + return ret; + + } + + +} Index: src/org/wmo/geonet/utils/jzkitextensions/GNCQLString.java =================================================================== --- src/org/wmo/geonet/utils/jzkitextensions/GNCQLString.java (revision 0) +++ src/org/wmo/geonet/utils/jzkitextensions/GNCQLString.java (revision 0) @@ -0,0 +1,159 @@ +package org.wmo.geonet.utils.jzkitextensions; + + +import org.jzkit.search.util.QueryModel.*; +import org.jzkit.search.util.QueryModel.CQLString.CQLString; +import org.jzkit.search.util.QueryModel.Internal.*; +import org.z3950.zing.cql.*; +import org.springframework.context.ApplicationContext; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * code copied and pasted from JZKit sourcecode to fix a bug in the original class + * @author 'Ian Ibbotson <ianibbo@googlemail.com>' + * @author 'Timo Proescholdt <tproescholdt@wmo.int>' + * @see CQLString + */ +public class GNCQLString implements QueryModel, java.io.Serializable { + + private Log log = LogFactory.getLog(GNCQLString.class); + + private static String default_qualifier="cql.serverChoice"; + + private String the_cql_string; + private InternalModelRootNode internal_model = null; + private CQLNode cql_root; + + + public GNCQLString(String the_cql_string) { + + try { + this.the_cql_string = the_cql_string; + CQLParser parser = new CQLParser(); + cql_root = parser.parse(the_cql_string); + log.debug("Parsed CQL"); + } + catch ( org.z3950.zing.cql.CQLParseException cqle ) { + log.warn("Problem parsing CQL",cqle); + // cqle.printStackTrace(); + } + catch ( java.io.IOException ioe ) { + log.warn("Problem parsing CQL",ioe); + // ioe.printStackTrace(); + } + } + + public GNCQLString( CQLNode cql_root ) { + this.cql_root = cql_root; + } + + public InternalModelRootNode toInternalQueryModel(ApplicationContext ctx) throws InvalidQueryException { + if ( internal_model == null ) { + internal_model = new InternalModelRootNode(translate(cql_root)); + } + return internal_model; + } + + private QueryNode translate(CQLNode cql_node) { + QueryNode result = null; + + if ( cql_node instanceof CQLBooleanNode ) { + CQLBooleanNode cbn = (CQLBooleanNode)cql_node; + if ( cbn instanceof CQLAndNode ) { + result = new ComplexNode(translate(cbn.left), translate(cbn.right),ComplexNode.COMPLEX_AND); + } + else if ( cbn instanceof CQLOrNode ) { + result = new ComplexNode(translate(cbn.left),translate(cbn.right),ComplexNode.COMPLEX_OR); + } + else if ( cbn instanceof CQLNotNode ) { + result = new ComplexNode(translate(cbn.left),translate(cbn.right),ComplexNode.COMPLEX_ANDNOT); + } + else if ( cbn instanceof CQLProxNode ) { + result = new ComplexNode(translate(cbn.left),translate(cbn.right),ComplexNode.COMPLEX_PROX); + } + } + else if ( cql_node instanceof CQLTermNode ) { + log.debug("Warning: We should properly translate the CQLTermNode"); + CQLTermNode cql_term_node = (CQLTermNode) cql_node; + AttrPlusTermNode aptn = new AttrPlusTermNode(); + + + processCQLTermNode(aptn,cql_term_node); + + + result = aptn; + } + else if ( cql_node instanceof CQLPrefixNode ) { + CQLPrefixNode pn = (CQLPrefixNode)cql_node; + result = new InternalModelNamespaceNode(pn.prefix.name, translate(((CQLPrefixNode)cql_node).subtree)); + } + + return result; + } + + protected void processCQLTermNode(AttrPlusTermNode aptn, CQLTermNode cql_term_node) { + + aptn.setTerm(cql_term_node.getTerm()); + + if ( ( cql_term_node.getQualifier() != null ) && ( cql_term_node.getQualifier().length() > 0 ) ) { + log.debug("Using supplied qualifier : "+cql_term_node.getQualifier()); + aptn.setAttr(AttrPlusTermNode.ACCESS_POINT_ATTR,process(cql_term_node.getQualifier())); + } + else { + log.debug("Using default qualifier"); + aptn.setAttr(AttrPlusTermNode.ACCESS_POINT_ATTR,process(default_qualifier)); + } + + // CQL Relation object: + CQLRelation relation = cql_term_node.getRelation(); + String test = relation.getBase(); + + if ( relation != null ) { + if ( relation.getBase() != null ) { + if ( relation.getBase().equalsIgnoreCase("scr") ) { + aptn.setAttr(AttrPlusTermNode.RELATION_ATTR,new AttrValue("=")); + } + else if ( relation.getBase().equalsIgnoreCase("exact") ) { + aptn.setAttr(AttrPlusTermNode.RELATION_ATTR,new AttrValue("=")); + } + else if ( relation.getBase().equalsIgnoreCase("all") ) { + aptn.setAttr(AttrPlusTermNode.RELATION_ATTR,new AttrValue("=")); + } + else if ( relation.getBase().equalsIgnoreCase("any") ) { + aptn.setAttr(AttrPlusTermNode.RELATION_ATTR,new AttrValue("=")); + } + else { + aptn.setAttr(AttrPlusTermNode.RELATION_ATTR,new AttrValue(relation.getBase())); + } + } + } + + } + + private AttrValue process(String s) { + AttrValue result = null; + if ( ( s != null ) && ( s.length() > 0 ) ) { + String[] components = s.split("\\."); + if ( components.length == 1 ) { + result=new AttrValue(components[0]); + } + else if ( components.length == 2 ) { + result=new AttrValue(components[0],components[1]); + } + else { + result = new AttrValue(s); + } + } + return result; + } + + public String toString() { + if ( cql_root != null ) + return cql_root.toCQL(); + + return null; + } +} + Index: src/org/wmo/geonet/utils/jzkitextensions/GNProfileService.java =================================================================== --- src/org/wmo/geonet/utils/jzkitextensions/GNProfileService.java (revision 0) +++ src/org/wmo/geonet/utils/jzkitextensions/GNProfileService.java (revision 0) @@ -0,0 +1,305 @@ +package org.wmo.geonet.utils.jzkitextensions; + + +import java.util.*; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jzkit.search.util.Profile.CrosswalkDBO; +import org.jzkit.search.util.Profile.ProfileDBO; +import org.jzkit.search.util.Profile.ProfileService; +import org.jzkit.search.util.Profile.ProfileServiceException; +import org.jzkit.search.util.Profile.ProfileServiceImpl; +import org.jzkit.search.util.Profile.QueryVerifyResult; +import org.jzkit.search.util.QueryModel.*; +import org.jzkit.search.util.QueryModel.Internal.*; +import org.jzkit.configuration.api.*; +import org.springframework.context.*; +import org.jzkit.ServiceDirectory.AttributeSetDBO; + + +/** + * code copied and pasted from JZKit sourcecode to fix a bug in the original class + * @author 'Ian Ibbotson <ianibbo@googlemail.com>' + * @author 'Timo Proescholdt <tproescholdt@wmo.int>' + * @see ProfileServiceImpl + */ +public class GNProfileService implements ProfileService, ApplicationContextAware { + + // private Map m = new HashMap(); + private static Log log = LogFactory.getLog(GNProfileService.class); + private ApplicationContext ctx = null; + private Configuration configuration = null; + + /** If we can't map directly, abort */ + private static final int SEMANTIC_ACTION_STRICT = 1; + /** If we can't map directly, strip the un-mappable component (Should feedback somehow) */ + private static final int SEMANTIC_ACTION_STRIP = 2; + /** If we can't map directly, do a best effort match */ + private static final int SEMANTIC_ACTION_FUZZY = 3; + + public GNProfileService() { + } + + public void setApplicationContext(ApplicationContext ctx) { + this.ctx = ctx; + } + + public void setConfiguration(Configuration configuration) { + this.configuration = configuration; + } + + public Configuration getConfiguration() { + return configuration; + } + + /* For backwards compatibility */ + // public InternalModelRootNode makeConformant(QueryModel qm, String profile_code) throws ProfileServiceException { + // return makeConformant(qm,profile_code,SEMANTIC_ACTION_STRICT); + // } + + public InternalModelRootNode makeConformant(QueryModel qm, + Map<String,AttributeSetDBO> valid_attributes, + Map<String,AttrValue> service_specific_rewrite_rules, + String profile_code) throws ProfileServiceException { + + InternalModelRootNode result = null; + + // Walk the query tree.. validate each node. + log.debug("makeConformant profile:"+profile_code+" query:"+qm.toString()); + + try { + ProfileDBO p = configuration.lookupProfile(profile_code); + + if ( ( p == null ) && ( valid_attributes == null ) ) { + log.debug("No profile defined and no valid attributes list, unable to rewrite"); + result = qm.toInternalQueryModel(ctx); + } + else { + log.debug("Rewriting"); + result = (InternalModelRootNode) visit(qm.toInternalQueryModel(ctx), "bib-1", valid_attributes, service_specific_rewrite_rules, p); + } + } + catch ( org.jzkit.search.util.QueryModel.InvalidQueryException iqe ) { + throw new ProfileServiceException(iqe.toString()); + } + catch ( org.jzkit.configuration.api.ConfigurationException ce ) { + throw new ProfileServiceException(ce.toString()); + } + + // log.debug("makeConformant result="+result); + return result; + } + + private QueryNode visit(QueryNode qn, + String default_namespace, + Map<String,AttributeSetDBO> valid_attributes, + Map<String,AttrValue> service_specific_rewrite_rules, + ProfileDBO p) throws org.jzkit.search.util.QueryModel.InvalidQueryException, ProfileServiceException { + + if ( qn == null ) + throw new org.jzkit.search.util.QueryModel.InvalidQueryException("Query node was null, unable to rewrite"); + + log.debug("Rewrite: visit instance of "+qn.getClass().getName()); + + if ( qn instanceof InternalModelRootNode ) { + InternalModelRootNode imrn = (InternalModelRootNode)qn; + return new InternalModelRootNode(visit(imrn.getChild(), default_namespace, valid_attributes, service_specific_rewrite_rules, p)); + } + else if ( qn instanceof InternalModelNamespaceNode ) { + InternalModelNamespaceNode imns = (InternalModelNamespaceNode)qn; + log.debug("child default attrset will be "+imns.getAttrset()); + return new InternalModelNamespaceNode(imns.getAttrset(), visit(imns.getChild(), + imns.getAttrset(), + valid_attributes, + service_specific_rewrite_rules, + p)); + } + else if ( qn instanceof ComplexNode ) { + ComplexNode cn = (ComplexNode)qn; + + QueryNode lhs = null; + QueryNode rhs = null; + + if ( ( cn.getLHS() != null ) && ( cn.getLHS().countChildrenWithTerms() > 0 ) ) + lhs = visit(cn.getLHS(), default_namespace, valid_attributes, service_specific_rewrite_rules, p); + + if ( ( cn.getRHS() != null ) && ( cn.getRHS().countChildrenWithTerms() > 0 ) ) + rhs = visit(cn.getRHS(), default_namespace, valid_attributes, service_specific_rewrite_rules, p); + + if ( ( lhs != null ) && ( rhs != null ) ) + return new ComplexNode(lhs, rhs, cn.getOp()); + else if ( lhs != null ) + return lhs; + else + return rhs; + } + else if ( qn instanceof AttrPlusTermNode ) { + AttrPlusTermNode aptn = null; + + if ( ( valid_attributes != null ) && + ( service_specific_rewrite_rules != null ) && + ( valid_attributes.size() > 0 ) ) { + // Use explain mode - valid queries taken from service itself + aptn = rewriteUntilValid((AttrPlusTermNode)qn, valid_attributes, service_specific_rewrite_rules,default_namespace); + } + else { + // Use profile mode - valid queries determined from a pre-arranged profile + aptn = rewriteUntilValid((AttrPlusTermNode)qn,p,default_namespace); + } + + // If we are in strict mode, throw an exception + if ( aptn == null ) + throw new ProfileServiceException("Unable to rewrite node. Semantic action was set to strict, and there appears to be no valid alternatives for node "+qn,3); + + return aptn; + } + else + throw new ProfileServiceException("Should never be here"); + } + + private AttrPlusTermNode rewriteUntilValid(AttrPlusTermNode q, + Map<String,AttributeSetDBO> valid_attributes, + Map<String,AttrValue> service_specific_rewrite_rules, + String default_namespace) + throws org.jzkit.search.util.QueryModel.InvalidQueryException, ProfileServiceException { + + AttrPlusTermNode result = q; + + for ( java.util.Iterator i = q.getAttrIterator(); i.hasNext(); ) { + // 1. extract and rewrite use attribute + String attr_type = (String) i.next(); + AttrValue av = (AttrValue) q.getAttr(attr_type); + log.debug("Rewriting "+attr_type+"="+av); + AttributeSetDBO as = valid_attributes.get(attr_type); + + if ( as == null ) + throw new ProfileServiceException("No "+attr_type+" attr types allowed for target repository",4); + + AttrValue new_av = rewriteUntilValid(av,as.getAttrs(),service_specific_rewrite_rules,default_namespace); + log.debug("Setting attr "+attr_type+" to "+new_av); + q.setAttr(attr_type, new_av); + + } + + log.debug(q.getAttrs()); + + return result; + } + + private AttrValue rewriteUntilValid(AttrValue av, + Set<AttrValue> explain_use_indexes, + Map<String,AttrValue> service_specific_rewrite_rules, + String default_namespace) throws ProfileServiceException { + AttrValue result = av; + + if ( av != null ) { + String av_str_val = av.getWithDefaultNamespace(default_namespace); + if ( explain_use_indexes.contains(av) ) { + log.debug("No need to rewrite, source index "+av+" is already allowed by target"); + } + else { + log.debug("Rewrite, source index "+av+" is disallowed, scanning server alternatives allowed="+explain_use_indexes); + boolean found = false; + for ( java.util.Iterator i = service_specific_rewrite_rules.entrySet().iterator(); ( ( i.hasNext() ) && ( !found ) ); ) { + Map.Entry e = (Map.Entry) i.next(); + if ( e.getKey().equals(av_str_val) ) { + AttrValue new_av = (AttrValue) e.getValue(); + log.debug("Possible rewrite: "+new_av); + if ( explain_use_indexes.contains(new_av) ) { + log.debug("Matched, replacing"); + result = new_av; + found=true; + } + } + } + if ( !found ) { + log.debug("Unable to rewrite query, exception"); + throw new ProfileServiceException("Unable to rewrite access point '"+av_str_val+"' to comply with service explain record",3); + } + } + } + else { + } + + return result; + } + + /** + * Continue to rewrite the source query until one which validates agains the profile is found. + * Returns null if there are no valid expansions. + */ + private AttrPlusTermNode rewriteUntilValid(AttrPlusTermNode q, + ProfileDBO p, + String default_namespace) throws ProfileServiceException { + + log.debug("rewriteUntilValid.... def ns = "+default_namespace); + + QueryVerifyResult qvr = p.validate(q, default_namespace); + // if ( p.isValid(q, default_namespace) ) + AttrPlusTermNode result = null; + + if ( qvr.queryIsValid() ) { + log.debug("Node is conformant to profile.... return it"); + result = q; + } + else { + log.debug("Node does not conform to profile ("+q.getAccessPoint()+" not allowed by profile "+p.getCode()+")"); + // Get failing attr from QVR, generate expansions, rewriteUntilValid each expansion. + // What if failing attr was an AND..?.. Still had to be a component that failed. The Rule that returned false. + String failing_attr_type = qvr.getFailingAttr(); + AttrValue av = (AttrValue) q.getAttr(failing_attr_type); + + if ( av != null ) { + Set<AttrValue> possible_alternatives = lookupKnownAlternatives(av,default_namespace); + if ( possible_alternatives != null ) { + log.debug("Check out alternatives for "+failing_attr_type+":"+possible_alternatives); + for ( Iterator i = possible_alternatives.iterator(); ( ( i.hasNext() ) && ( result == null ) ); ) { + AttrValue target_av = (AttrValue)i.next(); + AttrPlusTermNode new_variant = q.cloneForAttrs(); + new_variant.setAttr(failing_attr_type, target_av); + + result = rewriteUntilValid(new_variant, p, default_namespace); + } + } + else { + log.debug("No expansions available. Return null"); + } + } + else { + log.debug("Hmm.. It appears that we failed because a rule required an attr type which is not present in the query tree("+failing_attr_type+"). Perhaps we should add missing attrs ;)"); + } + } + + return result; + } + + private Set<AttrValue> lookupKnownAlternatives(AttrValue av, String default_namespace) { + Set<AttrValue> result = null; + try { + String namespace = av.getNamespaceIdentifier(); + if ( namespace == null ) + namespace = default_namespace; + + log.debug("Lookup mappings from namespace "+namespace+" attr value = "+av.getValue()); + + CrosswalkDBO cw = configuration.lookupCrosswalk(namespace); + + if ( cw != null ) { + org.jzkit.search.util.Profile.AttrMappingDBO am = cw.lookupMapping(av.getValue().toString()); + if (am != null) { + result = am.getTargetAttrs(); + } + } + else { + log.warn("No crosswalk available for source namespace "+namespace); + } + } + catch ( ConfigurationException ce ) { + log.warn("Problem looking up alternatives for "+av.getValue().toString(),ce); + } + + return result; + } + +} + Index: web/geonetwork/WEB-INF/config-sru.xml =================================================================== --- web/geonetwork/WEB-INF/config-sru.xml (revision 0) +++ web/geonetwork/WEB-INF/config-sru.xml (revision 0) @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> + +<geonet> + <services package="org.wmo.geonet"> + + <!-- Portal SRU search --> + + <service name="portal.sru"> + <class name="org.wmo.geonet.services.main.SRUSearch"> + <param name="maxSummaryKeys" value="10" /> + </class> + <output sheet="portal-srusearch.xsl" contentType="text/xml; charset=UTF-8"/> + + + </service> + + + </services> +</geonet> + Index: web/geonetwork/WEB-INF/config.xml =================================================================== --- web/geonetwork/WEB-INF/config.xml (revision 5690) +++ web/geonetwork/WEB-INF/config.xml (working copy) @@ -72,8 +72,8 @@ <name>main-db</name> <provider>jeeves.resources.dbms.DbmsPool</provider> <config> - <user>zmeJlnzp</user> - <password>f4TcH09S</password> + <user>NZPebKHg</user> + <password>nNvh7ldo</password> <driver>com.mckoi.JDBCDriver</driver> <url>jdbc:mckoi://localhost:9157/</url> <poolSize>10</poolSize> @@ -127,6 +127,7 @@ <param name="thesauriDir" value="WEB-INF/gsthesauri" /> <param name="codeListDir" value="xml/codelist" /> <param name="summaryConfig" value="WEB-INF/config-summary.xml" /> + <param name="jzkitConfig" value="JZkitApplicationContext.xml" /> </appHandler> <!-- ====================================================================== --> @@ -1031,6 +1032,7 @@ <include>config-export.xml</include> <include>config-test.xml</include> <include>config-csw.xml</include> + <include>config-sru.xml</include> <!-- ====================================================================== --> Index: web/geonetwork/WEB-INF/log4j.cfg =================================================================== --- web/geonetwork/WEB-INF/log4j.cfg (revision 5690) +++ web/geonetwork/WEB-INF/log4j.cfg (working copy) @@ -4,19 +4,33 @@ ### GEONETWORK SETTINGS ######################################################## log4j.logger.geonetwork = WARN, jeeves -log4j.logger.geonetwork.search = WARN +log4j.logger.geonetwork.search = DEBUG log4j.logger.geonetwork.editorexpandelement = WARN log4j.logger.geonetwork.editoraddelement = WARN log4j.logger.geonetwork.index = WARN log4j.logger.geonetwork.csw = WARN log4j.logger.geonetwork.mef = WARN -log4j.logger.geonetwork.z3950server = WARN +log4j.logger.geonetwork.z3950server = INFO +log4j.logger.geonetwork.sru = DEBUG +log4j.logger.geonetwork.sru.search = DEBUG + ### JEEVES SETTINGS ############################################################ log4j.logger.jeeves = DEBUG, jeeves log4j.logger.jeeves.dbms = WARN +### SPRING #### + +log4j.logger.org.springframework = WARN, jeeves, console + +### JZKIT SETTINGS #### + +log4j.logger.com.k_int=DEBUG, console, jeeves +log4j.logger.org.jzkit=DEBUG, console, jeeves +log4j.logger.org.jzkit.a2j=WARN, console, jeeves +log4j.logger.org.jzkit.search.impl.LRUCache = INFO, console,jeeves + ### JEEVES APPENDER ############################################################ log4j.appender.jeeves =org.apache.log4j.DailyRollingFileAppender Index: web/geonetwork/WEB-INF/user-profiles.xml =================================================================== --- web/geonetwork/WEB-INF/user-profiles.xml (revision 5690) +++ web/geonetwork/WEB-INF/user-profiles.xml (working copy) @@ -335,6 +335,9 @@ <!-- User self registration --> <allow service="user.register.get"/> <allow service="user.register.submit"/> + + <allow service="portal.sru"/> + </profile> <!-- ====================================================================== --> Index: web/geonetwork/WEB-INF/classes/ConversionRules.properties =================================================================== --- web/geonetwork/WEB-INF/classes/ConversionRules.properties (revision 0) +++ web/geonetwork/WEB-INF/classes/ConversionRules.properties (revision 0) @@ -0,0 +1,12 @@ +geo.1=AccessPoint +geo.2=Relation +geo.3=Position +geo.4=Structure +bib-1.1=AccessPoint +bib-1.2=Relation +bib-1.3=Position +bib-1.4=Structure +bib-1.5=Truncation +bib-1.6=Completeness +gils.1=AccessPoint +gils.10=SpatialQualifier Index: web/geonetwork/WEB-INF/classes/InternalAttrTypes.properties =================================================================== --- web/geonetwork/WEB-INF/classes/InternalAttrTypes.properties (revision 0) +++ web/geonetwork/WEB-INF/classes/InternalAttrTypes.properties (revision 0) @@ -0,0 +1,8 @@ +bib-1.1=AccessPoint +bib-1.2=Relation +bib-1.3=Position +bib-1.4=Structure +bib-1.5=Truncation +bib-1.6=Completeness +gils_attrset.1=AccessPoint +gils_attrset.10=SpatialQualifier Index: web/geonetwork/WEB-INF/classes/InternalToType1Rules.properties =================================================================== --- web/geonetwork/WEB-INF/classes/InternalToType1Rules.properties (revision 0) +++ web/geonetwork/WEB-INF/classes/InternalToType1Rules.properties (revision 0) @@ -0,0 +1,4 @@ +DefaultTargetAttrset=bib-1 +dc.title=bib-1:1:4 +dc.creator=bib-1:1:4 +k-int.anywhere=bib-1:1:1016 Index: web/geonetwork/WEB-INF/classes/JZKitConfig.out.xml =================================================================== Index: web/geonetwork/WEB-INF/classes/JZKitConfig.xml =================================================================== --- web/geonetwork/WEB-INF/classes/JZKitConfig.xml (revision 0) +++ web/geonetwork/WEB-INF/classes/JZKitConfig.xml (revision 0) @@ -0,0 +1,81 @@ +<ServiceDirectory> + + <ApplicationProfiles> + <ClasspathProfile resource="profiles/geo.xml"/> + </ApplicationProfiles> + + <Crosswalks> + <ClasspathCrosswalk resource="crosswalks/QueryModel/gils.xml"/> + <ClasspathCrosswalk resource="crosswalks/QueryModel/dc.xml"/> + <ClasspathCrosswalk resource="crosswalks/QueryModel/rec.xml"/> + <ClasspathCrosswalk resource="crosswalks/QueryModel/geo.xml"/> + <ClasspathCrosswalk resource="crosswalks/QueryModel/cql.xml"/> + </Crosswalks> + + + <!-- IR provider for Geonetwork--> + <Repository className="org.wmo.geonet.provider.GN.GNSearchable" + code="GN" + serviceName="Geonetwork Repository"> + <Preferences> + <Preference name="timeout">10000</Preference> + </Preferences> + + <RecordArchetypes> + <Archetype name="f">xml:f:</Archetype> + </RecordArchetypes> + + + <Collections> + <Collection code="Default" name="Geonetwork" localId="def" profile="geo" /> <!-- for the Z39.50 --> + </Collections> + </Repository> + + + <Repository className="org.jzkit.search.provider.z3950.Z3950Origin" + code="CSIROMAR" + serviceName="Australia - CSIRO Marine and Atmospheric Research"> + <Preferences> + <Preference name="defaultRecordSyntax">xml</Preference> + <Preference name="defaultElementSetName">s</Preference> + <Preference name="host">127.0.0.1</Preference> + <Preference name="port">7002</Preference> + <Preference name="smallSetElementSetName">F</Preference> + <Preference name="charsetEncoding">UTF-8</Preference> + <Preference name="useReferenceId">negotiate</Preference> + </Preferences> + <RecordArchetypes> + <Archetype name="F">xml::F</Archetype> + </RecordArchetypes> + + <Collections> + <Collection code="127.0.0.1:7002/marlin" name="Australia - CSIRO Marine and Atmospheric Research" localId="marlin" /> + </Collections> + + </Repository> + + + <Repository className="org.jzkit.search.provider.z3950.Z3950Origin" + code="NSWRDD" + serviceName="Australia - NSW Natural Resources Data Directory"> + <Preferences> + <Preference name="defaultRecordSyntax">xml</Preference> + <Preference name="defaultElementSetName">s</Preference> + <Preference name="host">127.0.0.1</Preference> + <Preference name="port">7001</Preference> + <Preference name="smallSetElementSetName">F</Preference> + <Preference name="charsetEncoding">UTF-8</Preference> + <Preference name="useReferenceId">negotiate</Preference> + </Preferences> + <RecordArchetypes> + <Archetype name="F">xml::F</Archetype> + </RecordArchetypes> + + <Collections> + <Collection code="127.0.0.1:7001/nrdd" name="Australia - NSW Natural Resources Data Directory" localId="nrdd" /> + </Collections> + + </Repository> + + +</ServiceDirectory> Index: web/geonetwork/WEB-INF/classes/JZkitApplicationContext.xml =================================================================== --- web/geonetwork/WEB-INF/classes/JZkitApplicationContext.xml (revision 0) +++ web/geonetwork/WEB-INF/classes/JZkitApplicationContext.xml (revision 0) @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:context="http://www.springframework.org/schema/context" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans-2.5.xsd + http://www.springframework.org/schema/context + http://www.springframework.org/schema/context/spring-context-2.5.xsd"> + + <context:component-scan base-package="org.jzkit.search.provider"> + <context:include-filter type="regex" expression=".*PluginMetadata.*"/> + </context:component-scan> + <context:component-scan base-package="org.jzkit.search.util.QueryBuilder"/> + <context:component-scan base-package="org.jzkit.search.util.QueryFormatter"/> + <context:component-scan base-package="org.jzkit.search.util.RecordBuilder"/> + + + <!-- ========================= GENERAL DEFINITIONS ========================= --> + + <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> + <property name="basename"><value>messages</value></property> + </bean> + + + <!-- ========================= PERSISTENCE DEFINITIONS ========================= --> + + <!-- ========================= BUSINESS DEFINITIONS ========================= --> + + <bean id="ContextGateway" class="org.wmo.geonet.ContextContainer"> + </bean> + + <bean id="OIDRegister" class="org.jzkit.a2j.codec.util.OIDRegister"> + <constructor-arg index="0"><value>/a2j.properties</value></constructor-arg> + </bean> + + <bean id="Z3950Listener" class="org.jzkit.z3950.server.Z3950Listener"> + <property name="backendBeanName"><value>JZKit2SearchBackend</value></property> + <property name="default"><value>GeonetworkDB</value></property> + </bean> + + <bean id="JZKit2SearchBackend" class="org.jzkit.service.z3950server.JZKitBackend"> + </bean> + + <bean id="JZKitConfig" class="org.jzkit.configuration.provider.xml.XMLImpl" init-method="init"> + <constructor-arg index="0"><value>/JZKitConfig.xml</value></constructor-arg> + </bean> + + <bean id="ProfileService" class="org.wmo.geonet.utils.jzkitextensions.GNProfileService"> + <property name="configuration"><ref bean="JZKitConfig"/></property> + </bean> + + <bean id="TransformationService" class="org.jzkit.search.util.RecordConversion.FragmentTransformerService" init-method="init"> + <constructor-arg index="0"><ref bean="JZKitConfig"/></constructor-arg> + </bean> + + + + <bean id="RecordBuilderService" class="org.jzkit.search.util.RecordBuilder.RecordBuilderService" init-method="init"/> + + <bean id="StatelessQueryService" class="org.jzkit.search.impl.StatelessQueryService" init-method="init"> + <constructor-arg index="0"><value>50</value></constructor-arg> + <constructor-arg index="1"><value>180000</value></constructor-arg> + </bean> + + <bean id="SearchSession" class="org.jzkit.search.impl.SearchSessionImpl" scope="prototype"> + <constructor-arg index="0"><ref bean="ProfileService"/></constructor-arg> + <constructor-arg index="1"><ref bean="TransformationService"/></constructor-arg> + <constructor-arg index="2"><ref bean="RecordBuilderService"/></constructor-arg> + </bean> + + <bean id="SearchSessionFactory" + class="org.wmo.geonet.GNSearchSessionFactory" + init-method="init"> + </bean> + + <!-- conversion rules --> + <bean id="RPNToInternalRules" class="org.jzkit.util.PropsHolder"> + <constructor-arg index="0"><value>/InternalAttrTypes.properties</value></constructor-arg> + </bean> + + <bean id="InternalToType1ConversionRules" class="org.jzkit.z3950.QueryModel.PropsBasedInternalToType1ConversionRules"> + <constructor-arg index="0"><value>/InternalToType1Rules.properties</value></constructor-arg> + </bean> + +</beans> Index: web/geonetwork/WEB-INF/classes/oidreg.default =================================================================== --- web/geonetwork/WEB-INF/classes/oidreg.default (revision 0) +++ web/geonetwork/WEB-INF/classes/oidreg.default (revision 0) @@ -0,0 +1,215 @@ +# Default OID Configuration file +# +# $Id: oidreg.default,v 1.1.1.1 2004/06/18 06:38:12 ibbo Exp $ +# + +# Attribute Set OID's + +# Bib-1 Attribute Set +oid.bib-1={1,2,840,10003,3,1} + +# Explain Attribute Set +oid.exp-1={1,2,840,10003,3,2} + +oid.ext-1={1,2,840,10003,3,3} + +oid.ccl={1,2,840,10003,3,4} + +oid.gils_attrset={1,2,840,10003,3,5} + +oid.stas={1,2,840,10003,3,6} + +oid.collect1={1,2,840,10003,3,7} + +oid.cimi={1,2,840,10003,3,8} + +oid.geo={1,2,840,10003,3,9} + +oid.zbig={1,2,840,10003,3,10} + +oid.util={1,2,840,10003,3,11} + +oid.xd1={1,2,840,10003,3,12} +name.xd1=Cross Domain Attribute Set + +oid.zthes={1,2,840,10003,3,13} +name.zthes=Thesaurus Attribute Set + +oid.holdings={1,2,840,10003,3,16} +name.holdings=Holdings Attribute Set + + +# Record Syntax OID's + +oid.unimarc={1,2,840,10003,5,1} +name.unimarc=UNIMarc Record + +oid.usmarc={1,2,840,10003,5,10} +name.usmarc=US Marc Record + +oid.marc21={1,2,840,10003,5,10} +name.marc21=Marc21 Record + +oid.ukmarc={1,2,840,10003,5,11} +name.ukmark=UK Marc Record + +oid.normarc={1,2,840,10003,5,12} +name.normarc=NorMarc Record + +oid.librismarc={1,2,840,10003,5,13} +name.librismarc=LibrisMarc Record + +oid.danmarc={1,2,840,10003,5,14} + +oid.finmarc={1,2,840,10003,5,15} + +oid.canmarc={1,2,840,10003,5,17} + +oid.ausmarc={1,2,840,10003,5,20} + +oid.ibermarc={1,2,840,10003,5,21} +name.ibermarc=IberMarc Record + +oid.catmarc={1,2,840,10003,5,22} +name.catmarc=CatMarc Record + +oid.explain={1,2,840,10003,5,100} +name.explain=Explain Record +# codec.explain=com.k_int.IR.provider.z3950.gen.RecordSyntax_explain.Explain_Record_codec + +oid.sutrs={1,2,840,10003,5,101} +name.sutrs=Simple Unstructured Text Record +# codec.sutrs=com.k_int.IR.provider.z3950.gen.RecordSyntax_SUTRS.SutrsRecord_codec + +oid.opac={1,2,840,10003,5,102} +name.opac=Opac Record +# codec.opac=com.k_int.IR.provider.z3950.gen.RecordSyntax_opac.OPACRecord_codec + +oid.summary={1,2,840,10003,5,103} +name.summary=Summary Record +# codec.summary=com.k_int.IR.provider.z3950.gen.RecordSyntax_summary.BriefBib_codec + +oid.grs-1={1,2,840,10003,5,105} +name.grs-1=Generic Record +# codec.grs-1=com.k_int.IR.provider.z3950.gen.RecordSyntax_generic.GenericRecord_codec + +oid.pdf={1,2,840,10003,5,109,1} +name.pdf=PDF Document + +oid.postscript={1,2,840,10003,5,109,2} +name.postscript=Postscript Document + +oid.html={1,2,840,10003,5,109,3} +name.html=HTML data + +oid.sgml={1,2,840,10003,5,109,9} +name.sgml=SGML data + +oid.xml={1,2,840,10003,5,109,10} +name.xml=XML data + +# Diagnostic Set OID's + +oid.diag-bib-1={1,2,840,10003,4,1} +name.diag-bib-1=Bib1 Diagnostic Set + +oid.diag-1={1,2,840,10003,4,2} +name.diag-1=Diag1 Diagnostic Set +# codec.diag-1=com.k_int.IR.provider.z3950.gen.DiagnosticFormatDiag1.DiagnosticFormat_codec + +# Schema OID's + +oid.wais_schema={1,2,840,10003,13,1} +name.wais_schema=WAIS Record Schema + +oid.gils_schema={1,2,840,10003,13,2} +name.gils_schema=GILS Record Schema + +oid.collections_schema={1,2,840,10003,13,3} +name.collections_schema=GILS Record Schema + +oid.geo_schema={1,2,840,10003,13,4} +name.geo_schema=GEO Record Schema + +oid.cimi_schema={1,2,840,10003,13,5} +name.cimi_schema=CIMI Record Schema + +oid.update_schema={1,2,840,10003,13,6} +name.update_schema=CIMI Record Schema + +oid.holdings_schema={1,2,840,10003,13,7} +name.holdings_schema=Holdings Record Schema + +oid.zthes_schema={1,2,840,10003,13,8} +name.zthes_schema=Zthes Record Schema + +oid.z_charset_neg_3={1,2,840,10003,15,3} +name.z_charset_neg_3=Z39.50-Character-Set-Negotiation-3 +# codec.z_charset_neg_3=com.k_int.IR.provider.z3950.gen.NegotiationRecordDefinition_charSetandLanguageNegotiation_3.CharSetandLanguageNegotiation_codec + +oid.charset_ucs2={1,0,10646,1,0,2} +name.charset_ucs2=UCS-2 + +oid.charset_ucs4={1,0,10646,1,0,4} +name.charset_ucs4=UCS-4 + +oid.charset_utf16={1,0,10646,1,0,5} +name.charset_utf16=UTF-16 + +oid.charset_utf8={1,0,10646,1,0,8} +name.charset_utf8=UTF-8 + +oid.es_persistent_result_set={1,2,840,10003,9,1} +name.es_persistent_result_set=Persistent Result Set Extended Service + +oid.es_persistent_query={1,2,840,10003,9,2} +name.es_persistent_query=Persistent Query Extended Service + +oid.es_periodic_query_schedule={1,2,840,10003,9,3} +name.es_periodic_query_schedule=Periodic Query Schedule Extended Service + +oid.es_item_order={1,2,840,10003,9,4} +name.es_item_order=Item Order Extended Service + +oid.es_database_update={1,2,840,10003,9,5} +name.es_database_update=Database Update +# codec.es_database_update=com.k_int.IR.provider.z3950.gen.ESFormat_Update0.Update_codec + +oid.es_database_update_r1={1,2,840,10003,9,5,1,1} +name.es_database_update_r1=Database Update Revision 1 +# codec.es_database_update_r1=com.k_int.IR.provider.z3950.gen.ESFormat_Update.Update_codec + +oid.es_export_specification={1,2,840,10003,9,6} +name.es_export_specification=Export Specification + +oid.es_export_invocation={1,2,840,10003,9,6} +name.es_export_invocation=Export Invocation + +# ISO ILL Externals +oid.ILL_OCLC_PRISM_Error={1,0,10161,13,1} +name.ILL_OCLC_PRISM_Error=OCLC Prism Error Extension + +oid.ILL_OCLC_Request={1,0,10161,13,2} +name.ILL_OCLC_Request=OCLC Request Extension + +oid.ILL_APDU_Delivery_Info={1,0,10161,13,3} +name.ILL_APDU_Delivery_Info=ILL APDU Delivery Info +# codec.ILL_APDU_Delivery_Info=com.k_int.OpenRequest.isoill.gen.ILL_APDU_Delivery_Info.APDU_Delivery_Info_codec + +oid.ILL_Supplemental_Client_Info={1,0,10161,13,4} +name.ILL_Supplemental_Client_Info=ILL Supplemental Client Info + +oid.ILL_Forwarded_Additional_Info={1,0,10161,13,5} +name.ILL_Forwarded_Additional_Info=ILL Forwarded Additional Info + +oid.IPIG_Additional_User_Error_Info={1,0,10161,13,6} +name.IPIG_Additional_User_Error_Info=IPIG Additional User Error Information + +oid.ILL_Suppliers_Reference={1,0,10161,13,7} +name.ILL_Suppliers_Reference=ILL Suppliers Reference + +oid.ILL_Internal_Reference_Number={1,0,10161,13,8} +name.ILL_Internal_Reference_Number=ILL Internal Reference Number + +oid.IPIG_ILL_Request_Extension={1,0,10161,13,9} +name.IPIG_ILL_Request_Extension=IPIG ILL Request Extension Index: web/geonetwork/WEB-INF/classes/crosswalks/QueryModel/cql.xml =================================================================== --- web/geonetwork/WEB-INF/classes/crosswalks/QueryModel/cql.xml (revision 0) +++ web/geonetwork/WEB-INF/classes/crosswalks/QueryModel/cql.xml (revision 0) @@ -0,0 +1,75 @@ +<Crosswalk scope="Global" sourceNamespace="cql"> + + <!-- relations --> + + <SourceAttr sourceAttrValue=">="> + <MapsTo targetNamespace="geo">2.4</MapsTo> + </SourceAttr> + + <SourceAttr sourceAttrValue="ge"> + <MapsTo targetNamespace="geo">2.4</MapsTo> + </SourceAttr> + + <SourceAttr sourceAttrValue=">"> + <MapsTo targetNamespace="geo">2.5</MapsTo> + </SourceAttr> + + <SourceAttr sourceAttrValue="<"> + <MapsTo targetNamespace="geo">2.1</MapsTo> + </SourceAttr> + + <SourceAttr sourceAttrValue="<="> + <MapsTo targetNamespace="geo">2.2</MapsTo> + </SourceAttr> + + <SourceAttr sourceAttrValue="le"> + <MapsTo targetNamespace="geo">2.2</MapsTo> + </SourceAttr> + + <SourceAttr sourceAttrValue="="> + <MapsTo targetNamespace="geo">2.3</MapsTo> + </SourceAttr> + + <SourceAttr sourceAttrValue="scr"> + <MapsTo targetNamespace="geo">2.3</MapsTo> + </SourceAttr> + + <SourceAttr sourceAttrValue="exact"> + <MapsTo targetNamespace="geo">2.3</MapsTo> + </SourceAttr> + + <SourceAttr sourceAttrValue="equals"> + <MapsTo targetNamespace="geo">2.3</MapsTo> + </SourceAttr> + + <SourceAttr sourceAttrValue="within"> + <MapsTo targetNamespace="geo">2.7</MapsTo> + </SourceAttr> + + <SourceAttr sourceAttrValue="overlaps"> + <MapsTo targetNamespace="geo">2.7</MapsTo> + </SourceAttr> + + <SourceAttr sourceAttrValue="partial"> + <MapsTo targetNamespace="geo">2.7</MapsTo> + </SourceAttr> + + <SourceAttr sourceAttrValue="within"> + <MapsTo targetNamespace="geo">2.8</MapsTo> + </SourceAttr> + + <SourceAttr sourceAttrValue="encloses"> + <MapsTo targetNamespace="geo">2.9</MapsTo> + </SourceAttr> + + <SourceAttr sourceAttrValue="notWithin"> + <MapsTo targetNamespace="geo">2.10</MapsTo> + </SourceAttr> + + <!-- attributes --> + + <SourceAttr sourceAttrValue="serverChoice"> + <MapsTo targetNamespace="cql">1.1016</MapsTo> + </SourceAttr> + +</Crosswalk> Index: web/geonetwork/WEB-INF/classes/crosswalks/QueryModel/dc.xml =================================================================== --- web/geonetwork/WEB-INF/classes/crosswalks/QueryModel/dc.xml (revision 0) +++ web/geonetwork/WEB-INF/classes/crosswalks/QueryModel/dc.xml (revision 0) @@ -0,0 +1,26 @@ +<Crosswalk scope="Global" sourceNamespace="dc"> + <SourceAttr sourceAttrValue="resourceIdentifier"> + <MapsTo targetNamespace="dc">1.12</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="title"> + <MapsTo targetNamespace="dc">1.4</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="creator"> + <MapsTo targetNamespace="dc">1.1003</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="description"> + <MapsTo targetNamespace="dc">1.62</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="subject"> + <MapsTo targetNamespace="dc">1.29</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="date"> + <MapsTo targetNamespace="dc">1.30</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="resourceType"> + <MapsTo targetNamespace="dc">1.1031</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="format"> + <MapsTo targetNamespace="dc">1.1034</MapsTo> + </SourceAttr> +</Crosswalk> \ No newline at end of file Index: web/geonetwork/WEB-INF/classes/crosswalks/QueryModel/geo.xml =================================================================== --- web/geonetwork/WEB-INF/classes/crosswalks/QueryModel/geo.xml (revision 0) +++ web/geonetwork/WEB-INF/classes/crosswalks/QueryModel/geo.xml (revision 0) @@ -0,0 +1,120 @@ +<Crosswalk scope="Global" sourceNamespace="geo"> + + +<!-- attributes --> + +<SourceAttr sourceAttrValue="any"> + <MapsTo targetNamespace="geo">1.1016</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="anywhere"> + <MapsTo targetNamespace="geo">1.1016</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="serverChoice"> + <MapsTo targetNamespace="geo">1.1016</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="id"> + <MapsTo targetNamespace="geo">1.12</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="identifier"> + <MapsTo targetNamespace="geo">1.12</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="title"> + <MapsTo targetNamespace="geo">1.4</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="author"> + <MapsTo targetNamespace="geo">1.1003</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="creator"> + <MapsTo targetNamespace="geo">1.1003</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="origin"> + <MapsTo targetNamespace="geo">1.1003</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="abstract"> + <MapsTo targetNamespace="geo">1.62</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="description"> + <MapsTo targetNamespace="geo">1.62</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="keywords"> + <MapsTo targetNamespace="geo">1.21</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="subject"> + <MapsTo targetNamespace="geo">1.29</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="themeKeyword"> + <MapsTo targetNamespace="geo">1.2002</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="theme"> + <MapsTo targetNamespace="geo">1.3122</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="creationDate"> + <MapsTo targetNamespace="geo">1.30</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="publicationDate"> + <MapsTo targetNamespace="geo">1.31</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="modificationDate"> + <MapsTo targetNamespace="geo">1.1012</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="begdate"> + <MapsTo targetNamespace="geo">1.2072</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="beginningDate"> + <MapsTo targetNamespace="geo">1.2072</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="enddate"> + <MapsTo targetNamespace="geo">1.2073</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="endingDate"> + <MapsTo targetNamespace="geo">1.2073</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="spatialDomain"> + <MapsTo targetNamespace="geo">1.2059</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="bounds"> + <MapsTo targetNamespace="geo">1.2060</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="placeKeyword"> + <MapsTo targetNamespace="geo">1.2042</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="place"> + <MapsTo targetNamespace="geo">1.2061</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="directSpatialReferenceMethod"> + <MapsTo targetNamespace="geo">1.3302</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="Type"> + <MapsTo targetNamespace="geo">1.1031</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="type"> + <MapsTo targetNamespace="geo">1.1031</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="format"> + <MapsTo targetNamespace="geo">1.1034</MapsTo> + </SourceAttr> + + <!-- structures. Mapping does not yet work --> + + <SourceAttr sourceAttrValue="*"> + <MapsTo targetNamespace="geo">4.1</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="any"> + <MapsTo targetNamespace="geo">4.2</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="exact"> + <MapsTo targetNamespace="geo">4.108</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="coordinate"> + <MapsTo targetNamespace="geo">4.108</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="nwse"> + <MapsTo targetNamespace="geo">4.201</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="date"> + <MapsTo targetNamespace="geo">4.210</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="isodate"> + <MapsTo targetNamespace="geo">4.210</MapsTo> + </SourceAttr> +</Crosswalk> \ No newline at end of file Index: web/geonetwork/WEB-INF/classes/crosswalks/QueryModel/gils.xml =================================================================== --- web/geonetwork/WEB-INF/classes/crosswalks/QueryModel/gils.xml (revision 0) +++ web/geonetwork/WEB-INF/classes/crosswalks/QueryModel/gils.xml (revision 0) @@ -0,0 +1,74 @@ +<Crosswalk scope="Global" sourceNamespace="gils"> + <SourceAttr sourceAttrValue="any"> + <MapsTo targetNamespace="gils">1.1016</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="identifier"> + <MapsTo targetNamespace="gils">1.12</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="recordIdentifier"> + <MapsTo targetNamespace="gils">1.12</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="title"> + <MapsTo targetNamespace="gils">1.4</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="author"> + <MapsTo targetNamespace="gils">1.1003</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="creator"> + <MapsTo targetNamespace="gils">1.1003</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="origin"> + <MapsTo targetNamespace="gils">1.1003</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="abstract"> + <MapsTo targetNamespace="gils">1.62</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="description"> + <MapsTo targetNamespace="gils">1.62</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="keywords"> + <MapsTo targetNamespace="gils">1.21</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="subject"> + <MapsTo targetNamespace="gils">1.29</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="creationDate"> + <MapsTo targetNamespace="gils">1.30</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="publicationDate"> + <MapsTo targetNamespace="gils">1.31</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="modificationDate"> + <MapsTo targetNamespace="gils">1.1012</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="begdate"> + <MapsTo targetNamespace="gils">1.2072</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="beginningDate"> + <MapsTo targetNamespace="gils">1.2072</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="enddate"> + <MapsTo targetNamespace="gils">1.2073</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="endingDate"> + <MapsTo targetNamespace="gils">1.2073</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="spatialDomain"> + <MapsTo targetNamespace="gils">1.2059</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="bounds"> + <MapsTo targetNamespace="gils">1.2060</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="placeKeyword"> + <MapsTo targetNamespace="gils">1.2042</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="place"> + <MapsTo targetNamespace="gils">1.2061</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="type"> + <MapsTo targetNamespace="gils">1.1031</MapsTo> + </SourceAttr> + <SourceAttr sourceAttrValue="format"> + <MapsTo targetNamespace="gils">1.1034</MapsTo> + </SourceAttr> +</Crosswalk> Index: web/geonetwork/WEB-INF/classes/crosswalks/QueryModel/rec.xml =================================================================== --- web/geonetwork/WEB-INF/classes/crosswalks/QueryModel/rec.xml (revision 0) +++ web/geonetwork/WEB-INF/classes/crosswalks/QueryModel/rec.xml (revision 0) @@ -0,0 +1,5 @@ +<Crosswalk scope="Global" sourceNamespace="rec"> + <SourceAttr sourceAttrValue="id"> + <MapsTo targetNamespace="rec">1.12</MapsTo> + </SourceAttr> +</Crosswalk> Index: web/geonetwork/WEB-INF/classes/crosswalks/RecordModel/meta_to_html.xsl =================================================================== --- web/geonetwork/WEB-INF/classes/crosswalks/RecordModel/meta_to_html.xsl (revision 0) +++ web/geonetwork/WEB-INF/classes/crosswalks/RecordModel/meta_to_html.xsl (revision 0) @@ -0,0 +1,23 @@ +<?xml version="1.0"?> + +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> +<xsl:output method="html"/> + +<xsl:template match="/meta"> + <html> + <head> + <title>HTML representation of <xsl:value-of select="title"/> + + + + + + + + +
Title
Creator
Originator
Subject
Coverage
+ + + + + Index: web/geonetwork/WEB-INF/classes/crosswalks/RecordModel/meta_to_sutrs.xsl =================================================================== --- web/geonetwork/WEB-INF/classes/crosswalks/RecordModel/meta_to_sutrs.xsl (revision 0) +++ web/geonetwork/WEB-INF/classes/crosswalks/RecordModel/meta_to_sutrs.xsl (revision 0) @@ -0,0 +1,14 @@ + + + + + + +A Sutrs record for the item with title , the +Coverage attribute is and the creator +is . The item was originated by + and our poor representation of a +subject heading is . + + + Index: web/geonetwork/WEB-INF/classes/crosswalks/RecordModel/meta_to_usmarc.xsl =================================================================== --- web/geonetwork/WEB-INF/classes/crosswalks/RecordModel/meta_to_usmarc.xsl (revision 0) +++ web/geonetwork/WEB-INF/classes/crosswalks/RecordModel/meta_to_usmarc.xsl (revision 0) @@ -0,0 +1,12 @@ + + + + + + + + + + + + Index: web/geonetwork/WEB-INF/classes/crosswalks/RecordModel/meta_to_xml.xsl =================================================================== --- web/geonetwork/WEB-INF/classes/crosswalks/RecordModel/meta_to_xml.xsl (revision 0) +++ web/geonetwork/WEB-INF/classes/crosswalks/RecordModel/meta_to_xml.xsl (revision 0) @@ -0,0 +1,20 @@ + + + + + + + + <xsl:value-of select="title"/> + + + + + + This tag added by the record syntax conversion step + And this + + + + + Index: web/geonetwork/WEB-INF/classes/profiles/bath.xml =================================================================== --- web/geonetwork/WEB-INF/classes/profiles/bath.xml (revision 0) +++ web/geonetwork/WEB-INF/classes/profiles/bath.xml (revision 0) @@ -0,0 +1,10 @@ + + + + bib-1\.1\..* + + + bib-1\.2\..* + + + Index: web/geonetwork/WEB-INF/classes/profiles/geo.xml =================================================================== --- web/geonetwork/WEB-INF/classes/profiles/geo.xml (revision 0) +++ web/geonetwork/WEB-INF/classes/profiles/geo.xml (revision 0) @@ -0,0 +1,49 @@ + + + + .*\.1\.1016 + .*\.1\.4 + .*\.1\.12 + .*\.1\.1003 + .*\.1\.62 + .*\.1\.3102 + .*\.1\.21 + .*\.1\.29 + .*\.1\.2002 + .*\.1\.3112 + .*\.1\.3122 + .*\.1\.30 + .*\.1\.31 + .*\.1\.1012 + .*\.1\.2072 + .*\.1\.2073 + .*\.1\.2059 + .*\.1\.2060 + .*\.1\.2042 + .*\.1\.2061 + .*\.1\.3302 + .*\.1\.1031 + .*\.1\.1034 + + + + .*\.2\.1 + .*\.2\.2 + .*\.2\.3 + .*\.2\.4 + .*\.2\.5 + .*\.2\.6 + .*\.2\.7 + + + + \ No newline at end of file Index: web/geonetwork/WEB-INF/classes/profiles/lom.xml =================================================================== --- web/geonetwork/WEB-INF/classes/profiles/lom.xml (revision 0) +++ web/geonetwork/WEB-INF/classes/profiles/lom.xml (revision 0) @@ -0,0 +1,6 @@ + + + lom\..* + dc\..* + + Index: web/geonetwork/WEB-INF/db/db.conf =================================================================== --- web/geonetwork/WEB-INF/db/db.conf (revision 5690) +++ web/geonetwork/WEB-INF/db/db.conf (working copy) @@ -1,212 +1,212 @@ - -####################################################### -# -# Configuration options for the Mckoi SQL Database. -# -# NOTE: Lines starting with '#' are comments. -# -####################################################### - -# -# database_path - The path where the database data files -# are located. -# See the 'root_path' configuration property for the -# details of how the engine resolves this to an -# absolute path in your file system. - -database_path=./data - -# -# log_path - The path the log files are written. -# See the 'root_path' configuration property for the -# details of how the engine resolves this to an -# absolute path in your file system. -# The log path must point to a writable directory. If -# no log files are to be kept, then comment out (or -# remove) the 'log_path' variable. - -log_path=./log - -# -# root_path - If this is set to 'jvm' then the root -# path of all database files is the root path of the -# JVM (Java virtual machine) running the database -# engine. If this property is set to 'configuration' -# or if it is not present then the root path is the -# path of this configuration file. -# This property is useful if you are deploying a -# database and need this configuration file to be the -# root of the directory tree of the database files. - -root_path=configuration -#root_path=jvm - -# -# jdbc_server_port - The TCP/IP port on this host where -# the database server is mounted. The default port -# of the Mckoi SQL Database server is '9157' - -jdbc_server_port=9157 - -# -# ignore_case_for_identifiers - If enabled all -# identifiers are compared case insensitive. If -# disabled (the default) the case of the identifier -# is important. -# For example, if a table called 'MyTable' contains -# a column called 'my_column' and this property is -# enabled, the identifier 'MYTAble.MY_COlumN' will -# correctly reference the column of the table. If -# this property is disabled a not found error is -# generated. -# This property is intended for compatibility with -# other database managements systems where the case -# of identifiers is not important. - -ignore_case_for_identifiers=enabled - - - - -# ----- PLUG-INS ----- - -# -# function_factories - Registers one or more FunctionFactory -# classes with the database at boot time. A -# FunctionFactory allows user-defined functions to be -# incorporated into the SQL language. Each factory class -# is separated by a semi-colon (;) character. -# -#function_factories=mypackage.MyFunctionFactory - -# -# The Java regular expression library to use. Currently -# the engine supports the Apache Jakarta regular expression -# library, and the GNU LGPL regular expression library. -# These two regular expression libraries can be found at the -# following web sites: -# -# GNU Regexp: http://www.cacas.org/~wes/java/ -# Apache Regexp: http://jakarta.apache.org/regexp/ -# -# The libraries provide similar functionality, however they -# are released under a different license. The GNU library -# is released under the LGPL and is compatible with GPL -# distributions of the database. The Apache Jakarta library -# is released under the Apache Software License and must not -# be linked into GPL distributions. -# -# Use 'regex_library=gnu.regexp' to use the GNU library, or -# 'regex_library=org.apache.regexp' to use the Apache -# library. -# -# NOTE: To use either library, you must include the -# respective .jar package in the Java classpath. - -regex_library=gnu.regexp - - - - -# ----- PERFORMANCE ----- - -# -# data_cache_size - The maximum amount of memory (in bytes) -# to allow the memory cache to grow to. If this is set -# to a value < 4096 then the internal cache is disabled. -# It is recommended that a database server should provide -# a cache of 4 Megabytes (4194304). A stand alone -# database need not have such a large cache. - -data_cache_size=4194304 - -# -# max_cache_entry_size - The maximum size of an element -# in the data cache. This is available for tuning -# reasons and the value here is dependant on the type -# of data being stored. If your data has more larger -# fields that would benefit from being stored in the -# cache then increase this value from its default of -# 8192 (8k). - -max_cache_entry_size=8192 - -# -# max_worker_threads - The maximum number of worker -# threads that can be spawned to handle incoming -# requests. The higher this number, the more -# 'multi-threaded' the database becomes. The -# default setting is '4'. - -maximum_worker_threads=4 - -# -# dont_synch_filesystem - If this is enabled, the engine -# will not synchronize the file handle when a table change -# is committed. This will mean the data is not as -# safe but the 'commit' command will work faster. If this -# is enabled, there is a chance that committed changes will -# not get a chance to flush to the file system if the -# system crashes. -# -# It is recommended this property is left commented out. -# -#dont_synch_filesystem=enabled - -# -# transaction_error_on_dirty_select - If this is disabled -# the 4th conflict (dirty read on modified table) will -# not be detected. This has transactional consequences -# that will cause data modifications to sometimes be -# out of syncronization. For example, one transaction -# adds an entry, and another concurrent transaction -# deletes all entries. If this is disabled this -# conflict will not be detected. The table will end up -# with the one entry added after commit. -# -# It is recommended this property is left commented out. -# -#transaction_error_on_dirty_select=disabled - - - - - - - - -# ----- SPECIAL ----- - -# -# read_only - If this is set to 'enabled' then the database -# is readable and not writable. You may boot a database -# in read only mode from multiple VM's. If the database -# data files are stored on a read only medium such as a -# CD, then the property must be enabled else it will not -# be possible to boot the database. -# ( Uncomment the line below for read only mode ) -#read_only=enabled - - - - -# ----- DEBUGGING ----- - -# -# debug_log_file - The file that is used to log all debug -# information. This file is stored in the 'log_path' -# path. - -debug_log_file=debug.log - -# -# debug_level - The minimum debug level of messages that -# are written to the log file. Reducing this number -# will cause more debug information to be written to -# the log. -# 10 = INFORMATION -# 20 = WARNINGS -# 30 = ALERTS -# 40 = ERRORS - -debug_level=20 + +####################################################### +# +# Configuration options for the Mckoi SQL Database. +# +# NOTE: Lines starting with '#' are comments. +# +####################################################### + +# +# database_path - The path where the database data files +# are located. +# See the 'root_path' configuration property for the +# details of how the engine resolves this to an +# absolute path in your file system. + +database_path=./data + +# +# log_path - The path the log files are written. +# See the 'root_path' configuration property for the +# details of how the engine resolves this to an +# absolute path in your file system. +# The log path must point to a writable directory. If +# no log files are to be kept, then comment out (or +# remove) the 'log_path' variable. + +log_path=./log + +# +# root_path - If this is set to 'jvm' then the root +# path of all database files is the root path of the +# JVM (Java virtual machine) running the database +# engine. If this property is set to 'configuration' +# or if it is not present then the root path is the +# path of this configuration file. +# This property is useful if you are deploying a +# database and need this configuration file to be the +# root of the directory tree of the database files. + +root_path=configuration +#root_path=jvm + +# +# jdbc_server_port - The TCP/IP port on this host where +# the database server is mounted. The default port +# of the Mckoi SQL Database server is '9157' + +jdbc_server_port=9157 + +# +# ignore_case_for_identifiers - If enabled all +# identifiers are compared case insensitive. If +# disabled (the default) the case of the identifier +# is important. +# For example, if a table called 'MyTable' contains +# a column called 'my_column' and this property is +# enabled, the identifier 'MYTAble.MY_COlumN' will +# correctly reference the column of the table. If +# this property is disabled a not found error is +# generated. +# This property is intended for compatibility with +# other database managements systems where the case +# of identifiers is not important. + +ignore_case_for_identifiers=enabled + + + + +# ----- PLUG-INS ----- + +# +# function_factories - Registers one or more FunctionFactory +# classes with the database at boot time. A +# FunctionFactory allows user-defined functions to be +# incorporated into the SQL language. Each factory class +# is separated by a semi-colon (;) character. +# +#function_factories=mypackage.MyFunctionFactory + +# +# The Java regular expression library to use. Currently +# the engine supports the Apache Jakarta regular expression +# library, and the GNU LGPL regular expression library. +# These two regular expression libraries can be found at the +# following web sites: +# +# GNU Regexp: http://www.cacas.org/~wes/java/ +# Apache Regexp: http://jakarta.apache.org/regexp/ +# +# The libraries provide similar functionality, however they +# are released under a different license. The GNU library +# is released under the LGPL and is compatible with GPL +# distributions of the database. The Apache Jakarta library +# is released under the Apache Software License and must not +# be linked into GPL distributions. +# +# Use 'regex_library=gnu.regexp' to use the GNU library, or +# 'regex_library=org.apache.regexp' to use the Apache +# library. +# +# NOTE: To use either library, you must include the +# respective .jar package in the Java classpath. + +regex_library=gnu.regexp + + + + +# ----- PERFORMANCE ----- + +# +# data_cache_size - The maximum amount of memory (in bytes) +# to allow the memory cache to grow to. If this is set +# to a value < 4096 then the internal cache is disabled. +# It is recommended that a database server should provide +# a cache of 4 Megabytes (4194304). A stand alone +# database need not have such a large cache. + +data_cache_size=4194304 + +# +# max_cache_entry_size - The maximum size of an element +# in the data cache. This is available for tuning +# reasons and the value here is dependant on the type +# of data being stored. If your data has more larger +# fields that would benefit from being stored in the +# cache then increase this value from its default of +# 8192 (8k). + +max_cache_entry_size=8192 + +# +# max_worker_threads - The maximum number of worker +# threads that can be spawned to handle incoming +# requests. The higher this number, the more +# 'multi-threaded' the database becomes. The +# default setting is '4'. + +maximum_worker_threads=4 + +# +# dont_synch_filesystem - If this is enabled, the engine +# will not synchronize the file handle when a table change +# is committed. This will mean the data is not as +# safe but the 'commit' command will work faster. If this +# is enabled, there is a chance that committed changes will +# not get a chance to flush to the file system if the +# system crashes. +# +# It is recommended this property is left commented out. +# +#dont_synch_filesystem=enabled + +# +# transaction_error_on_dirty_select - If this is disabled +# the 4th conflict (dirty read on modified table) will +# not be detected. This has transactional consequences +# that will cause data modifications to sometimes be +# out of syncronization. For example, one transaction +# adds an entry, and another concurrent transaction +# deletes all entries. If this is disabled this +# conflict will not be detected. The table will end up +# with the one entry added after commit. +# +# It is recommended this property is left commented out. +# +#transaction_error_on_dirty_select=disabled + + + + + + + + +# ----- SPECIAL ----- + +# +# read_only - If this is set to 'enabled' then the database +# is readable and not writable. You may boot a database +# in read only mode from multiple VM's. If the database +# data files are stored on a read only medium such as a +# CD, then the property must be enabled else it will not +# be possible to boot the database. +# ( Uncomment the line below for read only mode ) +#read_only=enabled + + + + +# ----- DEBUGGING ----- + +# +# debug_log_file - The file that is used to log all debug +# information. This file is stored in the 'log_path' +# path. + +debug_log_file=debug.log + +# +# debug_level - The minimum debug level of messages that +# are written to the log file. Reducing this number +# will cause more debug information to be written to +# the log. +# 10 = INFORMATION +# 20 = WARNINGS +# 30 = ALERTS +# 40 = ERRORS + +debug_level=20 Index: web/geonetwork/xml/search/z3950Server.xsl =================================================================== --- web/geonetwork/xml/search/z3950Server.xsl (revision 5690) +++ web/geonetwork/xml/search/z3950Server.xsl (working copy) @@ -135,7 +135,7 @@ - + @@ -162,6 +162,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -181,7 +251,8 @@ - + + Index: web/geonetwork/xsl/portal-srusearch.xsl =================================================================== --- web/geonetwork/xsl/portal-srusearch.xsl (revision 0) +++ web/geonetwork/xsl/portal-srusearch.xsl (revision 0) @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + + + + + + + + + + XML + + + + + + + + + + 1.1 + + XML + http://explain.z3950.org/dtd/2.1/ + + + + + + + + + + + Geonetwork SRU Interface + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ISO 19139 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +