Index: SRUServer/tags/SRUServer-1.6.0/LICENSE.txt
===================================================================
--- SRUServer/tags/SRUServer-1.6.0/LICENSE.txt	(revision 5925)
+++ SRUServer/tags/SRUServer-1.6.0/LICENSE.txt	(revision 5925)
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
Index: SRUServer/tags/SRUServer-1.6.0/README.txt
===================================================================
--- SRUServer/tags/SRUServer-1.6.0/README.txt	(revision 5925)
+++ SRUServer/tags/SRUServer-1.6.0/README.txt	(revision 5925)
@@ -0,0 +1,56 @@
+ABSTRACT:
+---------
+This package implements the server-side part of the SRU/CQL protocol (SRU/S)
+and conforms to SRU version 1.1 and 1.2. The library will handle most of the
+protocol related tasks for you and you'll only need to implement a few classes
+to connect you search engine. However, the library will not save you from
+doing your SRU/CQL homework (i.e. you'll need to have at least some
+understanding of the protocol and adhere to the protocol semantics).
+Furthermore, you need to have at least some basic understanding of Java web
+application development (Servlets in particular) to use this library.
+
+More Information about SRU/CQL:
+  http://www.loc.gov/standards/sru/
+Forthcoming standardization of SRU/CQL 2.0 (includes description of 1.2)
+  http://www.loc.gov/standards/sru/oasis/  
+
+
+HOW TO USE:
+-----------
+The implementation is designed to make very minimal assumptions about the
+environment it's deployed in. For interfacing with your search engine, you
+need to implement the SRUSearchEngine interface. At minimum, you'll need
+to implement at least the search() method. Please check the Java API
+documentation for further details about this interface.
+The SRUServer implements the SRU protocol an used your supplied search engine
+implementation to talk to your search engine. The SRUServer is configured
+using a SRUServerConfig instance. The SRUServerConfig reads an XML document,
+which contains the (static) server configuration. It must conform to the
+"sru-server-config.xsd" schema in the "src/main/resources/META-INF" directory.
+You can either write your own Servlet implementation to drive the SRUServer or
+can use supplied SRUServerServlet from the "de.clarin.sru.server.utils"
+package. If you do so, your search engine needs to inherit from the abstract
+class SRUSearchEngineBase. Check the Java API documentation (and the code) of
+these classes for more information.
+Of course, you can use frameworks like Spring or similar to assemble your
+web application.
+An usage example for this library is available from:
+  http://clarin.ids-mannheim.de/downloads/clarin/
+NB: you cannot deploy and use it, because it uses internal IDS services, but
+it should give you a basic idea how to use the library. 
+
+
+HOW TO BUILD:
+-------------
+1. Build the library with the following command:
+$ mvn package
+
+2a. Either install the artifact in your own repository with the following
+    command, if you use Maven for you project:
+$ mvn install
+
+2b. Or use the "sru-server-$VERSION.jar" file created within the
+    "target/" directory, if you do not use Maven for your project.
+
+The Java API documentation is created within the "target/apidocs" directory
+and is also zipped up into the "sru-server-$VERSION-javadoc.jar" archive.
Index: SRUServer/tags/SRUServer-1.6.0/pom.xml
===================================================================
--- SRUServer/tags/SRUServer-1.6.0/pom.xml	(revision 5925)
+++ SRUServer/tags/SRUServer-1.6.0/pom.xml	(revision 5925)
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>eu.clarin.sru</groupId>
+    <artifactId>sru-server</artifactId>
+    <version>1.6.0</version>
+    <packaging>jar</packaging>
+    <name>SRU/CQL Server</name>
+    <description>A SRU/CQL server implementation conforming to SRU/CQL protocol version 1.1 and 1.2</description>
+
+    <properties>
+        <!-- project settings -->
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <!-- plug-in settings -->
+        <maven.compiler.source>1.6</maven.compiler.source>
+        <maven.compiler.target>1.6</maven.compiler.target>
+        <!-- dependency versions -->
+        <slf4j.version>1.6.4</slf4j.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>${slf4j.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.z3950.zing</groupId>
+            <artifactId>cql-java</artifactId>
+            <version>1.11</version>
+        </dependency>
+
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>2.5</version>
+            <type>jar</type>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <repositories>
+        <repository>
+            <id>indexdata</id>
+            <url>http://maven.indexdata.com/</url>
+            <layout>default</layout>
+        </repository>
+    </repositories>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>2.1</version>
+                <configuration>
+                    <source>${maven.compiler.source}</source>
+                    <target>${maven.compiler.target}</target>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <version>2.8.1</version>
+                <executions>
+                    <execution>
+                        <id>attach-javadoc</id>
+                        <goals>
+                            <goal>jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-source-plugin</artifactId>
+                <version>2.2</version>
+                <executions>
+                    <execution>
+                        <id>attach-source</id>
+                        <goals>
+                            <goal>jar-no-fork</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
Index: SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUAbstractResult.java
===================================================================
--- SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUAbstractResult.java	(revision 5925)
+++ SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUAbstractResult.java	(revision 5925)
@@ -0,0 +1,121 @@
+/**
+ * This software is copyright (c) 2011-2013 by
+ *  - Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ * This is free software. You can redistribute it
+ * and/or modify it under the terms described in
+ * the GNU General Public License v3 of which you
+ * should have received a copy. Otherwise you can download
+ * it from
+ *
+ *   http://www.gnu.org/licenses/gpl-3.0.txt
+ *
+ * @copyright Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ *
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt
+ *  GNU General Public License v3
+ */
+package eu.clarin.sru.server;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+
+/**
+ * Base class for SRU responses.
+ */
+abstract class SRUAbstractResult {
+    private final SRUDiagnosticList diagnostics;
+
+
+    SRUAbstractResult(SRUDiagnosticList diagnosticList) {
+        if (diagnosticList == null) {
+            throw new NullPointerException("Implementation error: "
+                    + "diagnosticList must not be null!");
+        }
+        this.diagnostics = diagnosticList;
+    }
+
+
+    /**
+     * Add a non surrogate diagnostic to the response.
+     *
+     * @param uri
+     *            the diagnostic's identifying URI
+     * @param details
+     *            supplementary information available, often in a format
+     *            specified by the diagnostic or <code>null</code>
+     * @param message
+     *            human readable message to display to the end user or
+     *            <code>null</code>
+     */
+    protected final void addDiagnostic(String uri, String details, String message) {
+        diagnostics.addDiagnostic(uri, details, message);
+    }
+
+
+    /**
+     * Add a non surrogate diagnostic to the response.
+     *
+     * @param uri
+     *            the diagnostic's identifying URI
+     * @param details
+     *            supplementary information available, often in a format
+     *            specified by the diagnostic or <code>null</code>
+     */
+    protected final void addDiagnostic(String uri, String details) {
+        addDiagnostic(uri, details, null);
+    }
+
+
+    /**
+     * Add a non surrogate diagnostic to the response.
+     *
+     * @param uri
+     *            the diagnostic's identifying URI
+     */
+    protected final void addDiagnostic(String uri) {
+        addDiagnostic(uri, null, null);
+    }
+
+
+    /**
+     * Check, if extra response data should be serialized for this request.
+     * Default implementation is provided for convince and always returns
+     * <code>false</code>.
+     *
+     * @return <code>true</code> if extra response data should be serialized.
+     * @see #writeExtraResponseData(XMLStreamWriter)
+     */
+    public boolean hasExtraResponseData() {
+        return false;
+    }
+
+
+    /**
+     * Serialize extra response data for this request.
+     *
+     * @param writer
+     *            the {@link XMLStreamException} instance to be used
+     * @throws XMLStreamException
+     *             an error occurred while serializing the result
+     * @see #hasExtraResponseData()
+     */
+    public void writeExtraResponseData(XMLStreamWriter writer)
+            throws XMLStreamException {
+    }
+
+
+    /**
+     * Release this result and free any associated resources.
+     * <p>
+     * This method <strong>must not</strong> throw any exceptions
+     * </p>
+     * <p>
+     * Calling the method <code>close</code> on a result object that is already
+     * closed is a no-op.
+     * </p>
+     */
+    public void close() {
+    }
+
+} // abstract class SRUAbstractResult
Index: SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUConfigException.java
===================================================================
--- SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUConfigException.java	(revision 5925)
+++ SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUConfigException.java	(revision 5925)
@@ -0,0 +1,48 @@
+/**
+ * This software is copyright (c) 2011-2013 by
+ *  - Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ * This is free software. You can redistribute it
+ * and/or modify it under the terms described in
+ * the GNU General Public License v3 of which you
+ * should have received a copy. Otherwise you can download
+ * it from
+ *
+ *   http://www.gnu.org/licenses/gpl-3.0.txt
+ *
+ * @copyright Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ *
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt
+ *  GNU General Public License v3
+ */
+package eu.clarin.sru.server;
+
+/**
+ * An exception raised, if some error occurred with the SRUServer configuration.
+ */
+@SuppressWarnings("serial")
+public class SRUConfigException extends Exception {
+
+    /**
+     * Constructor.
+     *
+     * @param msg
+     *            a message
+     */
+    public SRUConfigException(String msg) {
+        super(msg);
+    }
+
+
+    /**
+     * Constructor.
+     *
+     * @param msg
+     *            a message
+     * @param cause
+     *            the cause of the error
+     */
+    public SRUConfigException(String msg, Throwable cause) {
+        super(msg, cause);
+    }
+
+} // class SRUConfigException
Index: SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUConstants.java
===================================================================
--- SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUConstants.java	(revision 5925)
+++ SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUConstants.java	(revision 5925)
@@ -0,0 +1,212 @@
+/**
+ * This software is copyright (c) 2011-2013 by
+ *  - Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ * This is free software. You can redistribute it
+ * and/or modify it under the terms described in
+ * the GNU General Public License v3 of which you
+ * should have received a copy. Otherwise you can download
+ * it from
+ *
+ *   http://www.gnu.org/licenses/gpl-3.0.txt
+ *
+ * @copyright Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ *
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt
+ *  GNU General Public License v3
+ */
+package eu.clarin.sru.server;
+
+/**
+ * Constants for SRU diagnostics.
+ *
+ * @see <a href="http://www.loc.gov/standards/sru/specs/diagnostics.html"> SRU
+ *      Diagnostics</a>
+ * @see <a
+ *      href="http://www.loc.gov/standards/sru/resources/diagnostics-list.html">
+ *      SRU Diagnostics List</a>
+ */
+public final class SRUConstants {
+    // private constants
+    private static final String SRU_DIAGNOSTIC_URI_PREFIX =
+            "info:srw/diagnostic/1/";
+
+    // general diagnostics
+    public static final String SRU_GENERAL_SYSTEM_ERROR =
+            SRU_DIAGNOSTIC_URI_PREFIX + 1;
+    public static final String SRU_SYSTEM_TEMPORARILY_UNAVAILABLE =
+            SRU_DIAGNOSTIC_URI_PREFIX + 2;
+    public static final String SRU_AUTHENTICATION_ERROR =
+            SRU_DIAGNOSTIC_URI_PREFIX + 3;
+    public static final String SRU_UNSUPPORTED_OPERATION =
+            SRU_DIAGNOSTIC_URI_PREFIX + 4;
+    public static final String SRU_UNSUPPORTED_VERSION =
+            SRU_DIAGNOSTIC_URI_PREFIX + 5;
+    public static final String SRU_UNSUPPORTED_PARAMETER_VALUE =
+            SRU_DIAGNOSTIC_URI_PREFIX + 6;
+    public static final String SRU_MANDATORY_PARAMETER_NOT_SUPPLIED =
+            SRU_DIAGNOSTIC_URI_PREFIX + 7;
+    public static final String SRU_UNSUPPORTED_PARAMETER =
+            SRU_DIAGNOSTIC_URI_PREFIX + 8;
+    // diagnostics relating to CQL
+    public static final String SRU_QUERY_SYNTAX_ERROR =
+            SRU_DIAGNOSTIC_URI_PREFIX + 10;
+    public static final String SRU_TOO_MANY_CHARACTERS_IN_QUERY =
+            SRU_DIAGNOSTIC_URI_PREFIX + 12;
+    public static final String SRU_INVALID_OR_UNSUPPORTED_USE_OF_PARENTHESES =
+            SRU_DIAGNOSTIC_URI_PREFIX + 13;
+    public static final String SRU_INVALID_OR_UNSUPPORTED_USE_OF_QUOTES =
+            SRU_DIAGNOSTIC_URI_PREFIX + 14;
+    public static final String SRU_UNSUPPORTED_CONTEXT_SET =
+            SRU_DIAGNOSTIC_URI_PREFIX + 15;
+    public static final String SRU_UNSUPPORTED_INDEX =
+            SRU_DIAGNOSTIC_URI_PREFIX + 16;
+    public static final String SRU_UNSUPPORTED_COMBINATION_OF_INDEXES =
+            SRU_DIAGNOSTIC_URI_PREFIX + 18;
+    public static final String SRU_UNSUPPORTED_RELATION =
+            SRU_DIAGNOSTIC_URI_PREFIX + 19;
+    public static final String SRU_UNSUPPORTED_RELATION_MODIFIER =
+            SRU_DIAGNOSTIC_URI_PREFIX + 20;
+    public static final String SRU_UNSUPPORTED_COMBINATION_OF_RELATION_MODIFERS =
+            SRU_DIAGNOSTIC_URI_PREFIX + 21;
+    public static final String SRU_UNSUPPORTED_COMBINATION_OF_RELATION_AND_INDEX =
+            SRU_DIAGNOSTIC_URI_PREFIX + 22;
+    public static final String SRU_TOO_MANY_CHARACTERS_IN_TERM =
+            SRU_DIAGNOSTIC_URI_PREFIX + 23;
+    public static final String SRU_UNSUPPORTED_COMBINATION_OF_RELATION_AND_TERM =
+            SRU_DIAGNOSTIC_URI_PREFIX + 24;
+    public static final String SRU_NON_SPECIAL_CHARACTER_ESCAPED_IN_TERM =
+            SRU_DIAGNOSTIC_URI_PREFIX + 26;
+    public static final String SRU_EMPTY_TERM_UNSUPPORTED =
+            SRU_DIAGNOSTIC_URI_PREFIX + 27;
+    public static final String SRU_MASKING_CHARACTER_NOT_SUPPORTED =
+            SRU_DIAGNOSTIC_URI_PREFIX + 28;
+    public static final String SRU_MASKED_WORDS_TOO_SHORT =
+            SRU_DIAGNOSTIC_URI_PREFIX + 29;
+    public static final String SRU_TOO_MANY_MASKING_CHARACTERS_IN_TERM =
+            SRU_DIAGNOSTIC_URI_PREFIX + 30;
+    public static final String SRU_ANCHORING_CHARACTER_NOT_SUPPORTED =
+            SRU_DIAGNOSTIC_URI_PREFIX + 31;
+    public static final String SRU_ANCHORING_CHARACTER_IN_UNSUPPORTED_POSITION =
+            SRU_DIAGNOSTIC_URI_PREFIX + 32;
+    public static final String SRU_COMBINATION_OF_PROXIMITY_ADJACENCY_AND_MASKING_CHARACTERS_NOT_SUPPORTED =
+            SRU_DIAGNOSTIC_URI_PREFIX + 33;
+    public static final String SRU_COMBINATION_OF_PROXIMITY_ADJACENCY_AND_ANCHORING_CHARACTERS_NOT_SUPPORTED =
+            SRU_DIAGNOSTIC_URI_PREFIX + 34;
+    public static final String SRU_TERM_CONTAINS_ONLY_STOPWORDS =
+            SRU_DIAGNOSTIC_URI_PREFIX + 35;
+    public static final String SRU_TERM_IN_INVALID_FORMAT_FOR_INDEX_OR_RELATION =
+            SRU_DIAGNOSTIC_URI_PREFIX + 36;
+    public static final String SRU_UNSUPPORTED_BOOLEAN_OPERATOR =
+            SRU_DIAGNOSTIC_URI_PREFIX + 37;
+    public static final String SRU_TOO_MANY_BOOLEAN_OPERATORS_IN_QUERY =
+            SRU_DIAGNOSTIC_URI_PREFIX + 38;
+    public static final String SRU_PROXIMITY_NOT_SUPPORTED =
+            SRU_DIAGNOSTIC_URI_PREFIX + 39;
+    public static final String SRU_UNSUPPORTED_PROXIMITY_RELATION =
+            SRU_DIAGNOSTIC_URI_PREFIX + 40;
+    public static final String SRU_UNSUPPORTED_PROXIMITY_DISTANCE =
+            SRU_DIAGNOSTIC_URI_PREFIX + 41;
+    public static final String SRU_UNSUPPORTED_PROXIMITY_UNIT =
+            SRU_DIAGNOSTIC_URI_PREFIX + 42;
+    public static final String SRU_UNSUPPORTED_PROXIMITY_ORDERING =
+            SRU_DIAGNOSTIC_URI_PREFIX + 43;
+    public static final String SRU_UNSUPPORTED_COMBINATION_OF_PROXIMITY_MODIFIERS =
+            SRU_DIAGNOSTIC_URI_PREFIX + 44;
+    public static final String SRU_UNSUPPORTED_BOOLEAN_MODIFIER =
+            SRU_DIAGNOSTIC_URI_PREFIX + 46;
+    public static final String SRU_CANNOT_PROCESS_QUERY_REASON_UNKNOWN =
+            SRU_DIAGNOSTIC_URI_PREFIX + 47;
+    public static final String SRU_QUERY_FEATURE_UNSUPPORTED =
+            SRU_DIAGNOSTIC_URI_PREFIX + 48;
+    public static final String SRU_MASKING_CHARACTER_IN_UNSUPPORTED_POSITION =
+            SRU_DIAGNOSTIC_URI_PREFIX + 49;
+    // diagnostics relating to result sets
+    public static final String SRU_RESULT_SETS_NOT_SUPPORTED =
+            SRU_DIAGNOSTIC_URI_PREFIX + 50;
+    public static final String SRU_RESULT_SET_DOES_NOT_EXIST =
+            SRU_DIAGNOSTIC_URI_PREFIX + 51;
+    public static final String SRU_RESULT_SET_TEMPORARILY_UNAVAILABLE =
+            SRU_DIAGNOSTIC_URI_PREFIX + 52;
+    public static final String SRU_RESULT_SETS_ONLY_SUPPORTED_FOR_RETRIEVAL =
+            SRU_DIAGNOSTIC_URI_PREFIX + 53;
+    public static final String SRU_COMBINATION_OF_RESULT_SETS_WITH_SEARCH_TERMS_NOT_SUPPORTED =
+            SRU_DIAGNOSTIC_URI_PREFIX + 55;
+    public static final String SRU_RESULT_SET_CREATED_WITH_UNPREDICTABLE_PARTIAL_RESULTS_AVAILABLE =
+            SRU_DIAGNOSTIC_URI_PREFIX + 58;
+    public static final String SRU_RESULT_SET_CREATED_WITH_VALID_PARTIAL_RESULTS_AVAILABLE =
+            SRU_DIAGNOSTIC_URI_PREFIX + 59;
+    public static final String SRU_RESULT_SET_NOT_CREATED_TOO_MANY_MATCHING_RECORDS =
+            SRU_DIAGNOSTIC_URI_PREFIX + 60;
+    // diagnostics relating to records
+    public static final String SRU_FIRST_RECORD_POSITION_OUT_OF_RANGE =
+            SRU_DIAGNOSTIC_URI_PREFIX + 61;
+    public static final String SRU_RECORD_TEMPORARILY_UNAVAILABLE =
+            SRU_DIAGNOSTIC_URI_PREFIX + 64;
+    public static final String SRU_RECORD_DOES_NOT_EXIST =
+            SRU_DIAGNOSTIC_URI_PREFIX + 65;
+    public static final String SRU_UNKNOWN_SCHEMA_FOR_RETRIEVAL =
+            SRU_DIAGNOSTIC_URI_PREFIX + 66;
+    public static final String SRU_RECORD_NOT_AVAILABLE_IN_THIS_SCHEMA =
+            SRU_DIAGNOSTIC_URI_PREFIX + 67;
+    public static final String SRU_NOT_AUTHORISED_TO_SEND_RECORD =
+            SRU_DIAGNOSTIC_URI_PREFIX + 68;
+    public static final String SRU_NOT_AUTHORISED_TO_SEND_RECORD_IN_THIS_SCHEMA =
+            SRU_DIAGNOSTIC_URI_PREFIX + 69;
+    public static final String SRU_RECORD_TOO_LARGE_TO_SEND =
+            SRU_DIAGNOSTIC_URI_PREFIX + 70;
+    public static final String SRU_UNSUPPORTED_RECORD_PACKING =
+            SRU_DIAGNOSTIC_URI_PREFIX + 71;
+    public static final String SRU_XPATH_RETRIEVAL_UNSUPPORTED =
+            SRU_DIAGNOSTIC_URI_PREFIX + 72;
+    public static final String SRU_XPATH_EXPRESSION_CONTAINS_UNSUPPORTED_FEATURE =
+            SRU_DIAGNOSTIC_URI_PREFIX + 73;
+    public static final String SRU_UNABLE_TO_EVALUATE_XPATH_EXPRESSION =
+            SRU_DIAGNOSTIC_URI_PREFIX + 74;
+    // diagnostics relating to sorting
+    public static final String SRU_SORT_NOT_SUPPORTED =
+            SRU_DIAGNOSTIC_URI_PREFIX + 80;
+    public static final String SRU_UNSUPPORTED_SORT_SEQUENCE =
+            SRU_DIAGNOSTIC_URI_PREFIX + 82;
+    public static final String SRU_TOO_MANY_RECORDS_TO_SORT =
+            SRU_DIAGNOSTIC_URI_PREFIX + 83;
+    public static final String SRU_TOO_MANY_SORT_KEYS_TO_SORT =
+            SRU_DIAGNOSTIC_URI_PREFIX + 84;
+    public static final String SRU_CANNOT_SORT_INCOMPATIBLE_RECORD_FORMATS =
+            SRU_DIAGNOSTIC_URI_PREFIX + 86;
+    public static final String SRU_UNSUPPORTED_SCHEMA_FOR_SORT =
+            SRU_DIAGNOSTIC_URI_PREFIX + 87;
+    public static final String SRU_UNSUPPORTED_PATH_FOR_SORT =
+            SRU_DIAGNOSTIC_URI_PREFIX + 88;
+    public static final String SRU_PATH_UNSUPPORTED_FOR_SCHEMA =
+            SRU_DIAGNOSTIC_URI_PREFIX + 89;
+    public static final String SRU_UNSUPPORTED_DIRECTION =
+            SRU_DIAGNOSTIC_URI_PREFIX + 90;
+    public static final String SRU_UNSUPPORTED_CASE =
+            SRU_DIAGNOSTIC_URI_PREFIX + 91;
+    public static final String SRU_UNSUPPORTED_MISSING_VALUE_ACTION =
+            SRU_DIAGNOSTIC_URI_PREFIX + 92;
+    public static final String SRU_SORT_ENDED_DUE_TO_MISSING_VALUE =
+            SRU_DIAGNOSTIC_URI_PREFIX + 93;
+    public static final String SRU_SORT_SPEC_INCLUDED_BOTH_IN_QUERY_AND_PROTOCOL_QUERY_PREVAILS =
+            SRU_DIAGNOSTIC_URI_PREFIX + 94;
+    public static final String SRU_SORT_SPEC_INCLUDED_BOTH_IN_QUERY_AND_PROTOCOL_PROTOCOL_PREVAILS =
+            SRU_DIAGNOSTIC_URI_PREFIX + 95;
+    public static final String SRU_SORT_SPEC_INCLUDED_BOTH_IN_QUERY_AND_PROTOCOL_ERROR =
+            SRU_DIAGNOSTIC_URI_PREFIX + 96;
+    // diagnostics relating to stylesheets
+    public static final String SRU_STYLESHEETS_NOT_SUPPORTED =
+            SRU_DIAGNOSTIC_URI_PREFIX + 110;
+    public static final String SRU_UNSUPPORTED_STYLESHEET =
+            SRU_DIAGNOSTIC_URI_PREFIX + 111;
+    // diagnostics relating to scan
+    public static final String SRU_RESPONSE_POSITION_OUT_OF_RANGE =
+            SRU_DIAGNOSTIC_URI_PREFIX + 120;
+    public static final String SRU_TOO_MANY_TERMS_REQUESTED =
+            SRU_DIAGNOSTIC_URI_PREFIX + 121;
+
+
+    /* hide constructor */
+    private SRUConstants() {
+    }
+
+} // interface SRUConstants
Index: SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUDiagnostic.java
===================================================================
--- SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUDiagnostic.java	(revision 5925)
+++ SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUDiagnostic.java	(revision 5925)
@@ -0,0 +1,310 @@
+/**
+ * This software is copyright (c) 2011-2013 by
+ *  - Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ * This is free software. You can redistribute it
+ * and/or modify it under the terms described in
+ * the GNU General Public License v3 of which you
+ * should have received a copy. Otherwise you can download
+ * it from
+ *
+ *   http://www.gnu.org/licenses/gpl-3.0.txt
+ *
+ * @copyright Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ *
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt
+ *  GNU General Public License v3
+ */
+package eu.clarin.sru.server;
+
+/**
+ * Class to hold a SRU diagnostic.
+ *
+ * @see SRUConstants
+ * @see SRUDiagnosticList
+ * @see <a href="http://www.loc.gov/standards/sru/specs/diagnostics.html">SRU
+ *      Diagnostics</a>
+ * @see <a href="http://www.loc.gov/standards/sru/resources/diagnostics-list.html">SRU
+ *      Diagnostics List</a>
+ */
+public final class SRUDiagnostic {
+    private final String uri;
+    private final String details;
+    private final String message;
+
+
+    /**
+     * Constructor.
+     *
+     * @param uri
+     *            the diagnostic's identifying URI
+     * @param details
+     *            supplementary information available, often in a format
+     *            specified by the diagnostic or <code>null</code>
+     * @param message
+     *            human readable message to display to the end user or
+     *            <code>null</code>
+     * @throws NullPointerException
+     *             if uri is <code>null</code>
+     * @throws IllegalArgumentException
+     *             if uri is empty string
+     */
+    public SRUDiagnostic(String uri, String details, String message) {
+        if (uri == null) {
+            throw new NullPointerException("uri == null");
+        }
+        uri = uri.trim();
+        if (uri.isEmpty()) {
+            throw new IllegalArgumentException("uri is empty");
+        }
+        this.uri     = uri;
+        if (details != null) {
+            details = details.trim();
+            if (details.isEmpty()) {
+                details = null;
+            }
+        }
+        this.details = details;
+        if (message != null) {
+            message = message.trim();
+            if (message.isEmpty()) {
+                message = null;
+            }
+        }
+        this.message = message;
+    }
+
+
+    /**
+     * Constructor.
+     *
+     * @param uri
+     *            the diagnostic's identifying URI
+     * @param details
+     *            supplementary information available, often in a format
+     *            specified by the diagnostic or <code>null</code>
+     */
+    public SRUDiagnostic(String uri, String details) {
+        this(uri, details, null);
+    }
+
+
+    /**
+     * Constructor.
+     *
+     * @param uri
+     *            the diagnostic's identifying URI
+     */
+    public SRUDiagnostic(String uri) {
+        this(uri, null, null);
+    }
+
+
+    /**
+     * Get code for this diagnostic.
+     *
+     * @return diagnostic code
+     * @see SRUConstants
+     */
+    public String getURI() {
+        return uri;
+    }
+
+
+    /**
+     * Get supplementary information for this diagnostic. The format for this
+     * value is often specified by the diagnostic code.
+     *
+     * @return supplementary information
+     */
+    public String getDetails() {
+        return details;
+    }
+
+
+    /**
+     * Get human readable message.
+     *
+     * @return human readable message
+     */
+    public String getMessage() {
+        return message != null ? message : getDefaultErrorMessage(uri);
+    }
+
+
+    private static String getDefaultErrorMessage(final String uri) {
+        /*
+         * Ugly, but can't do better unless we drop JDK 1.6 compatibility.
+         * (JDK 1.7+ has switch on String)
+         */
+        if (uri.equals(SRUConstants.SRU_GENERAL_SYSTEM_ERROR)) {
+            return "General system error";
+        } else if (uri.equals(SRUConstants.SRU_SYSTEM_TEMPORARILY_UNAVAILABLE)) {
+            return "System temporarily unavailable";
+        } else if (uri.equals(SRUConstants.SRU_AUTHENTICATION_ERROR)) {
+            return "Authentication error";
+        } else if (uri.equals(SRUConstants.SRU_UNSUPPORTED_OPERATION)) {
+            return "Unsupported operation";
+        } else if (uri.equals(SRUConstants.SRU_UNSUPPORTED_VERSION)) {
+            return "Unsupported version";
+        } else if (uri.equals(SRUConstants.SRU_UNSUPPORTED_PARAMETER_VALUE)) {
+            return "Unsupported parameter value";
+        } else if (uri.equals(SRUConstants.SRU_MANDATORY_PARAMETER_NOT_SUPPLIED)) {
+            return "Mandatory parameter not supplied";
+        } else if (uri.equals(SRUConstants.SRU_UNSUPPORTED_PARAMETER)) {
+            return "Unsupported Parameter";
+        } else if (uri.equals(SRUConstants.SRU_QUERY_SYNTAX_ERROR)) {
+            return "Query syntax error";
+        } else if (uri.equals(SRUConstants.SRU_TOO_MANY_CHARACTERS_IN_QUERY)) {
+            return "Too many characters in query";
+        } else if (uri.equals(SRUConstants.SRU_INVALID_OR_UNSUPPORTED_USE_OF_PARENTHESES)) {
+            return "Invalid or unsupported use of parentheses";
+        } else if (uri.equals(SRUConstants.SRU_INVALID_OR_UNSUPPORTED_USE_OF_QUOTES)) {
+            return "Invalid or unsupported use of quotes";
+        } else if (uri.equals(SRUConstants.SRU_UNSUPPORTED_CONTEXT_SET)) {
+            return "Unsupported context set";
+        } else if (uri.equals(SRUConstants.SRU_UNSUPPORTED_INDEX)) {
+            return "Unsupported index";
+        } else if (uri.equals(SRUConstants.SRU_UNSUPPORTED_COMBINATION_OF_INDEXES)) {
+            return "Unsupported combination of indexes";
+        } else if (uri.equals(SRUConstants.SRU_UNSUPPORTED_RELATION)) {
+            return "Unsupported relation";
+        } else if (uri.equals(SRUConstants.SRU_UNSUPPORTED_RELATION_MODIFIER)) {
+            return "Unsupported relation modifier";
+        } else if (uri.equals(SRUConstants.SRU_UNSUPPORTED_COMBINATION_OF_RELATION_MODIFERS)) {
+            return "Unsupported combination of relation modifers";
+        } else if (uri.equals(SRUConstants.SRU_UNSUPPORTED_COMBINATION_OF_RELATION_AND_INDEX)) {
+            return "Unsupported combination of relation and index";
+        } else if (uri.equals(SRUConstants.SRU_TOO_MANY_CHARACTERS_IN_TERM)) {
+            return "Too many characters in term";
+        } else if (uri.equals(SRUConstants.SRU_UNSUPPORTED_COMBINATION_OF_RELATION_AND_TERM)) {
+            return "Unsupported combination of relation and term";
+        } else if (uri.equals(SRUConstants.SRU_NON_SPECIAL_CHARACTER_ESCAPED_IN_TERM)) {
+            return "Non special character escaped in term";
+        } else if (uri.equals(SRUConstants.SRU_EMPTY_TERM_UNSUPPORTED)) {
+            return "Empty term unsupported";
+        } else if (uri.equals(SRUConstants.SRU_MASKING_CHARACTER_NOT_SUPPORTED)) {
+            return "Masking character not supported";
+        } else if (uri.equals(SRUConstants.SRU_MASKED_WORDS_TOO_SHORT)) {
+            return "Masked words too short";
+        } else if (uri.equals(SRUConstants.SRU_TOO_MANY_MASKING_CHARACTERS_IN_TERM)) {
+            return "Too many masking characters in term";
+        } else if (uri.equals(SRUConstants.SRU_ANCHORING_CHARACTER_NOT_SUPPORTED)) {
+            return "Anchoring character not supported";
+        } else if (uri.equals(SRUConstants.SRU_ANCHORING_CHARACTER_IN_UNSUPPORTED_POSITION)) {
+            return "Anchoring character in unsupported position";
+        } else if (uri.equals(SRUConstants.SRU_COMBINATION_OF_PROXIMITY_ADJACENCY_AND_MASKING_CHARACTERS_NOT_SUPPORTED)) {
+            return "Combination of proximity adjacency and masking characters not supported";
+        } else if (uri.equals(SRUConstants.SRU_COMBINATION_OF_PROXIMITY_ADJACENCY_AND_ANCHORING_CHARACTERS_NOT_SUPPORTED)) {
+            return "Combination of proximity adjacency and anchoring characters not supported";
+        } else if (uri.equals(SRUConstants.SRU_TERM_CONTAINS_ONLY_STOPWORDS)) {
+            return "Term contains only stopwords";
+        } else if (uri.equals(SRUConstants.SRU_TERM_IN_INVALID_FORMAT_FOR_INDEX_OR_RELATION)) {
+            return "Term in invalid format for index or relation";
+        } else if (uri.equals(SRUConstants.SRU_UNSUPPORTED_BOOLEAN_OPERATOR)) {
+            return "Unsupported boolean operator";
+        } else if (uri.equals(SRUConstants.SRU_TOO_MANY_BOOLEAN_OPERATORS_IN_QUERY)) {
+            return "Too many boolean operators in query";
+        } else if (uri.equals(SRUConstants.SRU_PROXIMITY_NOT_SUPPORTED)) {
+            return "Proximity not supported";
+        } else if (uri.equals(SRUConstants.SRU_UNSUPPORTED_PROXIMITY_RELATION)) {
+            return "Unsupported proximity relation";
+        } else if (uri.equals(SRUConstants.SRU_UNSUPPORTED_PROXIMITY_DISTANCE)) {
+            return "Unsupported proximity distance";
+        } else if (uri.equals(SRUConstants.SRU_UNSUPPORTED_PROXIMITY_UNIT)) {
+            return "Unsupported proximity unit";
+        } else if (uri.equals(SRUConstants.SRU_UNSUPPORTED_PROXIMITY_ORDERING)) {
+            return "Unsupported proximity ordering";
+        } else if (uri.equals(SRUConstants.SRU_UNSUPPORTED_COMBINATION_OF_PROXIMITY_MODIFIERS)) {
+            return "Unsupported combination of proximity modifiers";
+        } else if (uri.equals(SRUConstants.SRU_UNSUPPORTED_BOOLEAN_MODIFIER)) {
+            return "Unsupported boolean modifier";
+        } else if (uri.equals(SRUConstants.SRU_CANNOT_PROCESS_QUERY_REASON_UNKNOWN)) {
+            return "Cannot process query; reason unknown";
+        } else if (uri.equals(SRUConstants.SRU_QUERY_FEATURE_UNSUPPORTED)) {
+            return "Query feature unsupported";
+        } else if (uri.equals(SRUConstants.SRU_MASKING_CHARACTER_IN_UNSUPPORTED_POSITION)) {
+            return "Masking character in unsupported position";
+        } else if (uri.equals(SRUConstants.SRU_RESULT_SETS_NOT_SUPPORTED)) {
+            return "Result sets not supported";
+        } else if (uri.equals(SRUConstants.SRU_RESULT_SET_DOES_NOT_EXIST)) {
+            return "Result set does not exist";
+        } else if (uri.equals(SRUConstants.SRU_RESULT_SET_TEMPORARILY_UNAVAILABLE)) {
+            return "Result set temporarily unavailable";
+        } else if (uri.equals(SRUConstants.SRU_RESULT_SETS_ONLY_SUPPORTED_FOR_RETRIEVAL)) {
+            return "Result sets only supported for retrieval";
+        } else if (uri.equals(SRUConstants.SRU_COMBINATION_OF_RESULT_SETS_WITH_SEARCH_TERMS_NOT_SUPPORTED)) {
+            return "Combination of result sets with search terms not supported";
+        } else if (uri.equals(SRUConstants.SRU_RESULT_SET_CREATED_WITH_UNPREDICTABLE_PARTIAL_RESULTS_AVAILABLE)) {
+            return "Result set created with unpredictable partial results available";
+        } else if (uri.equals(SRUConstants.SRU_RESULT_SET_CREATED_WITH_VALID_PARTIAL_RESULTS_AVAILABLE)) {
+            return "Result set created with valid partial results available";
+        } else if (uri.equals(SRUConstants.SRU_RESULT_SET_NOT_CREATED_TOO_MANY_MATCHING_RECORDS)) {
+            return "Result set not created)) { too many matching records";
+        } else if (uri.equals(SRUConstants.SRU_FIRST_RECORD_POSITION_OUT_OF_RANGE)) {
+            return "First record position out of range";
+        } else if (uri.equals(SRUConstants.SRU_RECORD_TEMPORARILY_UNAVAILABLE)) {
+            return "Record temporarily unavailable";
+        } else if (uri.equals(SRUConstants.SRU_RECORD_DOES_NOT_EXIST)) {
+            return "Record does not exist";
+        } else if (uri.equals(SRUConstants.SRU_UNKNOWN_SCHEMA_FOR_RETRIEVAL)) {
+            return "Unknown schema for retrieval";
+        } else if (uri.equals(SRUConstants.SRU_RECORD_NOT_AVAILABLE_IN_THIS_SCHEMA)) {
+            return "Record not available in this schema";
+        } else if (uri.equals(SRUConstants.SRU_NOT_AUTHORISED_TO_SEND_RECORD)) {
+            return "Not authorised to send record";
+        } else if (uri.equals(SRUConstants.SRU_NOT_AUTHORISED_TO_SEND_RECORD_IN_THIS_SCHEMA)) {
+            return "Not authorised to send record in this schema";
+        } else if (uri.equals(SRUConstants.SRU_RECORD_TOO_LARGE_TO_SEND)) {
+            return "Record too large to send";
+        } else if (uri.equals(SRUConstants.SRU_UNSUPPORTED_RECORD_PACKING)) {
+            return "Unsupported record packing";
+        } else if (uri.equals(SRUConstants.SRU_XPATH_RETRIEVAL_UNSUPPORTED)) {
+            return "XPath retrieval unsupported";
+        } else if (uri.equals(SRUConstants.SRU_XPATH_EXPRESSION_CONTAINS_UNSUPPORTED_FEATURE)) {
+            return "XPath expression contains unsupported feature";
+        } else if (uri.equals(SRUConstants.SRU_UNABLE_TO_EVALUATE_XPATH_EXPRESSION)) {
+            return "Unable to evaluate XPath expression";
+        } else if (uri.equals(SRUConstants.SRU_SORT_NOT_SUPPORTED)) {
+            return "Sort not supported";
+        } else if (uri.equals(SRUConstants.SRU_UNSUPPORTED_SORT_SEQUENCE)) {
+            return "Unsupported sort sequence";
+        } else if (uri.equals(SRUConstants.SRU_TOO_MANY_RECORDS_TO_SORT)) {
+            return "Too many records to sort";
+        } else if (uri.equals(SRUConstants.SRU_TOO_MANY_SORT_KEYS_TO_SORT)) {
+            return "Too many sort keys to sort";
+        } else if (uri.equals(SRUConstants.SRU_CANNOT_SORT_INCOMPATIBLE_RECORD_FORMATS)) {
+            return "Cannot sort incompatible record formats";
+        } else if (uri.equals(SRUConstants.SRU_UNSUPPORTED_SCHEMA_FOR_SORT)) {
+            return "Unsupported schema for sort";
+        } else if (uri.equals(SRUConstants.SRU_UNSUPPORTED_PATH_FOR_SORT)) {
+            return "Unsupported path for sort";
+        } else if (uri.equals(SRUConstants.SRU_PATH_UNSUPPORTED_FOR_SCHEMA)) {
+            return "Path unsupported for schema";
+        } else if (uri.equals(SRUConstants.SRU_UNSUPPORTED_DIRECTION)) {
+            return "Unsupported direction";
+        } else if (uri.equals(SRUConstants.SRU_UNSUPPORTED_CASE)) {
+            return "Unsupported } else if (uri.equals(";
+        } else if (uri.equals(SRUConstants.SRU_UNSUPPORTED_MISSING_VALUE_ACTION)) {
+            return "Unsupported missing value action";
+        } else if (uri.equals(SRUConstants.SRU_SORT_ENDED_DUE_TO_MISSING_VALUE)) {
+            return "Sort ended due to missing value";
+        } else if (uri.equals(SRUConstants.SRU_SORT_SPEC_INCLUDED_BOTH_IN_QUERY_AND_PROTOCOL_QUERY_PREVAILS)) {
+            return "Sort spec included both in query and protocol query prevails";
+        } else if (uri.equals(SRUConstants.SRU_SORT_SPEC_INCLUDED_BOTH_IN_QUERY_AND_PROTOCOL_PROTOCOL_PREVAILS)) {
+            return "Sort spec included both in query and protocol protocol prevails";
+        } else if (uri.equals(SRUConstants.SRU_SORT_SPEC_INCLUDED_BOTH_IN_QUERY_AND_PROTOCOL_ERROR)) {
+            return "Sort spec included both in query and protocol error";
+        } else if (uri.equals(SRUConstants.SRU_STYLESHEETS_NOT_SUPPORTED)) {
+            return "Stylesheets not supported";
+        } else if (uri.equals(SRUConstants.SRU_UNSUPPORTED_STYLESHEET)) {
+            return "Unsupported stylesheet";
+        } else if (uri.equals(SRUConstants.SRU_RESPONSE_POSITION_OUT_OF_RANGE)) {
+            return "Response position out of range";
+        } else if (uri.equals(SRUConstants.SRU_TOO_MANY_TERMS_REQUESTED)) {
+            return "Too many terms requested";
+        } else {
+            return null;
+        }
+    }
+
+} // class SRUDiagnostic
Index: SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUDiagnosticList.java
===================================================================
--- SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUDiagnosticList.java	(revision 5925)
+++ SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUDiagnosticList.java	(revision 5925)
@@ -0,0 +1,46 @@
+/**
+ * This software is copyright (c) 2011-2013 by
+ *  - Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ * This is free software. You can redistribute it
+ * and/or modify it under the terms described in
+ * the GNU General Public License v3 of which you
+ * should have received a copy. Otherwise you can download
+ * it from
+ *
+ *   http://www.gnu.org/licenses/gpl-3.0.txt
+ *
+ * @copyright Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ *
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt
+ *  GNU General Public License v3
+ */
+package eu.clarin.sru.server;
+
+/**
+ * Container for non surrogate diagnostics for the request. The will be put in
+ * the <em>diagnostics</em> part of the response.
+ *
+ * @see SRUConstants
+ * @see SRUDiagnostic
+ * @see <a href="http://www.loc.gov/standards/sru/specs/diagnostics.html"> SRU
+ *      Diagnostics</a>
+ * @see <a href="http://www.loc.gov/standards/sru/resources/diagnostics-list.html">
+ *      SRU Diagnostics List</a>
+ */
+public interface SRUDiagnosticList {
+
+    /**
+     * Add a non surrogate diagnostic to the response.
+     *
+     * @param uri
+     *            the diagnostic's identifying URI
+     * @param details
+     *            supplementary information available, often in a format
+     *            specified by the diagnostic or <code>null</code>
+     * @param message
+     *            human readable message to display to the end user or
+     *            <code>null</code>
+     */
+    public void addDiagnostic(String uri, String details, String message);
+
+} // interface DiagnosticList
Index: SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUException.java
===================================================================
--- SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUException.java	(revision 5925)
+++ SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUException.java	(revision 5925)
@@ -0,0 +1,134 @@
+/**
+ * This software is copyright (c) 2011-2013 by
+ *  - Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ * This is free software. You can redistribute it
+ * and/or modify it under the terms described in
+ * the GNU General Public License v3 of which you
+ * should have received a copy. Otherwise you can download
+ * it from
+ *
+ *   http://www.gnu.org/licenses/gpl-3.0.txt
+ *
+ * @copyright Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ *
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt
+ *  GNU General Public License v3
+ */
+package eu.clarin.sru.server;
+
+/**
+ * An exception raised, if something went wrong processing the request. For
+ * diagnostic codes, see constants in {@link SRUConstants}.
+ *
+ * @see SRUConstants
+ */
+@SuppressWarnings("serial")
+public class SRUException extends Exception {
+    private final String uri;
+    private final String details;
+
+
+    /**
+     * Constructor.
+     *
+     * @param uri
+     *            the diagnostic's identifying URI
+     * @param details
+     *            diagnostic details or <code>null</code>
+     * @param message
+     *            diagnostic message or <code>null</code>
+     * @param cause
+     *            the cause of the error or <code>null</code>
+     */
+    public SRUException(String uri, String details, String message,
+            Throwable cause) {
+        super(message, cause);
+        if (uri == null) {
+            throw new NullPointerException("uri == null");
+        }
+        uri = uri.trim();
+        if (uri.isEmpty()) {
+            throw new IllegalArgumentException("uri is empty");
+        }
+        this.uri     = uri;
+        this.details = details;
+    }
+
+
+    /**
+     * Constructor.
+     *
+     * @param uri
+     *            the diagnostic's identifying URI
+     * @param details
+     *            diagnostic details or <code>null</code>
+     * @param message
+     *            diagnostic message or <code>null</code>
+     */
+    public SRUException(String uri, String details, String message) {
+        this(uri, details, message, null);
+    }
+
+
+    /**
+     * Constructor.
+     *
+     * @param uri
+     *            the diagnostic's identifying URI
+     * @param message
+     *            diagnostic message or <code>null</code>
+     * @param cause
+     *            the cause of the error or <code>null</code>
+     */
+    public SRUException(String uri, String message, Throwable cause) {
+        this(uri, null, message, cause);
+    }
+
+
+    /**
+     * Constructor.
+     *
+     * @param uri
+     *            the diagnostic's identifying URI
+     * @param message
+     *            diagnostic message or <code>null</code>
+     */
+    public SRUException(String uri, String message) {
+        this(uri, null, message, null);
+    }
+
+
+    /**
+     * Constructor.
+     *
+     * @param uri
+     *            the diagnostic's identifying URI
+     * @param cause
+     *            the cause of the error or <code>null</code>
+     */
+    public SRUException(String uri, Throwable cause) {
+        this(uri, null, null, cause);
+    }
+
+
+    /**
+     * Constructor.
+     *
+     * @param uri
+     *            the diagnostic's identifying URI
+     */
+    public SRUException(String uri) {
+        this(uri, null, null, null);
+    }
+
+
+    /**
+     * Create a SRU diagnostic from this exception.
+     *
+     * @return a {@link SRUDiagnostic} instance
+     */
+    public SRUDiagnostic getDiagnostic() {
+        return new SRUDiagnostic(uri, details, this.getMessage());
+    }
+
+} // class SRUException
Index: SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUExplainResult.java
===================================================================
--- SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUExplainResult.java	(revision 5925)
+++ SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUExplainResult.java	(revision 5925)
@@ -0,0 +1,44 @@
+/**
+ * This software is copyright (c) 2011-2013 by
+ *  - Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ * This is free software. You can redistribute it
+ * and/or modify it under the terms described in
+ * the GNU General Public License v3 of which you
+ * should have received a copy. Otherwise you can download
+ * it from
+ *
+ *   http://www.gnu.org/licenses/gpl-3.0.txt
+ *
+ * @copyright Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ *
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt
+ *  GNU General Public License v3
+ */
+package eu.clarin.sru.server;
+
+/**
+ * A result set of an <em>explain</em> operation. A database implementation may
+ * use it implement extensions to the SRU protocol, i.e. providing
+ * extraResponseData.
+ *
+ * <p>
+ * This class needs to be implemented for the target data source.
+ * </p>
+ *
+ * @see <a href="http://www.loc.gov/standards/sru/specs/explain.html">SRU
+ *      Explain Operation </a>
+ */
+public abstract class SRUExplainResult extends SRUAbstractResult {
+
+    /**
+     * Constructor.
+     *
+     * @param diagnostics
+     *            an instance of a SRUDiagnosticList
+     * @see SRUDiagnosticList
+     */
+    protected SRUExplainResult(SRUDiagnosticList diagnostics) {
+        super(diagnostics);
+    }
+
+} // class SRUExplainResult
Index: SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUOperation.java
===================================================================
--- SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUOperation.java	(revision 5925)
+++ SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUOperation.java	(revision 5925)
@@ -0,0 +1,37 @@
+/**
+ * This software is copyright (c) 2011-2013 by
+ *  - Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ * This is free software. You can redistribute it
+ * and/or modify it under the terms described in
+ * the GNU General Public License v3 of which you
+ * should have received a copy. Otherwise you can download
+ * it from
+ *
+ *   http://www.gnu.org/licenses/gpl-3.0.txt
+ *
+ * @copyright Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ *
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt
+ *  GNU General Public License v3
+ */
+package eu.clarin.sru.server;
+
+/**
+ * SRU operation.
+ */
+public enum SRUOperation {
+    /**
+     * A <em>explain</em> operation
+     */
+    EXPLAIN,
+
+    /**
+     * A <em>searchRetrieve</em> operation
+     */
+    SEARCH_RETRIEVE,
+
+    /**
+     * A <em>scan</em> operation
+     */
+    SCAN
+} // enum SRUOperation
Index: SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRURecordPacking.java
===================================================================
--- SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRURecordPacking.java	(revision 5925)
+++ SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRURecordPacking.java	(revision 5925)
@@ -0,0 +1,32 @@
+/**
+ * This software is copyright (c) 2011-2013 by
+ *  - Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ * This is free software. You can redistribute it
+ * and/or modify it under the terms described in
+ * the GNU General Public License v3 of which you
+ * should have received a copy. Otherwise you can download
+ * it from
+ *
+ *   http://www.gnu.org/licenses/gpl-3.0.txt
+ *
+ * @copyright Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ *
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt
+ *  GNU General Public License v3
+ */
+package eu.clarin.sru.server;
+
+/**
+ * SRU Record packing
+ */
+public enum SRURecordPacking {
+    /**
+     * XML record packing
+     */
+    XML,
+
+    /**
+     * String record packing
+     */
+    STRING
+} // enum SRURecordPacking
Index: SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRURequest.java
===================================================================
--- SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRURequest.java	(revision 5925)
+++ SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRURequest.java	(revision 5925)
@@ -0,0 +1,258 @@
+/**
+ * This software is copyright (c) 2011-2013 by
+ *  - Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ * This is free software. You can redistribute it
+ * and/or modify it under the terms described in
+ * the GNU General Public License v3 of which you
+ * should have received a copy. Otherwise you can download
+ * it from
+ *
+ *   http://www.gnu.org/licenses/gpl-3.0.txt
+ *
+ * @copyright Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ *
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt
+ *  GNU General Public License v3
+ */
+package eu.clarin.sru.server;
+
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.z3950.zing.cql.CQLNode;
+
+
+/**
+ * Provides information about a SRU request.
+ */
+public interface SRURequest {
+
+    /**
+     * Get the <em>operation</em> parameter of this request. Available for
+     * <em>explain</em>, <em>searchRetrieve</em> and <em>scan</em> requests.
+     *
+     * @return the operation
+     * @see SRUOperation
+     */
+    public SRUOperation getOperation();
+
+
+    /**
+     * Get the <em>version</em> parameter of this request. Available for
+     * <em>explain</em>, <em>searchRetrieve</em> and <em>scan</em> requests.
+     *
+     * @return the version
+     * @see SRUVersion
+     */
+    public SRUVersion getVersion();
+
+
+    /**
+     * Check if this request is of a specific version.
+     *
+     * @param version
+     *            the version to check
+     * @return <code>true</code> if this request is in the requested version,
+     *         <code>false</code> otherwise
+     * @throws NullPointerException
+     *             if version is <code>null</code>
+     */
+    public boolean isVersion(SRUVersion version);
+
+
+    /**
+     * Check if version of this request is at least <em>min</em> and at most
+     * <em>max</em>.
+     *
+     * @param min
+     *            the minimum version
+     * @param max
+     *            the maximum version
+     * @return <code>true</code> if this request is in the provides version,
+     *         <code>false</code> otherwise
+     * @throws NullPointerException
+     *             if minimum or maximum <code>null</code>
+     * @throws IllegalArgumentException
+     *             if minimum is larger the maximum
+     */
+    public boolean isVersion(SRUVersion min, SRUVersion max);
+
+
+    /**
+     * Get the <em>recordPacking</em> parameter of this request. Only available
+     * for <em>explain</em> and <em>searchRetrieve</em> requests.
+     *
+     * @return the record packing method
+     * @see SRURecordPacking
+     */
+    public SRURecordPacking getRecordPacking();
+
+
+    /**
+     * Get the <em>query</em> parameter of this request. Only available for
+     * <em>searchRetrieve</em> requests.
+     *
+     * @return the parsed query or <code>null</code> if not a
+     *         <em>searchRetrieve</em> request
+     */
+    public CQLNode getQuery();
+
+
+    /**
+     * Get the <em>startRecord</em> parameter of this request. Only available
+     * for <em>searchRetrieve</em> requests. If the client did not provide
+     * a value for the request, it is set to <code>1</code>.
+     *
+     * @return the number of the start record
+     */
+    public int getStartRecord();
+
+
+    /**
+     * Get the <em>maximumRecords</em> parameter of this request. Only available
+     * for <em>searchRetrieve</em> requests. If no value was supplied with the
+     * request, the server will automatically set a default value.
+     *
+     * @return the maximum number of records
+     */
+    public int getMaximumRecords();
+
+
+    /**
+     * Get the <em>recordSchema</em> parameter of this request. Only available
+     * for <em>searchRetrieve</em> requests.
+     *
+     * @return the record schema name or <code>null</code> if no value was
+     *         supplied for this request
+     * @deprecated use {@link #getRecordSchemaIdentifier()}
+     */
+    @Deprecated
+    public String getRecordSchemaName();
+
+
+    /**
+     * Get the record schema identifier derived from the <em>recordSchema</em>
+     * parameter of this request. Only available for <em>searchRetrieve</em>
+     * requests. If the request was send with the short record schema name,
+     * it will automatically expanded to the record schema identifier.
+     *
+     * @return the record schema identifier or <code>null</code> if no
+     *         <em>recordSchema</em> parameter was supplied for this request
+     */
+    public String getRecordSchemaIdentifier();
+
+
+    /**
+     * Get the <em>recordXPath</em> parameter of this request. Only available
+     * for <em>searchRetrieve</em> requests and version 1.1 requests.
+     *
+     * @return the record XPath or <code>null</code> of no value was supplied
+     *         for this request
+     */
+    public String getRecordXPath();
+
+
+    /**
+     * Get the <em>resultSetTTL</em> parameter of this request. Only available
+     * for <em>searchRetrieve</em> requests.
+     *
+     * @return the result set TTL or <code>-1</code> if no value was supplied
+     *         for this request
+     */
+    public int getResultSetTTL();
+
+
+    /**
+     * Get the <em>sortKeys</em> parameter of this request. Only available for
+     * <em>searchRetrieve</em> requests and version 1.1 requests.
+     *
+     * @return the record XPath or <code>null</code> of no value was supplied
+     *         for this request
+     */
+    public String getSortKeys();
+
+
+    /**
+     * Get the <em>scanClause</em> parameter of this request. Only available for
+     * <em>scan</em> requests.
+     *
+     * @return the parsed scan clause or <code>null</code> if not a
+     *         <em>scan</em> request
+     */
+    public CQLNode getScanClause();
+
+
+    /**
+     * Get the <em>responsePosition</em> parameter of this request. Only
+     * available for <em>scan</em> requests. If the client did not provide
+     * a value for the request, it is set to <code>1</code>.
+     *
+     * @return the response position
+     */
+    public int getResponsePosition();
+
+
+    /**
+     * Get the <em>maximumTerms</em> parameter of this request. Available for
+     * any type of request.
+     *
+     * @return the maximum number of terms or <code>-1</code> if no value was
+     *         supplied for this request
+     */
+    public int getMaximumTerms();
+
+
+    /**
+     * Get the <em>stylesheet</em> parameter of this request. Available for
+     * <em>explain</em>, <em>searchRetrieve</em> and <em>scan</em> requests.
+     *
+     * @return the stylesheet or <code>null</code> if no value was supplied for
+     *         this request
+     */
+    public String getStylesheet();
+
+
+    /**
+     * Get the protocol schema which was used of this request. Available for
+     * <em>explain</em>, <em>searchRetrieve</em> and <em>scan</em> requests.
+     *
+     * @return the protocol scheme
+     */
+    public String getProtocolScheme();
+
+
+    /**
+     * Get the names of extra parameters of this request. Available for
+     * <em>explain</em>, <em>searchRetrieve</em> and <em>scan</em> requests.
+     *
+     * @return a possibly empty list of parameter names
+     */
+    public List<String> getExtraRequestDataNames();
+
+
+    /**
+     * Get the value of an extra parameter of this request. Available for
+     * <em>explain</em>, <em>searchRetrieve</em> and <em>scan</em> requests.
+     *
+     * @param name
+     *            name of the extra parameter. Must be prefixed with
+     *            <code>x-</code>
+     * @return the value of the parameter of <code>null</code> of extra
+     *         parameter with that name exists
+     * @throws NullPointerException
+     *             if <code>name</code> is null
+     * @throws IllegalArgumentException
+     *             if <code>name</code> does not start with <code>x-</code>
+     */
+    public String getExtraRequestData(String name);
+
+
+    /**
+     * Get the raw client request information from the Servlet container.
+     *
+     * @return the Servlet request
+     */
+    public HttpServletRequest getServletRequest();
+
+} // interface SRURequest
Index: SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRURequestImpl.java
===================================================================
--- SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRURequestImpl.java	(revision 5925)
+++ SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRURequestImpl.java	(revision 5925)
@@ -0,0 +1,745 @@
+/**
+ * This software is copyright (c) 2011-2013 by
+ *  - Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ * This is free software. You can redistribute it
+ * and/or modify it under the terms described in
+ * the GNU General Public License v3 of which you
+ * should have received a copy. Otherwise you can download
+ * it from
+ *
+ *   http://www.gnu.org/licenses/gpl-3.0.txt
+ *
+ * @copyright Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ *
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt
+ *  GNU General Public License v3
+ */
+package eu.clarin.sru.server;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.z3950.zing.cql.CQLNode;
+import org.z3950.zing.cql.CQLParseException;
+import org.z3950.zing.cql.CQLParser;
+
+
+final class SRURequestImpl implements SRURequest, SRUDiagnosticList {
+    private static final String PARAM_OPERATION         = "operation";
+    private static final String PARAM_VERSION           = "version";
+    private static final String PARAM_RECORD_PACKING    = "recordPacking";
+    private static final String PARAM_STYLESHEET        = "stylesheet";
+    private static final String PARAM_QUERY             = "query";
+    private static final String PARAM_START_RECORD      = "startRecord";
+    private static final String PARAM_MAXIMUM_RECORDS   = "maximumRecords";
+    private static final String PARAM_RECORD_SCHEMA     = "recordSchema";
+    private static final String PARAM_RECORD_X_PATH     = "recordXPath";
+    private static final String PARAM_RESULT_SET_TTL    = "resultSetTTL";
+    private static final String PARAM_SORT_KEYS         = "sortKeys";
+    private static final String PARAM_SCAN_CLAUSE       = "scanClause";
+    private static final String PARAM_RESPONSE_POSITION = "responsePosition";
+    private static final String PARAM_MAXIMUM_TERMS     = "maximumTerms";
+    private static final String OP_EXPLAIN              = "explain";
+    private static final String OP_SCAN                 = "scan";
+    private static final String OP_SEARCH_RETRIEVE      = "searchRetrieve";
+    private static final String VERSION_1_1             = "1.1";
+    private static final String VERSION_1_2             = "1.2";
+    private static final String RECORD_PACKING_XML      = "xml";
+    private static final String RECORD_PACKING_STRING   = "string";
+    private static final String PARAM_EXTENSION_PREFIX  = "x-";
+    private static final String X_UNLIMITED_RESULTSET   = "x-unlimited-resultset";
+    private static final String X_UNLIMITED_TERMLIST    = "x-unlimited-termlist";
+    private static final String X_INDENT_RESPONSE       = "x-indent-response";
+    private static final int DEFAULT_START_RECORD       = 1;
+    private static final int DEFAULT_RESPONSE_POSITION  = 1;
+    private final SRUServerConfig config;
+    private final HttpServletRequest request;
+    private List<SRUDiagnostic> diagnostics;
+    private SRUOperation operation;
+    private SRUVersion version;
+    private SRURecordPacking recordPacking;
+    private CQLNode query;
+    private int startRecord = DEFAULT_START_RECORD;
+    private int maximumRecords = -1;
+    private String recordSchemaIdentifier;
+    private String stylesheet;
+    private String recordXPath;
+    private int resultSetTTL = -1;
+    private String sortKeys;
+    private CQLNode scanClause;
+    private int responsePosition = DEFAULT_RESPONSE_POSITION;
+    private int maximumTerms = -1;
+
+    private static enum Parameter {
+        RECORD_PACKING,
+        QUERY,
+        START_RECORD,
+        MAXIMUM_RECORDS,
+        RECORD_SCHEMA,
+        RECORD_XPATH,
+        RESULT_SET_TTL,
+        SORT_KEYS,
+        SCAN_CLAUSE,
+        RESPONSE_POSITION,
+        MAXIMUM_TERMS,
+        STYLESHEET
+    }
+
+    private static final class ParameterInfo {
+        private final Parameter parameter;
+        private final SRUVersion min;
+        private final SRUVersion max;
+        private final boolean mandatory;
+
+        private ParameterInfo(Parameter name, boolean mandatory,
+                SRUVersion min, SRUVersion max) {
+            this.parameter = name;
+            this.mandatory = mandatory;
+            this.min       = min;
+            this.max       = max;
+        }
+
+        public Parameter getParameter() {
+            return parameter;
+        }
+
+        public String getName() {
+            switch (parameter) {
+            case RECORD_PACKING:
+                return PARAM_RECORD_PACKING;
+            case QUERY:
+                return PARAM_QUERY;
+            case START_RECORD:
+                return PARAM_START_RECORD;
+            case MAXIMUM_RECORDS:
+                return PARAM_MAXIMUM_RECORDS;
+            case RECORD_SCHEMA:
+                return PARAM_RECORD_SCHEMA;
+            case RECORD_XPATH:
+                return PARAM_RECORD_X_PATH;
+            case RESULT_SET_TTL:
+                return PARAM_RESULT_SET_TTL;
+            case SORT_KEYS:
+                return PARAM_SORT_KEYS;
+            case SCAN_CLAUSE:
+                return PARAM_SCAN_CLAUSE;
+            case RESPONSE_POSITION:
+                return PARAM_RESPONSE_POSITION;
+            case MAXIMUM_TERMS:
+                return PARAM_MAXIMUM_TERMS;
+            case STYLESHEET:
+                return PARAM_STYLESHEET;
+            default:
+                throw new InternalError();
+            } // switch
+        }
+
+        public boolean getMandatory() {
+            return mandatory;
+        }
+
+        public boolean isForVersion(SRUVersion version) {
+            return (min.getVersionNumber() <= version.getVersionNumber()) &&
+                    (version.getVersionNumber() <= max.getVersionNumber());
+        }
+    } // class ParameterInfo
+
+    private static final ParameterInfo[] PARAMS_EXPLAIN = {
+        new ParameterInfo(Parameter.RECORD_PACKING, false,
+                SRUVersion.VERSION_1_1, SRUVersion.VERSION_1_2),
+        new ParameterInfo(Parameter.STYLESHEET, false,
+                SRUVersion.VERSION_1_1, SRUVersion.VERSION_1_2)
+    };
+    private static final ParameterInfo[] PARAMS_SCAN = {
+        new ParameterInfo(Parameter.SCAN_CLAUSE, true,
+                SRUVersion.VERSION_1_1, SRUVersion.VERSION_1_2),
+        new ParameterInfo(Parameter.RESPONSE_POSITION, false,
+                SRUVersion.VERSION_1_1, SRUVersion.VERSION_1_2),
+        new ParameterInfo(Parameter.MAXIMUM_TERMS, false,
+                SRUVersion.VERSION_1_1, SRUVersion.VERSION_1_2),
+        new ParameterInfo(Parameter.STYLESHEET, false,
+                SRUVersion.VERSION_1_1, SRUVersion.VERSION_1_2)
+
+    };
+    private static final ParameterInfo[] PARAMS_SEARCH_RETRIEVE = {
+        new ParameterInfo(Parameter.QUERY, true,
+                SRUVersion.VERSION_1_1, SRUVersion.VERSION_1_2),
+        new ParameterInfo(Parameter.START_RECORD, false,
+                SRUVersion.VERSION_1_1, SRUVersion.VERSION_1_2),
+        new ParameterInfo(Parameter.MAXIMUM_RECORDS, false,
+                SRUVersion.VERSION_1_1, SRUVersion.VERSION_1_2),
+        new ParameterInfo(Parameter.RECORD_PACKING, false,
+                SRUVersion.VERSION_1_1, SRUVersion.VERSION_1_2),
+        new ParameterInfo(Parameter.RECORD_SCHEMA, false,
+                SRUVersion.VERSION_1_1, SRUVersion.VERSION_1_2),
+        new ParameterInfo(Parameter.RECORD_XPATH, false,
+                SRUVersion.VERSION_1_1, SRUVersion.VERSION_1_1),
+        new ParameterInfo(Parameter.RESULT_SET_TTL, false,
+                SRUVersion.VERSION_1_1, SRUVersion.VERSION_1_2),
+        new ParameterInfo(Parameter.SORT_KEYS, false,
+                SRUVersion.VERSION_1_1, SRUVersion.VERSION_1_1),
+        new ParameterInfo(Parameter.STYLESHEET, false,
+                SRUVersion.VERSION_1_1, SRUVersion.VERSION_1_2)
+    };
+
+
+    SRURequestImpl(SRUServerConfig config, HttpServletRequest request) {
+        this.config        = config;
+        this.request       = request;
+    }
+
+
+    boolean checkParameters() {
+        // parse mandatory operation parameter
+        final String op = getParameter(PARAM_OPERATION, false, false);
+        if (op != null) {
+            if (!op.isEmpty()) {
+                if (op.equals(OP_EXPLAIN)) {
+                    this.operation = SRUOperation.EXPLAIN;
+                } else if (op.equals(OP_SCAN)) {
+                    this.operation = SRUOperation.SCAN;
+                } else if (op.equals(OP_SEARCH_RETRIEVE)) {
+                    this.operation = SRUOperation.SEARCH_RETRIEVE;
+                } else {
+                    addDiagnostic(SRUConstants.SRU_UNSUPPORTED_OPERATION, null,
+                            "Operation \"" + op + "\" is not supported.");
+                }
+            } else {
+                addDiagnostic(SRUConstants.SRU_UNSUPPORTED_OPERATION, null,
+                        "An empty parameter \"" + PARAM_OPERATION +
+                                "\" is not supported.");
+            }
+
+            // parse and check version
+            parseAndCheckVersionParameter();
+        } else {
+            /*
+             * absent parameter should be interpreted as "explain"
+             */
+            this.operation = SRUOperation.EXPLAIN;
+
+            // parse and check version
+            parseAndCheckVersionParameter();
+        }
+
+        if (diagnostics == null) {
+            // check mandatory/optional parameters for operation
+            ParameterInfo[] parameters;
+            switch (operation) {
+            case EXPLAIN:
+                parameters = PARAMS_EXPLAIN;
+                break;
+            case SCAN:
+                parameters = PARAMS_SCAN;
+                break;
+            case SEARCH_RETRIEVE:
+                parameters = PARAMS_SEARCH_RETRIEVE;
+                break;
+            default:
+                /* actually cannot happen */
+                addDiagnostic(SRUConstants.SRU_GENERAL_SYSTEM_ERROR, null,
+                        "internal error (invalid operation)");
+                return false;
+            }
+
+            /*
+             * keep list of all submitted parameters (except "operation" and
+             * "version"), so we can later warn if an unsupported parameter
+             * was sent (= not all parameters were consumed).
+             */
+            List<String> parameterNames = getParameterNames();
+
+            // check parameters ...
+            for (ParameterInfo parameter : parameters) {
+                String value = getParameter(parameter.getName(), true, true);
+                if (value != null) {
+                    // remove supported parameter from list
+                    parameterNames.remove(parameter.getName());
+
+                    /*
+                     * if parameter is not supported in this version, skip
+                     * it and create add an diagnostic.
+                     */
+                    if (!parameter.isForVersion(version)) {
+                        addDiagnostic(SRUConstants.SRU_UNSUPPORTED_PARAMETER,
+                                parameter.getName(),
+                                "Version " + version.getVersionString() +
+                                        " does not support parameter \"" +
+                                        parameter.getName() + "\".");
+                        continue;
+                    }
+
+                    // validate and parse parameters ...
+                    switch (parameter.getParameter()) {
+                    case RECORD_PACKING:
+                        if (value.endsWith(RECORD_PACKING_XML)) {
+                            recordPacking = SRURecordPacking.XML;
+                        } else if (value.equals(RECORD_PACKING_STRING)) {
+                            recordPacking = SRURecordPacking.STRING;
+                        } else {
+                            addDiagnostic(
+                                    SRUConstants.SRU_UNSUPPORTED_RECORD_PACKING,
+                                    null, "Record packing \"" + value +
+                                            "\" is not supported.");
+                        }
+                        break;
+                    case QUERY:
+                        query = parseCQLParameter(parameter.getName(), value);
+                        break;
+                    case START_RECORD:
+                        startRecord = parseNumberedParameter(
+                                parameter.getName(), value, 1);
+                        break;
+                    case MAXIMUM_RECORDS:
+                        maximumRecords = parseNumberedParameter(
+                                parameter.getName(), value, 0);
+                        break;
+                    case RECORD_SCHEMA:
+                        /*
+                         * The parameter recordSchema may contain either schema
+                         * identifier or the short name. If available, set to
+                         * appropriate schema identifier in the request object.
+                         */
+                        SRUServerConfig.SchemaInfo schemaInfo =
+                            config.findSchemaInfo(value);
+                        if (schemaInfo != null) {
+                            recordSchemaIdentifier = schemaInfo.getIdentifier();
+                        } else {
+                            /*
+                             * SRU servers are supposed to raise a non-surrogate
+                             * (fatal) diagnostic in case the record schema is
+                             * not known to the server.
+                             */
+                            addDiagnostic(
+                                    SRUConstants.SRU_UNKNOWN_SCHEMA_FOR_RETRIEVAL,
+                                    value, "Record schema \"" + value +
+                                    "\" is not supported  for retrieval.");
+                        }
+                        break;
+                    case RECORD_XPATH:
+                        recordXPath = value;
+                        break;
+                    case RESULT_SET_TTL:
+                        resultSetTTL = parseNumberedParameter(
+                                parameter.getName(), value, 0);
+                        break;
+                    case SORT_KEYS:
+                        sortKeys = value;
+                        break;
+                    case SCAN_CLAUSE:
+                        scanClause = parseCQLParameter(
+                                parameter.getName(), value);
+                        break;
+                    case RESPONSE_POSITION:
+                        responsePosition = parseNumberedParameter(
+                                parameter.getName(), value, 0);
+                        break;
+                    case MAXIMUM_TERMS:
+                        maximumTerms = parseNumberedParameter(
+                                parameter.getName(), value, 0);
+                        break;
+                    case STYLESHEET:
+                        stylesheet = value;
+                        break;
+                    } // switch
+                } else {
+                    if (parameter.getMandatory()) {
+                        addDiagnostic(
+                                SRUConstants.SRU_MANDATORY_PARAMETER_NOT_SUPPLIED,
+                                parameter.getName(), "Mandatory parameter \"" +
+                                        parameter.getName() +
+                                        "\" was not supplied.");
+                    }
+                }
+            } // for
+
+            /*
+             *  check if any parameters where not consumed and
+             *  add appropriate warnings
+             */
+            if (!parameterNames.isEmpty()) {
+                for (String name : parameterNames) {
+                    // skip extraRequestData (aka extensions)
+                    if (!name.startsWith(PARAM_EXTENSION_PREFIX)) {
+                        addDiagnostic(SRUConstants.SRU_UNSUPPORTED_PARAMETER,
+                                name, "Parameter \"" + name + "\" is not " +
+                                        "supported for this operation.");
+                    }
+                }
+            }
+        }
+
+        // diagnostics != null -> consider as success
+        return (diagnostics == null);
+    }
+
+
+    List<SRUDiagnostic> getDiagnostics() {
+        return diagnostics;
+    }
+
+
+    SRUVersion getRawVersion() {
+        return version;
+    }
+
+
+    SRURecordPacking getRawRecordPacking() {
+        return recordPacking;
+    }
+
+
+    String getRawRecordSchemaIdentifier() {
+        return getParameter(PARAM_RECORD_SCHEMA, true, false);
+    }
+
+
+    String getRawQuery() {
+        return getParameter(PARAM_QUERY, true, false);
+    }
+
+
+    int getRawMaximumRecords() {
+        return maximumRecords;
+    }
+
+
+    String getRawScanClause() {
+        return getParameter(PARAM_SCAN_CLAUSE, true, false);
+    }
+
+
+    int getIndentResponse() {
+        if (config.allowOverrideIndentResponse()) {
+            String s = getExtraRequestData(X_INDENT_RESPONSE);
+            if (s != null) {
+                try {
+                    int x = Integer.parseInt(s);
+                    if ((x > -2) && (x < 9)) {
+                        return x;
+                    }
+                } catch (NumberFormatException e) {
+                    /* IGNORE */
+                }
+            }
+        }
+        return config.getIndentResponse();
+    }
+
+
+    @Override
+    public SRUOperation getOperation() {
+        return operation;
+    }
+
+
+    @Override
+    public SRUVersion getVersion() {
+        return (version != null) ? version : config.getDefaultVersion();
+    }
+
+
+    @Override
+    public boolean isVersion(SRUVersion version) {
+        if (version == null) {
+            throw new NullPointerException("version == null");
+        }
+        return getVersion().equals(version);
+    }
+
+
+    @Override
+    public boolean isVersion(SRUVersion min, SRUVersion max) {
+        if (min == null) {
+            throw new NullPointerException("min == null");
+        }
+        if (max == null) {
+            throw new NullPointerException("max == null");
+        }
+        if (min.getVersionNumber() > max.getVersionNumber()) {
+            throw new IllegalArgumentException("min > max");
+        }
+        final SRUVersion v = getVersion();
+        return (min.getVersionNumber() >= v.getVersionNumber()) &&
+                (v.getVersionNumber() <= max.getVersionNumber());
+    }
+
+
+    @Override
+    public SRURecordPacking getRecordPacking() {
+        return (recordPacking != null)
+                ? recordPacking
+                : config.getDefaultRecordPacking();
+    }
+
+
+    @Override
+    public CQLNode getQuery() {
+        return query;
+    }
+
+
+    @Override
+    public int getStartRecord() {
+        return startRecord;
+    }
+
+
+    @Override
+    public int getMaximumRecords() {
+        if (config.allowOverrideMaximumRecords() &&
+                (getExtraRequestData(X_UNLIMITED_RESULTSET) != null)) {
+            return -1;
+        }
+        if (maximumRecords == -1) {
+            return config.getNumberOfRecords();
+        } else {
+            if (maximumRecords > config.getMaximumRecords()) {
+                return config.getMaximumRecords();
+            } else {
+                return maximumRecords;
+            }
+        }
+    }
+
+
+    @Override
+    @Deprecated
+    public String getRecordSchemaName() {
+        return getRawRecordSchemaIdentifier();
+    }
+
+
+    @Override
+    public String getRecordSchemaIdentifier() {
+        return recordSchemaIdentifier;
+    }
+
+
+    @Override
+    public String getRecordXPath() {
+        return recordXPath;
+    }
+
+
+    @Override
+    public int getResultSetTTL() {
+        return resultSetTTL;
+    }
+
+
+    @Override
+    public String getSortKeys() {
+        return sortKeys;
+    }
+
+
+    @Override
+    public CQLNode getScanClause() {
+        return scanClause;
+    }
+
+
+    @Override
+    public int getResponsePosition() {
+        return responsePosition;
+    }
+
+
+    @Override
+    public int getMaximumTerms() {
+        if (config.allowOverrideMaximumTerms() &&
+                (getExtraRequestData(X_UNLIMITED_TERMLIST) != null)) {
+            return -1;
+        }
+        if (maximumTerms == -1) {
+            return config.getNumberOfTerms();
+        } else {
+            if (maximumTerms > config.getMaximumTerms()) {
+                return config.getMaximumTerms();
+            } else {
+                return maximumTerms;
+            }
+        }
+    }
+
+
+    @Override
+    public String getStylesheet() {
+        return stylesheet;
+    }
+
+
+    @Override
+    public String getProtocolScheme() {
+        return request.isSecure() ? "https://" : "http://";
+    }
+
+
+    @Override
+    public List<String> getExtraRequestDataNames() {
+        List<String> result = null;
+        for (Enumeration<?> i = request.getParameterNames(); i.hasMoreElements(); ) {
+            String name = (String) i.nextElement();
+            if (name.startsWith(PARAM_EXTENSION_PREFIX)) {
+                if (result == null) {
+                    result = new ArrayList<String>();
+                }
+                result.add(name);
+            }
+        }
+
+        if (result != null) {
+            return result;
+        } else {
+            return Collections.emptyList();
+        }
+    }
+
+
+    @Override
+    public String getExtraRequestData(String name) {
+        if (name == null) {
+            throw new NullPointerException("name == null");
+        }
+        if (!name.startsWith(PARAM_EXTENSION_PREFIX)) {
+            throw new IllegalArgumentException(
+                    "name must start with \"" + PARAM_EXTENSION_PREFIX + "\"");
+        }
+        return request.getParameter(name);
+    }
+
+
+    @Override
+    public HttpServletRequest getServletRequest() {
+        return request;
+    }
+
+
+    @Override
+    public void addDiagnostic(String uri, String details, String message) {
+        final SRUDiagnostic diagnostic =
+                new SRUDiagnostic(uri, details, message);
+        if (diagnostics == null) {
+            diagnostics = new ArrayList<SRUDiagnostic>();
+        }
+        diagnostics.add(diagnostic);
+    }
+
+
+    private List<String> getParameterNames() {
+        List<String> list = new ArrayList<String>();
+        for (Enumeration<?> i = request.getParameterNames();
+                i.hasMoreElements();) {
+            String name = (String) i.nextElement();
+            if (!(name.equals(PARAM_OPERATION) || name.equals(PARAM_VERSION))) {
+                list.add(name);
+            }
+        }
+        return list;
+    }
+
+
+    private String getParameter(String name, boolean nullify,
+            boolean diagnosticIfEmpty) {
+        String s = request.getParameter(name);
+        if (s != null) {
+            s = s.trim();
+            if (nullify && s.isEmpty()) {
+                s = null;
+                if (diagnosticIfEmpty) {
+                    addDiagnostic(SRUConstants.SRU_UNSUPPORTED_PARAMETER_VALUE,
+                            name, "An empty parameter \"" + PARAM_OPERATION +
+                            "\" is not supported.");
+                }
+            }
+        }
+        return s;
+    }
+
+
+    private void parseAndCheckVersionParameter() {
+        final String v = getParameter(PARAM_VERSION, true, true);
+        if (v != null) {
+            if (v.equals(VERSION_1_1)) {
+                this.version = SRUVersion.VERSION_1_1;
+            } else if (v.equals(VERSION_1_2)) {
+                this.version = SRUVersion.VERSION_1_2;
+            } else {
+                addDiagnostic(SRUConstants.SRU_UNSUPPORTED_VERSION,
+                        VERSION_1_2, "Version \"" + v +
+                                "\" is not supported");
+            }
+        } else {
+            /*
+             * except for "explain" operation, complain if "version"
+             * parameter was not supplied.
+             */
+            if (this.operation != SRUOperation.EXPLAIN) {
+                addDiagnostic(
+                        SRUConstants.SRU_MANDATORY_PARAMETER_NOT_SUPPLIED,
+                        PARAM_VERSION, "Mandatory parameter \"" +
+                                PARAM_VERSION + "\" was not supplied.");
+            }
+
+            /*
+             * this is an explain operation, assume default version
+             */
+            this.version = config.getDefaultVersion();
+        }
+    }
+
+
+    private int parseNumberedParameter(String param, String value,
+            int minValue) {
+        int result = -1;
+
+        if (value != null) {
+            try {
+                result = Integer.parseInt(value);
+                if (result < minValue) {
+                    addDiagnostic(SRUConstants.SRU_UNSUPPORTED_PARAMETER_VALUE,
+                            param, "Value is less than " + minValue + ".");
+                }
+            } catch (NumberFormatException e) {
+                addDiagnostic(SRUConstants.SRU_UNSUPPORTED_PARAMETER_VALUE,
+                        param, "Invalid number format.");
+            }
+        }
+        return result;
+    }
+
+
+    private CQLNode parseCQLParameter(String param, String value) {
+        CQLNode result = null;
+
+        /*
+         * XXX: maybe query length against limit and return
+         * "Too many characters in query" error?
+         */
+        try {
+            int compat = -1;
+            switch (version) {
+            case VERSION_1_1:
+                compat = CQLParser.V1POINT1;
+                break;
+            case VERSION_1_2:
+                compat = CQLParser.V1POINT2;
+            }
+            result = new CQLParser(compat).parse(value);
+        } catch (CQLParseException e) {
+            addDiagnostic(SRUConstants.SRU_QUERY_SYNTAX_ERROR,
+                    null, "error parsing query");
+        } catch (IOException e) {
+            addDiagnostic(SRUConstants.SRU_QUERY_SYNTAX_ERROR,
+                    null, "error parsing query");
+        }
+        return result;
+    }
+
+} // class SRURequestImpl
Index: SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUScanResultSet.java
===================================================================
--- SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUScanResultSet.java	(revision 5925)
+++ SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUScanResultSet.java	(revision 5925)
@@ -0,0 +1,170 @@
+/**
+ * This software is copyright (c) 2011-2013 by
+ *  - Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ * This is free software. You can redistribute it
+ * and/or modify it under the terms described in
+ * the GNU General Public License v3 of which you
+ * should have received a copy. Otherwise you can download
+ * it from
+ *
+ *   http://www.gnu.org/licenses/gpl-3.0.txt
+ *
+ * @copyright Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ *
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt
+ *  GNU General Public License v3
+ */
+package eu.clarin.sru.server;
+
+import java.util.NoSuchElementException;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+
+/**
+ * A result set of a <em>scan</em> operation. It is used to iterate over the
+ * term set and provides a method to serialize the terms.
+ *
+ * <p>
+ * A <code>SRUScanResultSet</code> object maintains a cursor pointing to its
+ * current term. Initially the cursor is positioned before the first term. The
+ * <code>next</code> method moves the cursor to the next term, and because it
+ * returns <code>false</code> when there are no more terms in the
+ * <code>SRUScanResultSet</code> object, it can be used in a <code>while</code>
+ * loop to iterate through the term set.
+ * </p>
+ * <p>
+ * This class needs to be implemented for the target search engine.
+ * </p>
+ *
+ * @see <a href="http://www.loc.gov/standards/sru/specs/scan.html"> SRU Scan
+ *      Operation</a>
+ */
+public abstract class SRUScanResultSet extends SRUAbstractResult {
+    /**
+     * A flag to indicate the position of the term within the complete term
+     * list.
+     */
+    public enum WhereInList {
+        /**
+         * The first term (<em>first</em>)
+         */
+        FIRST,
+
+        /**
+         * The last term (<em>last</em>)
+         */
+        LAST,
+
+        /**
+         * The only term (<em>only</em>)
+         */
+        ONLY,
+
+        /**
+         * Any other term (<em>inner</em>)
+         */
+        INNER
+    }
+
+
+    /**
+     * Constructor.
+     *
+     * @param diagnostics
+     *            an instance of a SRUDiagnosticList.
+     * @see SRUDiagnosticList
+     */
+    protected SRUScanResultSet(SRUDiagnosticList diagnostics) {
+        super(diagnostics);
+    }
+
+
+    /**
+     * Moves the cursor forward one term from its current position. A result set
+     * cursor is initially positioned before the first record; the first call to
+     * the method <code>next</code> makes the first term the current term; the
+     * second call makes the second term the current term, and so on.
+     * <p>
+     * When a call to the <code>next</code> method returns <code>false</code>,
+     * the cursor is positioned after the last term.
+     * </p>
+     *
+     * @return <code>true</code> if the new current term is valid;
+     *         <code>false</code> if there are no more terms
+     * @throws SRUException
+     *             if an error occurred while fetching the next term
+     */
+    public abstract boolean nextTerm() throws SRUException;
+
+
+    /**
+     * Get the current term exactly as it appears in the index.
+     *
+     * @return current term
+     */
+    public abstract String getValue();
+
+
+    /**
+     * Get the number of records for the current term which would be matched if
+     * the index in the request's <em>scanClause</em> was searched with the term
+     * in the <em>value</em> field.
+     *
+     * @return a non-negative number of records or
+     *         <code>-1</code>, if the number is unknown.
+     */
+    public abstract int getNumberOfRecords();
+
+
+    /**
+     * Get the string for the current term to display to the end user in place
+     * of the term itself.
+     *
+     * @return display string or <code>null</code>
+     */
+    public abstract String getDisplayTerm();
+
+
+    /**
+     * Get the flag to indicate the position of the term within the complete
+     * term list.
+     *
+     * @return position within term list or <code>null</code>
+     */
+    public abstract WhereInList getWhereInList();
+
+
+    /**
+     * Check, if extra term data should be serialized for the current term. A
+     * default implementation is provided for convince and always returns
+     * <code>false</code>.
+     *
+     * @return <code>true</code> if the term has extra term data
+     * @throws NoSuchElementException
+     *             term set is already advanced past all past terms
+     * @see #writeExtraTermData(XMLStreamWriter)
+     */
+    public boolean hasExtraTermData() {
+        return false;
+    }
+
+
+    /**
+     * Serialize extra term data for the current term. A no-op default
+     * implementation is provided for convince.
+     *
+     * @param writer
+     *            the {@link XMLStreamException} instance to be used
+     * @throws XMLStreamException
+     *             an error occurred while serializing the term extra data
+     * @throws NoSuchElementException
+     *             result set already advanced past all terms
+     * @see #hasExtraTermData()
+     */
+    public void writeExtraTermData(XMLStreamWriter writer)
+            throws XMLStreamException {
+    }
+
+} // abstract class SRUScanResult
Index: SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUSearchEngine.java
===================================================================
--- SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUSearchEngine.java	(revision 5925)
+++ SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUSearchEngine.java	(revision 5925)
@@ -0,0 +1,120 @@
+/**
+ * This software is copyright (c) 2011-2013 by
+ *  - Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ * This is free software. You can redistribute it
+ * and/or modify it under the terms described in
+ * the GNU General Public License v3 of which you
+ * should have received a copy. Otherwise you can download
+ * it from
+ *
+ *   http://www.gnu.org/licenses/gpl-3.0.txt
+ *
+ * @copyright Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ *
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt
+ *  GNU General Public License v3
+ */
+package eu.clarin.sru.server;
+
+/**
+ * Interface for connecting the SRU protocol implementation to an actual search
+ * engine.
+ * <p>
+ * Implementing the
+ * {@link #explain(SRUServerConfig, SRURequest, SRUDiagnosticList)} and
+ * {@link #scan(SRUServerConfig, SRURequest, SRUDiagnosticList)} is optional,
+ * but implementing
+ * {@link #search(SRUServerConfig, SRURequest, SRUDiagnosticList)} is mandatory.
+ * </p>
+ * <p>
+ * The implementation of these methods <em>must</em> be thread-safe.
+ * </p>
+ */
+public interface SRUSearchEngine {
+
+    /**
+     * Handle an <em>explain</em> operation. Implementing this method is
+     * optional, but is required, if the <em>writeExtraResponseData</em> block
+     * of the SRU response needs to be filled. The arguments for this operation
+     * are provides by the {@link SRURequest} object.
+     * <p>
+     * The implementation of this method <em>must</em> be thread-safe.
+     * </p>
+     *
+     * @param config
+     *            the <code>SRUEndpointConfig</code> object that contains the
+     *            endpoint configuration
+     * @param request
+     *            the <code>SRURequest</code> object that contains the request
+     *            made to the endpoint
+     * @param diagnostics
+     *            the <code>SRUDiagnosticList</code> object for storing
+     *            non-fatal diagnostics
+     * @return a <code>SRUExplainResult</code> object or <code>null</code> if
+     *         the search engine does not want to provide
+     *         <em>writeExtraResponseData</em>
+     * @throws SRUException
+     *             if an fatal error occurred
+     * @see SRUExplainResult
+     */
+    public SRUExplainResult explain(SRUServerConfig config, SRURequest request,
+            SRUDiagnosticList diagnostics) throws SRUException;
+
+
+    /**
+     * Handle a <em>searchRetrieve</em> operation. Implementing this method is
+     * mandatory. The arguments for this operation are provides by the
+     * {@link SRURequest} object.
+     * <p>
+     * The implementation of this method <em>must</em> be thread-safe.
+     * </p>
+     *
+     * @param config
+     *            the <code>SRUEndpointConfig</code> object that contains the
+     *            endpoint configuration
+     * @param request
+     *            the <code>SRURequest</code> object that contains the request
+     *            made to the endpoint
+     * @param diagnostics
+     *            the <code>SRUDiagnosticList</code> object for storing
+     *            non-fatal diagnostics
+     * @return a <code>SRUSearchResultSet</code> object
+     * @throws SRUException
+     *             if an fatal error occurred
+     * @see SRURequest
+     * @see SRUExplainResult
+     */
+    public SRUSearchResultSet search(SRUServerConfig config,
+            SRURequest request, SRUDiagnosticList diagnostics)
+            throws SRUException;
+
+
+    /**
+     * Handle a <em>scan</em> operation. Implementing this method is optional.
+     * If you don't need to handle the <em>scan</em> operation, just return
+     * <code>null</code> and the SRU server will return the appropiate
+     * diagnostic to the client. The arguments for this operation are provides
+     * by the {@link SRURequest} object.
+     * <p>
+     * The implementation of this method <em>must</em> be thread-safe.
+     * </p>
+     *
+     * @param config
+     *            the <code>SRUEndpointConfig</code> object that contains the
+     *            endpoint configuration
+     * @param request
+     *            the <code>SRURequest</code> object that contains the request
+     *            made to the endpoint
+     * @param diagnostics
+     *            the <code>SRUDiagnosticList</code> object for storing
+     *            non-fatal diagnostics
+     * @return a <code>SRUScanResultSet</code> object or <code>null</code> if
+     *         this operation is not supported by this search engine
+     * @throws SRUException
+     *             if an fatal error occurred
+     * @see SRUExplainResult
+     */
+    public SRUScanResultSet scan(SRUServerConfig config, SRURequest request,
+            SRUDiagnosticList diagnostics) throws SRUException;
+
+} // interface SRUSearchEngine
Index: SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUSearchResultSet.java
===================================================================
--- SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUSearchResultSet.java	(revision 5925)
+++ SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUSearchResultSet.java	(revision 5925)
@@ -0,0 +1,200 @@
+/**
+ * This software is copyright (c) 2011-2013 by
+ *  - Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ * This is free software. You can redistribute it
+ * and/or modify it under the terms described in
+ * the GNU General Public License v3 of which you
+ * should have received a copy. Otherwise you can download
+ * it from
+ *
+ *   http://www.gnu.org/licenses/gpl-3.0.txt
+ *
+ * @copyright Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ *
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt
+ *  GNU General Public License v3
+ */
+package eu.clarin.sru.server;
+
+import java.util.NoSuchElementException;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+
+/**
+ * A result set of a <em>searchRetrieve</em> operation. It it used to iterate
+ * over the result set and provides a method to serialize the record in the
+ * requested format.
+ * <p>
+ * A <code>SRUSearchResultSet</code> object maintains a cursor pointing to its
+ * current record. Initially the cursor is positioned before the first record.
+ * The <code>next</code> method moves the cursor to the next record, and because
+ * it returns <code>false</code> when there are no more records in the
+ * <code>SRUSearchResultSet</code> object, it can be used in a
+ * <code>while</code> loop to iterate through the result set.
+ * </p>
+ * <p>
+ * This class needs to be implemented for the target search engine.
+ * </p>
+ *
+ * @see <a href="http://www.loc.gov/standards/sru/specs/search-retrieve.html">
+ *      SRU Search Retrieve Operation</a>
+ */
+public abstract class SRUSearchResultSet extends SRUAbstractResult {
+
+    /**
+     * Constructor.
+     *
+     * @param diagnostics
+     *            an instance of a SRUDiagnosticList
+     * @see SRUDiagnosticList
+     */
+    protected SRUSearchResultSet(SRUDiagnosticList diagnostics) {
+        super(diagnostics);
+    }
+
+
+    /**
+     * The number of records matched by the query. If the query fails this must
+     * be 0. If the search engine cannot determine the total number of matched
+     * by a query, it must return -1.
+     *
+     * @return the total number of results or 0 if the query failed or -1 if the
+     *         search engine cannot determine the total number of results
+     */
+    public abstract int getTotalRecordCount();
+
+
+    /**
+     * The number of records matched by the query but at most as the number of
+     * records requested to be returned (maximumRecords parameter). If the query
+     * fails this must be 0.
+     *
+     * @return the number of results or 0 if the query failed
+     */
+    public abstract int getRecordCount();
+
+
+    /**
+     * The result set id of this result. the default implementation returns
+     * <code>null</code>.
+     *
+     * @return the result set id or <code>null</code> if not applicable for this
+     *         result
+     */
+    public String getResultSetId() {
+        return null;
+    }
+
+
+    /**
+     * The idle time for this result. The default implementation returns
+     * <code>-1</code>.
+     *
+     * @return the result set idle time or <code>-1</code> if not applicable for
+     *         this result
+     */
+    public int getResultSetIdleTime() {
+        return -1;
+    }
+
+
+    /**
+     * The record schema identifier in which the records are returned
+     * (recordSchema parameter).
+     *
+     * @return the record schema identifier
+     */
+    public abstract String getRecordSchemaIdentifier();
+
+
+    /**
+     * Moves the cursor forward one record from its current position. A
+     * <code>SRUSearchResultSet</code> cursor is initially positioned before the
+     * first record; the first call to the method <code>next</code> makes the
+     * first record the current record; the second call makes the second record
+     * the current record, and so on.
+     * <p>
+     * When a call to the <code>next</code> method returns <code>false</code>,
+     * the cursor is positioned after the last record.
+     * </p>
+     *
+     * @return <code>true</code> if the new current record is valid;
+     *         <code>false</code> if there are no more records
+     * @throws SRUException
+     *             if an error occurred while fetching the next record
+     */
+    public abstract boolean nextRecord() throws SRUException;
+
+
+    /**
+     * An identifier for the current record by which it can unambiguously be
+     * retrieved in a subsequent operation.
+     *
+     * @return identifier for the record or <code>null</code> of none is
+     *         available
+     * @throws NoSuchElementException
+     *             result set is past all records
+     */
+    public abstract String getRecordIdentifier();
+
+
+    /**
+     * Get surrogate diagnostic for current record. If this method returns a
+     * diagnostic, the writeRecord method will not be called. The default
+     * implementation returns <code>null</code>.
+     *
+     * @return a surrogate diagnostic or <code>null</code>
+     */
+    public SRUDiagnostic getSurrogateDiagnostic() {
+        return null;
+    }
+
+
+    /**
+     * Serialize the current record in the requested format.
+     *
+     * @param writer
+     *            the {@link XMLStreamException} instance to be used
+     * @throws XMLStreamException
+     *             an error occurred while serializing the result
+     * @throws NoSuchElementException
+     *             result set past all records
+     * @see #getRecordSchemaIdentifier()
+     */
+    public abstract void writeRecord(XMLStreamWriter writer)
+            throws XMLStreamException;
+
+
+    /**
+     * Check, if extra record data should be serialized for the current record.
+     * The default implementation returns <code>false</code>.
+     *
+     * @return <code>true</code> if the record has extra record data
+     * @throws NoSuchElementException
+     *             result set is already advanced past all records
+     * @see #writeExtraResponseData(XMLStreamWriter)
+     */
+    public boolean hasExtraRecordData() {
+        return false;
+    }
+
+
+    /**
+     * Serialize extra record data for the current record. A no-op default
+     * implementation is provided for convince.
+     *
+     * @param writer
+     *            the {@link XMLStreamException} instance to be used
+     * @throws XMLStreamException
+     *             an error occurred while serializing the result extra data
+     * @throws NoSuchElementException
+     *             result set past already advanced past all records
+     * @see #hasExtraRecordData()
+     */
+    public void writeExtraRecordData(XMLStreamWriter writer)
+            throws XMLStreamException {
+    }
+
+} // class SRUSearchResultSet
Index: SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUServer.java
===================================================================
--- SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUServer.java	(revision 5925)
+++ SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUServer.java	(revision 5925)
@@ -0,0 +1,1103 @@
+/**
+ * This software is copyright (c) 2011-2013 by
+ *  - Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ * This is free software. You can redistribute it
+ * and/or modify it under the terms described in
+ * the GNU General Public License v3 of which you
+ * should have received a copy. Otherwise you can download
+ * it from
+ *
+ *   http://www.gnu.org/licenses/gpl-3.0.txt
+ *
+ * @copyright Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ *
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt
+ *  GNU General Public License v3
+ */
+package eu.clarin.sru.server;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.z3950.zing.cql.CQLNode;
+
+import eu.clarin.sru.server.SRUServerConfig.DatabaseInfo;
+import eu.clarin.sru.server.SRUServerConfig.IndexInfo;
+import eu.clarin.sru.server.SRUServerConfig.LocalizedString;
+import eu.clarin.sru.server.SRUServerConfig.SchemaInfo;
+import eu.clarin.sru.server.utils.SRUServerServlet;
+
+
+/**
+ * SRU/CQL protocol implementation for the server-side (SRU/S). This class
+ * implements SRU/CQL version 1.1 and and 1.2.
+ *
+ * @see SRUServerConfig
+ * @see SRUSearchEngine
+ * @see SRUServerServlet
+ * @see <a href="http://www.loc.gov/standards/sru/">SRU/CQL protocol 1.2</a>
+ */
+public final class SRUServer {
+    private static final String SRU_NS =
+            "http://www.loc.gov/zing/srw/";
+    private static final String SRU_PREFIX = "sru";
+    private static final String SRU_DIAGNOSIC_NS =
+            "http://www.loc.gov/zing/srw/diagnostic/";
+    private static final String SRU_DIAGNOSTIC_PREFIX = "diag";
+    private static final String SRU_DIAGNOSTIC_RECORD_SCHEMA =
+            "info:srw/schema/1/diagnostics-v1.1";
+    private static final String SRU_EXPLAIN_NS =
+            "http://explain.z3950.org/dtd/2.0/";
+    private static final String SRU_EXPLAIN_PREFIX = "zr";
+    private static final String SRU_XCQL_NS =
+            "http://www.loc.gov/zing/cql/xcql/";
+    static final String RESPONSE_ENCODING = "utf-8";
+    private static final String RESPONSE_CONTENT_TYPE = "application/xml";
+    private static final Logger logger =
+            LoggerFactory.getLogger(SRUServer.class);
+    private final SRUServerConfig config;
+    private final SRUSearchEngine searchEngine;
+    private final XMLOutputFactory writerFactory;
+
+
+    /**
+     * Constructor.
+     *
+     * @param config
+     *            a SRUEndpointConfig object
+     * @param searchEngine
+     *            an object implementing the SRUSearchEngine interface
+     * @throws NullPointerException
+     *             if config or searchEngine is <code>null</code>
+     * @throws SRUException
+     *             if an error occurred
+     */
+    public SRUServer(SRUServerConfig config, SRUSearchEngine searchEngine)
+            throws SRUException {
+        if (config == null) {
+            throw new NullPointerException("config == null");
+        }
+        this.config = config;
+        if (searchEngine == null) {
+            throw new NullPointerException("searchEngine == null");
+        }
+        this.searchEngine = searchEngine;
+        this.writerFactory = XMLOutputFactory.newInstance();
+    }
+
+
+    /**
+     * Handle a SRU request.
+     *
+     * @param request
+     *            a HttpServletRequest request
+     * @param response
+     *            a HttpServletResponse request
+     */
+    public void handleRequest(HttpServletRequest request,
+            HttpServletResponse response) {
+        final SRURequestImpl req = new SRURequestImpl(config, request);
+        try {
+            // set response properties
+            response.setContentType(RESPONSE_CONTENT_TYPE);
+            response.setCharacterEncoding(RESPONSE_ENCODING);
+            response.setStatus(HttpServletResponse.SC_OK);
+            // make sure we can reset the stream later in case of error ...
+            response.setBufferSize(config.getResponseBufferSize());
+            try {
+                if (req.checkParameters()) {
+                    switch (req.getOperation()) {
+                    case EXPLAIN:
+                        explain(req, response);
+                        break;
+                    case SCAN:
+                        scan(req, response);
+                        break;
+                    case SEARCH_RETRIEVE:
+                        search(req, response);
+                        break;
+                    }
+                } else {
+                    // (some) parameters are malformed, send error
+                    SRUXMLStreamWriter out =
+                        createXMLStreamWriter(response.getOutputStream(),
+                                SRURecordPacking.XML, false,
+                                req.getIndentResponse());
+                    writeFatalError(out, req, req.getDiagnostics());
+                }
+            } catch (XMLStreamException e) {
+                logger.error("An error occurred while serializing response", e);
+                throw new SRUException(SRUConstants.SRU_GENERAL_SYSTEM_ERROR,
+                        "An error occurred while serializing response.", e);
+            } catch (IOException e) {
+                /*
+                 * Well, can't really do anything useful here ...
+                 */
+                logger.error("An unexpected exception occurred", e);
+            }
+        } catch (SRUException e) {
+            if (!response.isCommitted()) {
+                if (logger.isInfoEnabled()) {
+                    final String message = e.getDiagnostic().getMessage();
+                    if (message != null) {
+                        logger.info("Sending fatal diagnostic '{}' with " +
+                                "message '{}'",
+                                e.getDiagnostic().getURI(),
+                                message);
+                    } else {
+                        logger.info("Sending fatal diagnostic '{}'",
+                                e.getDiagnostic().getURI());
+                    }
+                    logger.debug("Fatal diagnostic was caused by " +
+                            "this exception", e);
+                }
+                response.resetBuffer();
+                try {
+                    List<SRUDiagnostic> diagnostics = req.getDiagnostics();
+                    if (diagnostics != null) {
+                        diagnostics.add(e.getDiagnostic());
+                    } else {
+                        diagnostics = Arrays.asList(e.getDiagnostic());
+                    }
+                    SRUXMLStreamWriter out =
+                            createXMLStreamWriter(response.getOutputStream(),
+                                    SRURecordPacking.XML, false,
+                                    req.getIndentResponse());
+                    writeFatalError(out, req, diagnostics);
+                } catch (Exception ex) {
+                    logger.error("An exception occurred while in error state",
+                            ex);
+                }
+            } else {
+                /*
+                 * The Servlet already flushed the output buffer, so cannot
+                 * degrade gracefully anymore and, unfortunately, will produce
+                 * ill-formed XML output.
+                 * Increase the response buffer size, if you want to avoid
+                 * this (at the cost of memory).
+                 */
+                logger.error("A fatal error occurred, but the response was "
+                        + "already committed. Unable to recover gracefully.", e);
+            }
+        }
+    }
+
+
+    private void explain(SRURequestImpl request, HttpServletResponse response)
+            throws IOException, XMLStreamException, SRUException {
+        logger.info("explain");
+
+        // commence explain ...
+        final SRUExplainResult result =
+                searchEngine.explain(config, request, request);
+
+        try {
+            // send results
+            SRUXMLStreamWriter out =
+                    createXMLStreamWriter(response.getOutputStream(),
+                                          request.getRecordPacking(),
+                                          true,
+                                          request.getIndentResponse());
+
+            beginResponse(out, request);
+
+            // write the explain record
+            writeExplainRecord(out, request);
+
+            if (config.getEchoRequests()) {
+                writeEchoedExplainRequest(out, request);
+            }
+
+            // diagnostics
+            writeDiagnosticList(out, request.getDiagnostics());
+
+            // extraResponseData
+            if (result != null) {
+                if (result.hasExtraResponseData()) {
+                    out.writeStartElement(SRU_NS, "extraResponseData");
+                    result.writeExtraResponseData(out);
+                    out.writeEndElement(); // "extraResponseData" element
+                }
+            }
+
+            endResponse(out);
+        } finally {
+            if (result != null) {
+                result.close();
+            }
+        }
+    }
+
+
+    private void scan(SRURequestImpl request, HttpServletResponse response)
+            throws IOException, XMLStreamException, SRUException {
+        logger.info("scan: scanClause = \"{}\"",
+                new Object[] { request.getRawScanClause() });
+
+        // commence scan
+        final SRUScanResultSet result =
+                searchEngine.scan(config, request, request);
+        if (result == null) {
+            throw new SRUException(SRUConstants.SRU_UNSUPPORTED_OPERATION,
+                    "The 'scan' operation is not supported by this endpoint.");
+        }
+
+        try {
+            // send results
+            SRUXMLStreamWriter out =
+                    createXMLStreamWriter(response.getOutputStream(),
+                                          request.getRecordPacking(),
+                                          true,
+                                          request.getIndentResponse());
+
+            beginResponse(out, request);
+
+            try {
+                /*
+                 * a scan result without a list of terms is a valid response;
+                 * make sure, to produce the correct output and omit in that case
+                 * the <terms> ...
+                 */
+                boolean wroteTerms = false;
+                while (result.nextTerm()) {
+                    if (!wroteTerms) {
+                        out.writeStartElement(SRU_NS, "terms");
+                        wroteTerms = true;
+                    }
+                    out.writeStartElement(SRU_NS, "term");
+
+                    out.writeStartElement(SRU_NS, "value");
+                    out.writeCharacters(result.getValue());
+                    out.writeEndElement(); // "value" element
+
+                    if (result.getNumberOfRecords() > -1) {
+                        out.writeStartElement(SRU_NS, "numberOfRecords");
+                        out.writeCharacters(
+                                Integer.toString(result.getNumberOfRecords()));
+                        out.writeEndElement(); // "numberOfRecords" element
+                    }
+
+                    if (result.getDisplayTerm() != null) {
+                        out.writeStartElement(SRU_NS, "displayTerm");
+                        out.writeCharacters(result.getDisplayTerm());
+                        out.writeEndElement(); // "displayTerm" element
+                    }
+
+                    if (result.getWhereInList() != null) {
+                        out.writeStartElement(SRU_NS, "whereInList");
+                        switch (result.getWhereInList()) {
+                        case FIRST:
+                            out.writeCharacters("first");
+                            break;
+                        case LAST:
+                            out.writeCharacters("last");
+                            break;
+                        case ONLY:
+                            out.writeCharacters("only");
+                            break;
+                        case INNER:
+                            out.writeCharacters("inner");
+                            break;
+                        } // switch
+                        out.writeEndElement(); // "whereInList" element
+                    }
+
+                    if (result.hasExtraTermData()) {
+                        out.writeStartElement(SRU_NS, "extraTermData");
+                        result.writeExtraTermData(out);
+                        out.writeEndElement(); // "extraTermData" element
+                    }
+
+                    out.writeEndElement(); // "term" element
+                } // while
+                if (wroteTerms) {
+                    out.writeEndElement(); // "terms" element
+                }
+            } catch (NoSuchElementException e) {
+                throw new SRUException(SRUConstants.SRU_GENERAL_SYSTEM_ERROR,
+                        "An internal error occurred while "
+                                + "serializing scan results.");
+            }
+
+            // echoedScanRequest
+            if (config.getEchoRequests()) {
+                writeEchoedScanRequest(out, request, request.getScanClause());
+            }
+
+            // diagnostics
+            writeDiagnosticList(out, request.getDiagnostics());
+
+            // extraResponseData
+            if (result.hasExtraResponseData()) {
+                out.writeStartElement(SRU_NS, "extraResponseData");
+                result.writeExtraResponseData(out);
+                out.writeEndElement(); // "extraResponseData" element
+            }
+
+            endResponse(out);
+        } finally {
+            result.close();
+        }
+    }
+
+
+    private void search(SRURequestImpl request, HttpServletResponse response)
+            throws IOException, XMLStreamException, SRUException {
+        logger.info("searchRetrieve: query = \"{}\", startRecord = {}, " +
+                "maximumRecords = {}, recordSchema = {}, resultSetTTL = {}",
+                new Object[] { request.getRawQuery(), request.getStartRecord(),
+                        request.getMaximumRecords(),
+                        request.getRecordSchemaIdentifier(),
+                        request.getResultSetTTL() });
+
+        // commence search ...
+        final SRUSearchResultSet result =
+                searchEngine.search(config, request, request);
+        if (result == null) {
+            throw new SRUException(SRUConstants.SRU_GENERAL_SYSTEM_ERROR,
+                    "SRUSearchEngine implementation returned invalid result (null).");
+        }
+
+
+        // check, of startRecord position is greater than total record set
+        if ((result.getTotalRecordCount() >= 0) &&
+            (request.getStartRecord() > 1) &&
+            (request.getStartRecord() > result.getTotalRecordCount())) {
+            throw new SRUException(
+                    SRUConstants.SRU_FIRST_RECORD_POSITION_OUT_OF_RANGE);
+        }
+
+        try {
+            // send results
+            SRUXMLStreamWriter out =
+                    createXMLStreamWriter(response.getOutputStream(),
+                                          request.getRecordPacking(),
+                                          true,
+                                          request.getIndentResponse());
+
+            beginResponse(out, request);
+
+            // numberOfRecords
+            out.writeStartElement(SRU_NS, "numberOfRecords");
+            out.writeCharacters(
+                    Integer.toString(result.getTotalRecordCount()));
+            out.writeEndElement(); // "numberOfRecords" element
+
+            // resultSetId
+            if (result.getResultSetId() != null) {
+                out.writeStartElement(SRU_NS, "resultSetId");
+                out.writeCharacters(result.getResultSetId());
+                out.writeEndElement(); // "resultSetId" element
+            }
+
+            // resultSetIdleTime
+            if (result.getResultSetIdleTime() > 0) {
+                out.writeStartElement(SRU_NS, "resultSetIdleTime");
+                out.writeCharacters(Integer.toString(result
+                        .getResultSetIdleTime()));
+                out.writeEndElement(); // "resultSetIdleTime" element
+            }
+
+            int position = (request.getStartRecord() > 0)
+                    ? request.getStartRecord() : 1;
+            if (result.getRecordCount() > 0) {
+                final int maxPositionOffset =
+                        (request.getMaximumRecords() != -1)
+                        ? (position + request.getMaximumRecords() - 1)
+                        : -1;
+                try {
+                    out.writeStartElement(SRU_NS, "records");
+                    while (result.nextRecord()) {
+                        /*
+                         * Sanity check: do not return more then the maximum
+                         * requested records. If the search engine
+                         * implementation does not honor limit truncate the
+                         * result set.
+                         */
+                        if ((maxPositionOffset != -1) &&
+                                (position > maxPositionOffset)) {
+                            logger.error("SRUSearchEngine implementation did " +
+                                    "not honor limit for the amount of " +
+                                    "requsted records. Result set truncated!");
+                            break;
+                        }
+
+                        out.writeStartElement(SRU_NS, "record");
+
+                        /*
+                         * We need to output either the record or a surrogate
+                         * diagnostic. In case of the latter, we need to output
+                         * the appropriate record schema ...
+                         */
+                        final SRUDiagnostic diagnostic =
+                                result.getSurrogateDiagnostic();
+
+                        out.writeStartElement(SRU_NS, "recordSchema");
+                        if (diagnostic == null) {
+                            out.writeCharacters(
+                                    result.getRecordSchemaIdentifier());
+                        } else {
+                            out.writeCharacters(SRU_DIAGNOSTIC_RECORD_SCHEMA);
+                        }
+                        out.writeEndElement(); // "recordSchema" element
+
+                        // recordPacking
+                        writeRecordPacking(out, request.getRecordPacking());
+
+                        /*
+                         * Output either record data or surrogate diagnostic ...
+                         */
+                        out.writeStartElement(SRU_NS, "recordData");
+                        out.startRecord();
+                        if (diagnostic == null) {
+                            result.writeRecord(out);
+                        } else {
+                            // write a surrogate diagnostic
+                            writeDiagnostic(out, diagnostic, true);
+                        }
+                        out.endRecord();
+                        out.writeEndElement(); // "recordData" element
+
+                        /*
+                         * recordIdentifier is version 1.2 only
+                         */
+                        if (request.isVersion(SRUVersion.VERSION_1_2)) {
+                            final String identifier =
+                                    result.getRecordIdentifier();
+                            if (identifier != null) {
+                                out.writeStartElement(SRU_NS,
+                                                      "recordIdentifier");
+                                out.writeCharacters(identifier);
+                                out.writeEndElement(); // "recordIdentifier" element
+                            }
+                        }
+
+                        out.writeStartElement(SRU_NS, "recordPosition");
+                        out.writeCharacters(Integer.toString(position));
+                        out.writeEndElement(); // "recordPosition" element
+
+                        if (result.hasExtraRecordData()) {
+                            out.writeStartElement(SRU_NS, "extraRecordData");
+                            result.writeExtraRecordData(out);
+                            out.writeEndElement(); // "extraRecordData"
+                        }
+
+                        out.writeEndElement(); // "record" element
+
+                        position++;
+                    } // while
+                    out.writeEndElement(); // "records" element
+                } catch (NoSuchElementException e) {
+                    throw new SRUException(
+                            SRUConstants.SRU_GENERAL_SYSTEM_ERROR,
+                            "An internal error occurred while " +
+                            "serializing search result set.");
+                }
+            }
+
+            // nextRecordPosition
+            if (position <= result.getTotalRecordCount()) {
+                out.writeStartElement(SRU_NS, "nextRecordPosition");
+                out.writeCharacters(Integer.toString(position));
+                out.writeEndElement();
+            }
+
+            // echoedSearchRetrieveRequest
+            if (config.getEchoRequests()) {
+                writeEchoedSearchRetrieveRequest(out, request,
+                                                 request.getQuery());
+            }
+
+            // diagnostics
+            writeDiagnosticList(out, request.getDiagnostics());
+
+            // extraResponseData
+            if (result.hasExtraResponseData()) {
+                out.writeStartElement(SRU_NS, "extraResponseData");
+                result.writeExtraResponseData(out);
+                out.writeEndElement(); // "extraResponseData" element
+            }
+
+            endResponse(out);
+        } finally {
+            result.close();
+        }
+    }
+
+
+    private void beginResponse(SRUXMLStreamWriter out, SRUOperation operation,
+            SRUVersion version, String stylesheet) throws XMLStreamException {
+        out.writeStartDocument("utf-8", "1.0");
+
+        if (stylesheet != null) {
+            StringBuilder param = new StringBuilder();
+            param.append("type=\"text/xsl\"");
+            param.append(" ");
+            param.append("href=\"");
+            param.append(stylesheet);
+            param.append("\"");
+            out.writeProcessingInstruction("xml-stylesheet", param.toString());
+        }
+
+        out.setPrefix(SRU_PREFIX, SRU_NS);
+        switch (operation) {
+        case EXPLAIN:
+            out.writeStartElement(SRU_NS, "explainResponse");
+            break;
+        case SCAN:
+            out.writeStartElement(SRU_NS, "scanResponse");
+            break;
+        case SEARCH_RETRIEVE:
+            out.writeStartElement(SRU_NS, "searchRetrieveResponse");
+            break;
+        }
+        out.writeNamespace(SRU_PREFIX, SRU_NS);
+
+        // version
+        writeVersion(out, version);
+    }
+
+
+    private void beginResponse(SRUXMLStreamWriter out, SRURequest request)
+            throws XMLStreamException {
+        beginResponse(out, request.getOperation(), request.getVersion(),
+                request.getStylesheet());
+    }
+
+
+    private void endResponse(SRUXMLStreamWriter out)
+            throws XMLStreamException {
+        out.writeEndElement(); // "root" element
+
+        out.writeEndDocument();
+        out.close();
+        try {
+            out.getWriter().close();
+        } catch (IOException e) {
+            /* IGNORE */
+        }
+    }
+
+
+    private void writeFatalError(SRUXMLStreamWriter out,
+            SRURequestImpl request, List<SRUDiagnostic> diagnotics)
+            throws XMLStreamException {
+        /*
+         * if operation is unknown, default to 'explain'
+         */
+        SRUOperation operation = request.getOperation();
+        if (operation == null) {
+            operation = SRUOperation.EXPLAIN;
+        }
+        SRUVersion version = request.getVersion();
+        if (version == null) {
+            version = config.getDefaultVersion();
+        }
+        /*
+         * write a response which conforms to the schema
+         */
+        beginResponse(out, operation, version, null);
+        switch (operation) {
+        case EXPLAIN:
+            // 'explain' requires a complete explain record ...
+            writeExplainRecord(out, request);
+            break;
+        case SCAN:
+            // 'scan' fortunately does not need any elements ...
+            break;
+        case SEARCH_RETRIEVE:
+            // 'searchRetrieve' needs numberOfRecords ..
+            out.writeStartElement(SRU_NS, "numberOfRecords");
+            out.writeCharacters("0");
+            out.writeEndElement(); // "numberOfRecords" element
+            break;
+        }
+        writeDiagnosticList(out, diagnotics);
+        endResponse(out);
+    }
+
+
+    private void writeDiagnosticList(SRUXMLStreamWriter out,
+            List<SRUDiagnostic> diagnostics) throws XMLStreamException {
+        if ((diagnostics != null) && !diagnostics.isEmpty()) {
+            out.setPrefix(SRU_DIAGNOSTIC_PREFIX, SRU_DIAGNOSIC_NS);
+            out.writeStartElement(SRU_NS, "diagnostics");
+            out.writeNamespace(SRU_DIAGNOSTIC_PREFIX, SRU_DIAGNOSIC_NS);
+            for (SRUDiagnostic diagnostic : diagnostics) {
+                writeDiagnostic(out, diagnostic, false);
+            }
+            out.writeEndElement(); // "diagnostics" element
+        }
+    }
+
+
+    private void writeExplainRecord(SRUXMLStreamWriter out,
+            SRURequestImpl request) throws XMLStreamException {
+        out.writeStartElement(SRU_NS, "record");
+
+        out.writeStartElement(SRU_NS, "recordSchema");
+        out.writeCharacters(SRU_EXPLAIN_NS);
+        out.writeEndElement(); // "recordSchema" element
+
+        // recordPacking
+        writeRecordPacking(out, request.getRecordPacking());
+
+        out.writeStartElement(SRU_NS, "recordData");
+
+        out.startRecord();
+
+        // explain ...
+        out.setPrefix(SRU_EXPLAIN_PREFIX, SRU_EXPLAIN_NS);
+        out.writeStartElement(SRU_EXPLAIN_NS, "explain");
+        out.writeNamespace(SRU_EXPLAIN_PREFIX, SRU_EXPLAIN_NS);
+
+        // explain/serverInfo
+        out.writeStartElement(SRU_EXPLAIN_NS, "serverInfo");
+        out.writeAttribute("protocol", "SRU");
+        switch (config.getDefaultVersion()) {
+        case VERSION_1_1:
+            out.writeAttribute("version", "1.1");
+            break;
+        case VERSION_1_2:
+            out.writeAttribute("version", "1.2");
+        } // switch
+        out.writeAttribute("transport", config.getTransports());
+        out.writeStartElement(SRU_EXPLAIN_NS, "host");
+        out.writeCharacters(config.getHost());
+        out.writeEndElement(); // "host" element
+        out.writeStartElement(SRU_EXPLAIN_NS, "port");
+        out.writeCharacters(Integer.toString(config.getPort()));
+        out.writeEndElement(); // "port" element
+        out.writeStartElement(SRU_EXPLAIN_NS, "database");
+        out.writeCharacters(config.getDatabase());
+        out.writeEndElement(); // "database" element
+        out.writeEndElement(); // "serverInfo" element
+
+        // explain/databaseInfo
+        final DatabaseInfo dbinfo = config.getDatabaseInfo();
+        if (dbinfo != null) {
+            out.writeStartElement(SRU_EXPLAIN_NS, "databaseInfo");
+            writeLocalizedStrings(out, "title", dbinfo.getTitle());
+            writeLocalizedStrings(out, "description", dbinfo.getDescription());
+            writeLocalizedStrings(out, "author", dbinfo.getAuthor());
+            writeLocalizedStrings(out, "extent", dbinfo.getExtend());
+            writeLocalizedStrings(out, "history", dbinfo.getHistory());
+            writeLocalizedStrings(out, "langUsage", dbinfo.getLangUsage());
+            writeLocalizedStrings(out, "restrictions", dbinfo.getRestrictions());
+            writeLocalizedStrings(out, "subjects", dbinfo.getSubjects());
+            writeLocalizedStrings(out, "links", dbinfo.getLinks());
+            writeLocalizedStrings(out, "implementation",
+                    dbinfo.getImplementation());
+            out.writeEndElement(); // "databaseInfo" element
+        }
+
+        // explain/indexInfo
+        final IndexInfo indexInfo = config.getIndexInfo();
+        if (indexInfo != null) {
+            out.writeStartElement(SRU_EXPLAIN_NS, "indexInfo");
+
+            List<IndexInfo.Set> sets = indexInfo.getSets();
+            if (sets != null) {
+                for (IndexInfo.Set set : sets) {
+                    out.writeStartElement(SRU_EXPLAIN_NS, "set");
+                    out.writeAttribute("identifier", set.getIdentifier());
+                    out.writeAttribute("name", set.getName());
+                    writeLocalizedStrings(out, "title", set.getTitle());
+                    out.writeEndElement(); // "set" element
+                }
+            }
+
+            List<IndexInfo.Index> indexes = indexInfo.getIndexes();
+            if (indexes != null) {
+                for (IndexInfo.Index index : indexes) {
+                    out.writeStartElement(SRU_EXPLAIN_NS, "index");
+                    out.writeAttribute("search",
+                            index.canSearch() ? "true" : "false");
+                    out.writeAttribute("scan",
+                            index.canScan() ? "true" : "false");
+                    out.writeAttribute("sort",
+                            index.canSort() ? "true" : "false");
+                    writeLocalizedStrings(out, "title", index.getTitle());
+                    List<IndexInfo.Index.Map> maps = index.getMaps();
+                    if (maps != null) {
+                        for (IndexInfo.Index.Map map : maps) {
+                            out.writeStartElement(SRU_EXPLAIN_NS, "map");
+                            if (map.isPrimary()) {
+                                out.writeAttribute("primary", "true");
+                            }
+                            out.writeStartElement(SRU_EXPLAIN_NS, "name");
+                            out.writeAttribute("set", map.getSet());
+                            out.writeCharacters(map.getName());
+                            out.writeEndElement(); // "name" element
+                            out.writeEndElement(); // "map" element
+                        }
+                    }
+                    out.writeEndElement(); // "index" element
+                }
+            }
+            out.writeEndElement(); // "indexInfo" element
+        }
+
+        // explain/schemaInfo
+        final List<SchemaInfo> schemaInfo =
+                config.getSchemaInfo();
+        if (schemaInfo != null) {
+            out.writeStartElement(SRU_EXPLAIN_NS, "schemaInfo");
+            for (SRUServerConfig.SchemaInfo schema : schemaInfo) {
+                out.writeStartElement(SRU_EXPLAIN_NS, "schema");
+                out.writeAttribute("identifier", schema.getIdentifier());
+                out.writeAttribute("name", schema.getName());
+                /*
+                 * default is "false", so only add attribute if set to true
+                 */
+                if (schema.getSort() ) {
+                    out.writeAttribute("sort", "true");
+                }
+                /*
+                 * default is "true", so only add attribute if set to false
+                 */
+                if (!schema.getRetrieve()) {
+                    out.writeAttribute("retrieve", "false");
+                }
+                writeLocalizedStrings(out, "title", schema.getTitle());
+                out.writeEndElement(); // "schema" element
+            }
+            out.writeEndElement(); // "schemaInfo" element
+        }
+
+        // explain/configInfo
+        out.writeStartElement(SRU_EXPLAIN_NS, "configInfo");
+        // numberOfRecords (default)
+        out.writeStartElement(SRU_EXPLAIN_NS, "default");
+        out.writeAttribute("type", "numberOfRecords");
+        out.writeCharacters(Integer.toString(config.getNumberOfRecords()));
+        out.writeEndElement(); // default" element
+
+        // maximumRecords (setting)
+        out.writeStartElement(SRU_EXPLAIN_NS, "setting");
+        out.writeAttribute("type", "maximumRecords");
+        out.writeCharacters(Integer.toString(config.getMaximumRecords()));
+        out.writeEndElement(); // "setting" element
+
+        out.writeEndElement(); // "configInfo" element
+
+        out.writeEndElement(); // "explain" element
+
+        out.endRecord();
+
+        out.writeEndElement(); // "recordData" element
+        out.writeEndElement(); // "record" element
+    }
+
+
+    private void writeDiagnostic(SRUXMLStreamWriter out,
+            SRUDiagnostic diagnostic, boolean writeNsDecl)
+            throws XMLStreamException {
+        if (writeNsDecl) {
+            out.setPrefix(SRU_DIAGNOSTIC_PREFIX, SRU_DIAGNOSIC_NS);
+        }
+        out.writeStartElement(SRU_DIAGNOSIC_NS, "diagnostic");
+        if (writeNsDecl) {
+            out.writeNamespace(SRU_DIAGNOSTIC_PREFIX, SRU_DIAGNOSIC_NS);
+        }
+        out.writeStartElement(SRU_DIAGNOSIC_NS, "uri");
+        out.writeCharacters(diagnostic.getURI());
+        out.writeEndElement(); // "uri" element
+        if (diagnostic.getDetails() != null) {
+            out.writeStartElement(SRU_DIAGNOSIC_NS, "details");
+            out.writeCharacters(diagnostic.getDetails());
+            out.writeEndElement(); // "details" element
+        }
+        if (diagnostic.getMessage() != null) {
+            out.writeStartElement(SRU_DIAGNOSIC_NS, "message");
+            out.writeCharacters(diagnostic.getMessage());
+            out.writeEndElement(); // "message" element
+        }
+        out.writeEndElement(); // "diagnostic" element
+    }
+
+
+    private void writeEchoedExplainRequest(SRUXMLStreamWriter out,
+            SRURequestImpl request) throws XMLStreamException,
+            SRUException {
+        // echoedSearchRetrieveRequest
+        out.writeStartElement(SRU_NS, "echoedExplainRequest");
+
+        // echoedExplainRequest/version
+        if (request.getRawVersion() != null) {
+            writeVersion(out, request.getRawVersion());
+        }
+
+        // echoedExplainRequest/recordPacking
+        if (request.getRawRecordPacking() != null) {
+            writeRecordPacking(out, request.getRawRecordPacking());
+        }
+
+        // echoedExplainRequest/stylesheet
+        if (request.getStylesheet() != null) {
+            out.writeStartElement(SRU_NS, "stylesheet");
+            out.writeCharacters(request.getStylesheet());
+            out.writeEndElement(); // "stylesheet" element
+        }
+
+        // echoedExplainRequest/baseUrl (SRU 1.2 only)
+        if (request.isVersion(SRUVersion.VERSION_1_2)) {
+            writeBaseUrl(out, request);
+        }
+
+        out.writeEndElement(); // "echoedExplainRequest" element
+    }
+
+
+    private void writeEchoedScanRequest(SRUXMLStreamWriter out,
+            SRURequestImpl request, CQLNode cql) throws XMLStreamException,
+            SRUException {
+        // echoedScanRequest
+        out.writeStartElement(SRU_NS, "echoedScanRequest");
+
+        // echoedScanRequest/version
+        if (request.getRawVersion() != null) {
+            writeVersion(out, request.getRawVersion());
+        }
+
+        // echoedScanRequest/scanClause
+        out.writeStartElement(SRU_NS, "scanClause");
+        out.writeCharacters(request.getRawScanClause());
+        out.writeEndElement(); // "query"
+
+        // echoedScanRequest/xScanClause
+        out.setDefaultNamespace(SRU_XCQL_NS);
+        out.writeStartElement(SRU_NS, "xScanClause");
+        out.writeDefaultNamespace(SRU_XCQL_NS);
+        out.writeXCQL(cql, false);
+        out.writeEndElement(); // "xScanClause" element
+
+        // echoedScanRequest/responsePosition
+        if (request.getResponsePosition() != -1) {
+            out.writeStartElement(SRU_NS, "responsePosition");
+            out.writeCharacters(
+                    Integer.toString(request.getResponsePosition()));
+            out.writeEndElement(); // "responsePosition" element
+        }
+
+        // echoedScanRequest/maximumTerms
+        if (request.getMaximumTerms() != -1) {
+            out.writeStartElement(SRU_NS, "maximumTerms");
+            out.writeCharacters(Integer.toString(request.getMaximumTerms()));
+            out.writeEndElement(); // "maximumTerms" element
+        }
+
+        // echoedScanRequest/stylesheet
+        if (request.getStylesheet() != null) {
+            out.writeStartElement(SRU_NS, "stylesheet");
+            out.writeCharacters(request.getStylesheet());
+            out.writeEndElement(); // "stylesheet" element
+        }
+
+        // echoedScanRequest/baseUrl (SRU 1.2 only)
+        if (request.isVersion(SRUVersion.VERSION_1_2)) {
+            writeBaseUrl(out, request);
+        }
+
+        out.writeEndElement(); // "echoedScanRequest" element
+    }
+
+
+    private void writeEchoedSearchRetrieveRequest(SRUXMLStreamWriter out,
+            SRURequestImpl request, CQLNode cql) throws XMLStreamException,
+            SRUException {
+        // echoedSearchRetrieveRequest
+        out.writeStartElement(SRU_NS, "echoedSearchRetrieveRequest");
+
+        // echoedSearchRetrieveRequest/version
+        if (request.getRawVersion() != null) {
+            writeVersion(out, request.getRawVersion());
+        }
+
+        // echoedSearchRetrieveRequest/query
+        out.writeStartElement(SRU_NS, "query");
+        out.writeCharacters(request.getRawQuery());
+        out.writeEndElement(); // "query"
+
+        // echoedSearchRetrieveRequest/xQuery
+        out.setDefaultNamespace(SRU_XCQL_NS);
+        out.writeStartElement(SRU_NS, "xQuery");
+        out.writeDefaultNamespace(SRU_XCQL_NS);
+        out.writeXCQL(cql, true);
+        out.writeEndElement(); // "xQuery" element
+
+        // echoedSearchRetrieveRequest/startRecord
+        if (request.getStartRecord() > 0) {
+            out.writeStartElement(SRU_NS, "startRecord");
+            out.writeCharacters(Integer.toString(request.getStartRecord()));
+            out.writeEndElement(); // "startRecord" element
+        }
+
+        // echoedSearchRetrieveRequest/maximumRecords
+        if (request.getRawMaximumRecords() > 0) {
+            out.writeStartElement(SRU_NS, "maximumRecords");
+            out.writeCharacters(
+                    Integer.toString(request.getRawMaximumRecords()));
+            out.writeEndElement(); // "startRecord" element
+        }
+
+        // echoedSearchRetrieveRequest/recordPacking
+        if (request.getRawRecordPacking() != null) {
+            writeRecordPacking(out, request.getRawRecordPacking());
+        }
+
+        // echoedSearchRetrieveRequest/recordSchema
+        if (request.getRawRecordSchemaIdentifier() != null) {
+            out.writeStartElement(SRU_NS, "recordSchema");
+            out.writeCharacters(request.getRawRecordSchemaIdentifier());
+            out.writeEndElement(); // "recordSchema" element
+        }
+
+        // echoedSearchRetrieveRequest/recordXPath (1.1)
+        if (request.isVersion(SRUVersion.VERSION_1_1) &&
+                (request.getRecordXPath() != null)) {
+            out.writeStartElement(SRU_NS, "recordXPath");
+            out.writeCharacters(request.getRecordXPath());
+            out.writeEndElement(); // "recordXPath" element
+        }
+
+        // echoedSearchRetrieveRequest/resultSetTTL
+        if (request.getResultSetTTL() > 0) {
+            out.writeStartElement(SRU_NS, "resultSetTTL");
+            out.writeCharacters(Long.toString(request.getResultSetTTL()));
+            out.writeEndElement(); // "resultSetTTL" element
+        }
+
+        // echoedSearchRetrieveRequest/sortKeys
+        if (request.isVersion(SRUVersion.VERSION_1_1) &&
+                (request.getSortKeys() != null)) {
+            out.writeStartElement(SRU_NS, "sortKeys");
+            out.writeCharacters(request.getSortKeys());
+            out.writeEndElement(); // "sortKeys" element
+        }
+
+        // echoedSearchRetrieveRequest/xsortKeys
+
+        // echoedSearchRetrieveRequest/stylesheet
+        if (request.getStylesheet() != null) {
+            out.writeStartElement(SRU_NS, "stylesheet");
+            out.writeCharacters(request.getStylesheet());
+            out.writeEndElement(); // "stylesheet" element
+        }
+
+        // echoedSearchRetrieveRequest/baseUrl (SRU 1.2 only)
+        if (request.isVersion(SRUVersion.VERSION_1_2)) {
+            writeBaseUrl(out, request);
+        }
+
+        out.writeEndElement(); // "echoedSearchRetrieveRequest" element
+    }
+
+
+    private void writeVersion(SRUXMLStreamWriter out, SRUVersion version)
+            throws XMLStreamException {
+        out.writeStartElement(SRU_NS, "version");
+        switch (version) {
+        case VERSION_1_1:
+            out.writeCharacters("1.1");
+            break;
+        case VERSION_1_2:
+            out.writeCharacters("1.2");
+            break;
+        } // switch
+        out.writeEndElement(); // "version" element
+    }
+
+
+    private void writeRecordPacking(SRUXMLStreamWriter out,
+            SRURecordPacking recordPacking) throws XMLStreamException {
+        out.writeStartElement(SRU_NS, "recordPacking");
+        switch (recordPacking) {
+        case XML:
+            out.writeCharacters("xml");
+            break;
+        case STRING:
+            out.writeCharacters("string");
+            break;
+        } // switch
+        out.writeEndElement(); // "recordPacking" element
+    }
+
+
+    private void writeBaseUrl(SRUXMLStreamWriter out,
+            SRURequest request) throws XMLStreamException {
+        out.writeStartElement(SRU_NS, "baseUrl");
+        out.writeCharacters(request.getProtocolScheme());
+        out.writeCharacters(config.getBaseUrl());
+        out.writeEndElement(); // "baseUrl" element
+    }
+
+
+    private void writeLocalizedStrings(XMLStreamWriter writer, String name,
+            List<LocalizedString> list) throws XMLStreamException {
+        if ((list != null) && !list.isEmpty()) {
+            for (LocalizedString item : list) {
+                writer.writeStartElement(SRU_EXPLAIN_NS, name);
+                if (item.getLang() != null) {
+                    writer.writeAttribute("lang", item.getLang());
+                }
+                if (item.isPrimary()) {
+                    writer.writeAttribute("primary", "true");
+                }
+                writer.writeCharacters(item.getValue());
+                writer.writeEndElement();
+            }
+        }
+    }
+
+
+    private SRUXMLStreamWriter createXMLStreamWriter(OutputStream out,
+            SRURecordPacking recordPacking, boolean skipFlush, int indent)
+            throws SRUException {
+        try {
+            if (skipFlush) {
+                /*
+                 * Add a FilterOutputStream to delay flush() as long as
+                 * possible. Doing so, enabled us to send an appropriate SRU
+                 * diagnostic in case an error occurs during the serialization
+                 * of the response.
+                 * Of course, if an error occurs when the Servlet response
+                 * buffer already had been flushed, because it was to large,
+                 * we cannot fail gracefully and we will produce ill-formed
+                 * XML output.
+                 */
+                out = new FilterOutputStream(out) {
+                    @Override
+                    public void flush() throws IOException {
+                    }
+
+
+                    @Override
+                    public void close() throws IOException {
+                        super.flush();
+                        super.close();
+                    }
+                };
+            }
+            return new SRUXMLStreamWriter(out, writerFactory,
+                    recordPacking, indent);
+        } catch (Exception e) {
+            throw new SRUException(SRUConstants.SRU_GENERAL_SYSTEM_ERROR,
+                    "Error creating output stream.", e);
+
+        }
+    }
+
+} // class SRUService
Index: SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUServerConfig.java
===================================================================
--- SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUServerConfig.java	(revision 5925)
+++ SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUServerConfig.java	(revision 5925)
@@ -0,0 +1,1348 @@
+/**
+ * This software is copyright (c) 2011-2013 by
+ *  - Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ * This is free software. You can redistribute it
+ * and/or modify it under the terms described in
+ * the GNU General Public License v3 of which you
+ * should have received a copy. Otherwise you can download
+ * it from
+ *
+ *   http://www.gnu.org/licenses/gpl-3.0.txt
+ *
+ * @copyright Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ *
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt
+ *  GNU General Public License v3
+ */
+package eu.clarin.sru.server;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import javax.xml.XMLConstants;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.Source;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import javax.xml.validation.Validator;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathException;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
+
+/**
+ * SRU server configuration.
+ *
+ * <p>
+ * Example:
+ * </p>
+ * <pre>
+ * URL url = MySRUServlet.class.getClassLoader()
+ *               .getResource("META-INF/sru-server-config.xml");
+ * if (url == null) {
+ *     throw new ServletException("not found, url == null");
+ * }
+ *
+ * // other runtime configuration, usually obtained from Servlet context
+ * HashMap&lt;String, String&gt; params = new HashMap&lt;String, String&gt;();
+ * params.put(SRUServerConfig.SRU_TRANSPORT, "http");
+ * params.put(SRUServerConfig.SRU_HOST, "127.0.0.1");
+ * params.put(SRUServerConfig.SRU_PORT, "80");
+ * params.put(SRUServerConfig.SRU_DATABASE, "sru-server");
+ *
+ * SRUServerConfig config = SRUServerConfig.parse(params, url);
+ * </pre>
+ *
+ * <p>
+ * The XML configuration file must validate against the "sru-server-config.xsd"
+ * W3C schema bundled with the package and need to have the
+ * <code>http://www.clarin.eu/sru-server/1.0/</code> XML namespace.
+ * </p>
+ */
+public final class SRUServerConfig {
+    /**
+     * Parameter constant for configuring the transports for this SRU server.
+     * <p>
+     * Valid values: "<code>http</code>", "<code>https</code>" or "
+     * <code>http https</code>" (without quotation marks) <br />
+     * <p>
+     * Used as part of the <em>Explain</em> response.
+     * </p>
+     */
+    public static final String SRU_TRANSPORT =
+            "eu.clarin.sru.server.transport";
+    /**
+     * Parameter constant for configuring the host of this SRU server.
+     * <p>
+     * Valid values: any fully qualified hostname, e.g.
+     * <code>sru.example.org</code> <br />
+     * Used as part of the <em>Explain</em> response.
+     * </p>
+     */
+    public static final String SRU_HOST =
+            "eu.clarin.sru.server.host";
+    /**
+     * Parameter constant for configuring the port number of this SRU server.
+     * <p>
+     * Valid values: number between 1 and 65535 (typically 80 or 8080) <br />
+     * Used as part of the <em>Explain</em> response.
+     * </p>
+     */
+    public static final String SRU_PORT =
+            "eu.clarin.sru.server.port";
+    /**
+     * Parameter constant for configuring the database of this SRU server. This
+     * is usually the path component of the SRU servers URI.
+     * <p>
+     * Valid values: typically the path component if the SRU server URI. <br />
+     * Used as part of the <em>Explain</em> response.
+     * </p>
+     */
+    public static final String SRU_DATABASE =
+            "eu.clarin.sru.server.database";
+    /**
+     * Parameter constant for configuring the <em>default</em> number of records
+     * the SRU server will provide in the response to a <em>searchRetrieve</em>
+     * request if the client does not provide this value.
+     * <p>
+     * Valid values: a integer greater than 0 (default value is 100)
+     * </p>
+     */
+    public static final String SRU_NUMBER_OF_RECORDS =
+            "eu.clarin.sru.server.numberOfRecords";
+    /**
+     * Parameter constant for configuring the <em>maximum</em> number of records
+     * the SRU server will support in the response to a <em>searchRetrieve</em>
+     * request. If a client requests more records, the number will be limited to
+     * this value.
+     * <p>
+     * Valid values: a integer greater than 0 (default value is 250)
+     * </p>
+     */
+    public static final String SRU_MAXIMUM_RECORDS =
+            "eu.clarin.sru.server.maximumRecords";
+    /**
+     * Parameter constant for configuring the <em>default</em> number of terms
+     * the SRU server will provide in the response to a <em>scan</em> request if
+     * the client does not provide this value.
+     * <p>
+     * Valid values: a integer greater than 0 (default value is 250)
+     * </p>
+     */
+    public static final String SRU_NUMBER_OF_TERMS =
+            "eu.clarin.sru.server.numberOfTerms";
+    /**
+     * Parameter constant for configuring the <em>maximum</em> number of terms
+     * the SRU server will support in the response to a <em>scan</em> request.
+     * If a client requests more records, the number will be limited to this
+     * value.
+     * <p>
+     * Valid values: a integer greater than 0 (default value is 500)
+     * </p>
+     */
+    public static final String SRU_MAXIMUM_TERMS =
+            "eu.clarin.sru.server.maximumTerms";
+    /**
+     * Parameter constant for configuring, if the SRU server will echo the
+     * request.
+     * <p>
+     * Valid values: <code>true</code> or <code>false</code>
+     * </p>
+     */
+    public static final String SRU_ECHO_REQUESTS =
+            "eu.clarin.sru.server.echoRequests";
+    /**
+     * Parameter constant for configuring, if the SRU server pretty-print the
+     * XML response. Setting this parameter can be useful for manual debugging
+     * of the XML response, however it is <em>not recommended</em> for
+     * production setups.
+     * <p>
+     * Valid values: any integer greater or equal to <code>-1</code> (default)
+     * and less or equal to <code>8</code>
+     * </p>
+     */
+    public static final String SRU_INDENT_RESPONSE =
+            "eu.clarin.sru.server.indentResponse";
+    /**
+     * Parameter constant for configuring, if the SRU server will allow the
+     * client to override the maximum number of records the server supports.
+     * This parameter is solely intended for debugging and setting it to
+     * <code>true</code> is <em>strongly</em> discouraged for production setups.
+     * <p>
+     * Valid values: <code>true</code> or <code>false</code> (default)
+     * </p>
+     */
+    public static final String SRU_ALLOW_OVERRIDE_MAXIMUM_RECORDS =
+            "eu.clarin.sru.server.allowOverrideMaximumRecords";
+    /**
+     * Parameter constant for configuring, if the SRU server will allow the
+     * client to override the maximum number of terms the server supports. This
+     * parameter is solely intended for debugging and setting it to
+     * <code>true</code> it is <em>strongly</em> discouraged for production
+     * setups.
+     * <p>
+     * Valid values: <code>true</code> or <code>false</code> (default)
+     * </p>
+     */
+    public static final String SRU_ALLOW_OVERRIDE_MAXIMUM_TERMS =
+            "eu.clarin.sru.server.allowOverrideMaximumTerms";
+    /**
+     * Parameter constant for configuring, if the SRU server will allow the
+     * client to override the pretty-printing setting of the server. This
+     * parameter is solely intended for debugging and setting it to
+     * <code>true</code> it is <em>strongly</em> discouraged for production
+     * setups.
+     * <p>
+     * Valid values: <code>true</code> or <code>false</code> (default)
+     * </p>
+     */
+    public static final String SRU_ALLOW_OVERRIDE_INDENT_RESPONSE =
+            "eu.clarin.sru.server.allowOverrideIndentResponse";
+    /**
+     * Parameter constant for configuring the size of response buffer. The
+     * Servlet will buffer up to this amount of data before sending a response
+     * to the client. This value specifies the size of the buffer in bytes.
+     * <p>
+     * Valid values: any positive integer (default 65536)
+     * </p>
+     */
+    public static final String SRU_RESPONSE_BUFFER_SIZE =
+            "eu.clarin.sru.server.responseBufferSize";
+    /**
+     * @deprecated use {@link #SRU_TRANSPORT}
+     */
+    @Deprecated
+    private static final String LEGACY_SRU_TRANSPORT =
+            "sru.transport";
+    /**
+     * @deprecated use {@link #SRU_HOST}
+     */
+    @Deprecated
+    private static final String LEGACY_SRU_HOST =
+            "sru.host";
+    /**
+     * @deprecated use {@link #SRU_PORT}
+     */
+    @Deprecated
+    private static final String LEGACY_SRU_PORT =
+            "sru.port";
+    /**
+     * @deprecated use {@link #SRU_DATABASE}
+     */
+    @Deprecated
+    private static final String LEGACY_SRU_DATABASE =
+            "sru.database";
+    /**
+     * @deprecated use {@link #SRU_NUMBER_OF_RECORDS}
+     */
+    @Deprecated
+    private static final String LEGACY_SRU_NUMBER_OF_RECORDS =
+            "sru.numberOfRecords";
+    /**
+     * @deprecated use {@link #SRU_MAXIMUM_RECORDS}
+     */
+    @Deprecated
+    private static final String LEGACY_SRU_MAXIMUM_RECORDS =
+            "sru.maximumRecords";
+    /**
+     * @deprecated use {@link #SRU_ECHO_REQUESTS}
+     */
+    @Deprecated
+    private static final String LEGACY_SRU_ECHO_REQUESTS =
+            "sru.echoRequests";
+    /**
+     * @deprecated use {@link #SRU_INDENT_RESPONSE}
+     */
+    @Deprecated
+    private static final String LEGACY_SRU_INDENT_RESPONSE =
+            "sru.indentResponse";
+    /**
+     * @deprecated use {@link #SRU_ALLOW_OVERRIDE_MAXIMUM_RECORDS}
+     */
+    @Deprecated
+    private static final String LEGACY_SRU_ALLOW_OVERRIDE_MAXIMUM_RECORDS =
+            "sru.allowOverrideMaximumRecords";
+    /**
+     * @deprecated use {@link #SRU_ALLOW_OVERRIDE_INDENT_RESPONSE}
+     */
+    @Deprecated
+    private static final String LEGACY_SRU_ALLOW_OVERRIDE_INDENT_RESPONSE =
+            "sru.allowOverrideIndentResponse";
+    private static final int DEFAULT_NUMBER_OF_RECORDS    = 100;
+    private static final int DEFAULT_MAXIMUM_RECORDS      = 250;
+    private static final int DEFAULT_NUMBER_OF_TERMS      = 250;
+    private static final int DEFAULT_MAXIMUM_TERMS        = 500;
+    private static final int DEFAULT_RESPONSE_BUFFER_SIZE = 64 * 1024;
+    private static final String CONFIG_FILE_NAMESPACE_URI =
+            "http://www.clarin.eu/sru-server/1.0/";
+    private static final String CONFIG_FILE_SCHEMA_URL =
+            "META-INF/sru-server-config.xsd";
+
+    public static final class LocalizedString {
+        private final boolean primary;
+        private final String lang;
+        private final String value;
+
+        private LocalizedString(String value, String lang, boolean primary) {
+            this.value   = value;
+            this.lang    = lang;
+            this.primary = primary;
+        }
+
+        private LocalizedString(String value, String lang) {
+            this(value, lang, false);
+        }
+
+        public boolean isPrimary() {
+            return primary;
+        }
+
+        public String getLang() {
+            return lang;
+        }
+
+        public String getValue() {
+            return value;
+        }
+    } // class LocalizedString
+
+    public static final class DatabaseInfo {
+        private final List<LocalizedString> title;
+        private final List<LocalizedString> description;
+        private final List<LocalizedString> author;
+        private final List<LocalizedString> extent;
+        private final List<LocalizedString> history;
+        private final List<LocalizedString> langUsage;
+        private final List<LocalizedString> restrictions;
+        private final List<LocalizedString> subjects;
+        private final List<LocalizedString> links;
+        private final List<LocalizedString> implementation;
+
+        private DatabaseInfo(List<LocalizedString> title,
+                List<LocalizedString> description,
+                List<LocalizedString> author, List<LocalizedString> extent,
+                List<LocalizedString> history, List<LocalizedString> langUsage,
+                List<LocalizedString> restrictions,
+                List<LocalizedString> subjects, List<LocalizedString> links,
+                List<LocalizedString> implementation) {
+            if ((title != null) && !title.isEmpty()) {
+                this.title = Collections.unmodifiableList(title);
+            } else {
+                this.title = null;
+            }
+            if ((description != null) && !description.isEmpty()) {
+                this.description = Collections.unmodifiableList(description);
+            } else {
+                this.description = null;
+            }
+            if ((author != null) && !author.isEmpty()) {
+                this.author = Collections.unmodifiableList(author);
+            } else {
+                this.author = null;
+            }
+            if ((extent != null) && !extent.isEmpty()) {
+                this.extent = Collections.unmodifiableList(extent);
+            } else {
+                this.extent = null;
+            }
+            if ((history != null) && !history.isEmpty()) {
+                this.history = Collections.unmodifiableList(history);
+            } else {
+                this.history = null;
+            }
+            if ((langUsage != null) && !langUsage.isEmpty()) {
+                this.langUsage = Collections.unmodifiableList(langUsage);
+            } else {
+                this.langUsage = null;
+            }
+            if ((restrictions != null) && !restrictions.isEmpty()) {
+                this.restrictions = Collections.unmodifiableList(restrictions);
+            } else {
+                this.restrictions = null;
+            }
+            if ((subjects != null) && !subjects.isEmpty()) {
+                this.subjects = Collections.unmodifiableList(subjects);
+            } else {
+                this.subjects = null;
+            }
+            if ((links != null) && !links.isEmpty()) {
+                this.links = Collections.unmodifiableList(links);
+            } else {
+                this.links = null;
+            }
+            if ((implementation != null) && !implementation.isEmpty()) {
+                this.implementation =
+                        Collections.unmodifiableList(implementation);
+            } else {
+                this.implementation = null;
+            }
+        }
+
+        public List<LocalizedString> getTitle() {
+            return title;
+        }
+
+        public List<LocalizedString> getDescription() {
+            return description;
+        }
+
+        public List<LocalizedString> getAuthor() {
+            return author;
+        }
+
+        public List<LocalizedString> getExtend() {
+            return extent;
+        }
+
+        public List<LocalizedString> getHistory() {
+            return history;
+        }
+
+        public List<LocalizedString> getLangUsage() {
+            return langUsage;
+        }
+
+        public List<LocalizedString> getRestrictions() {
+            return restrictions;
+        }
+
+        public List<LocalizedString> getSubjects() {
+            return subjects;
+        }
+
+        public List<LocalizedString> getLinks() {
+            return links;
+        }
+
+        public List<LocalizedString> getImplementation() {
+            return implementation;
+        }
+    } // class DatabaseInfo
+
+    public static class SchemaInfo {
+        private final String identifier;
+        private final String name;
+        private final String location;
+        private final boolean sort;
+        private final boolean retrieve;
+        private final List<LocalizedString> title;
+
+        private SchemaInfo(String identifier, String name, String location,
+                boolean sort, boolean retieve, List<LocalizedString> title) {
+            this.identifier = identifier;
+            this.name       = name;
+            this.location   = location;
+            this.sort       = sort;
+            this.retrieve   = retieve;
+            if ((title != null) && !title.isEmpty()) {
+                this.title = Collections.unmodifiableList(title);
+            } else {
+                this.title = null;
+            }
+        }
+
+
+        public String getIdentifier() {
+            return identifier;
+        }
+
+
+        public String getName() {
+            return name;
+        }
+
+
+        public String getLocation() {
+            return location;
+        }
+
+
+        public boolean getSort() {
+            return sort;
+        }
+
+
+        public boolean getRetrieve() {
+            return retrieve;
+        }
+
+
+        public List<LocalizedString> getTitle() {
+            return title;
+        }
+    } // class SchemaInfo
+
+
+    public static class IndexInfo {
+        public static class Set {
+            private final String identifier;
+            private final String name;
+            private final List<LocalizedString> title;
+
+            private Set(String identifier, String name, List<LocalizedString> title) {
+                this.identifier = identifier;
+                this.name       = name;
+                if ((title != null) && !title.isEmpty()) {
+                    this.title = Collections.unmodifiableList(title);
+                } else {
+                    this.title = null;
+                }
+            }
+
+            public String getIdentifier() {
+                return identifier;
+            }
+
+            public String getName() {
+                return name;
+            }
+
+            public List<LocalizedString> getTitle() {
+                return title;
+            }
+        } // class IndexInfo.Set
+
+        public static class Index {
+            public static class Map {
+                private final boolean primary;
+                private final String set;
+                private final String name;
+
+                private Map(boolean primary, String set, String name) {
+                    this.primary = primary;
+                    this.set     = set;
+                    this.name    = name;
+                }
+
+                public boolean isPrimary() {
+                    return primary;
+                }
+
+                public String getSet() {
+                    return set;
+                }
+
+                public String getName() {
+                    return name;
+                }
+            } // class IndexInfo.Index.Map
+            private final List<LocalizedString> title;
+            private final boolean can_search;
+            private final boolean can_scan;
+            private final boolean can_sort;
+            private final List<Index.Map> maps;
+
+
+            public Index(List<LocalizedString> title, boolean can_search,
+                    boolean can_scan, boolean can_sort, List<Map> maps) {
+                if ((title != null) && !title.isEmpty()) {
+                    this.title = Collections.unmodifiableList(title);
+                } else {
+                    this.title = null;
+                }
+                this.can_search = can_search;
+                this.can_scan   = can_scan;
+                this.can_sort   = can_sort;
+                this.maps       = maps;
+            }
+
+
+            public List<LocalizedString> getTitle() {
+                return title;
+            }
+
+
+            public boolean canSearch() {
+                return can_search;
+            }
+
+
+            public boolean canScan() {
+                return can_scan;
+            }
+
+
+            public boolean canSort() {
+                return can_sort;
+            }
+
+
+            public List<Index.Map> getMaps() {
+                return maps;
+            }
+
+        } // class Index
+
+        private final List<IndexInfo.Set> sets;
+        private final List<IndexInfo.Index> indexes;
+
+        private IndexInfo(List<IndexInfo.Set> sets, List<IndexInfo.Index> indexes) {
+            if ((sets != null) && !sets.isEmpty()) {
+                this.sets = Collections.unmodifiableList(sets);
+            } else {
+                this.sets = null;
+            }
+            if ((indexes != null) && !indexes.isEmpty()) {
+                this.indexes = Collections.unmodifiableList(indexes);
+            } else {
+                this.indexes = null;
+            }
+        }
+
+
+        public List<IndexInfo.Set> getSets() {
+            return sets;
+        }
+
+
+        public List<IndexInfo.Index> getIndexes() {
+            return indexes;
+        }
+    } // IndexInfo
+
+    private static final Logger logger =
+            LoggerFactory.getLogger(SRUServerConfig.class);
+    private final String transport;
+    private final String host;
+    private final int port;
+    private final String database;
+    private final int numberOfRecords;
+    private final int maximumRecords;
+    private final int numberOfTerms;
+    private final int maximumTerms;
+    private final boolean echoRequests;
+    private final int indentResponse;
+    private final int responseBufferSize;
+    private final boolean allowOverrideMaximumRecords;
+    private final boolean allowOverrideMaximumTerms;
+    private final boolean allowOverrideIndentResponse;
+    private final String baseUrl;
+    private final DatabaseInfo databaseInfo;
+    private final IndexInfo indexInfo;
+    private final List<SchemaInfo> schemaInfo;
+
+
+    private SRUServerConfig(String transport, String host, int port,
+            String database, int numberOfRecords, int maximumRecords,
+            int numberOfTerms, int maximumTerms, boolean echoRequests,
+            int indentResponse, int responseBufferSize,
+            boolean allowOverrideMaximumRecords,
+            boolean allowOverrideMaximumTerms,
+            boolean allowOverrideIndentResponse, DatabaseInfo databaseinfo,
+            IndexInfo indexInfo, List<SchemaInfo> schemaInfo) {
+        this.transport                   = transport;
+        this.host                        = host;
+        this.port                        = port;
+        this.database                    = database;
+        this.numberOfRecords             = numberOfRecords;
+        this.maximumRecords              = maximumRecords;
+        this.numberOfTerms               = numberOfTerms;
+        this.maximumTerms                = maximumTerms;
+        this.echoRequests                = echoRequests;
+        this.indentResponse              = indentResponse;
+        this.responseBufferSize          = responseBufferSize;
+        this.allowOverrideMaximumRecords = allowOverrideMaximumRecords;
+        this.allowOverrideMaximumTerms   = allowOverrideMaximumTerms;
+        this.allowOverrideIndentResponse = allowOverrideIndentResponse;
+        this.databaseInfo                = databaseinfo;
+        this.indexInfo                   = indexInfo;
+        if ((schemaInfo != null) && !schemaInfo.isEmpty()) {
+            this.schemaInfo = Collections.unmodifiableList(schemaInfo);
+        } else {
+            this.schemaInfo = null;
+        }
+
+        // build baseUrl
+        StringBuilder sb = new StringBuilder();
+        sb.append(host);
+        if (port != 80) {
+            sb.append(":").append(port);
+        }
+        sb.append("/").append(database);
+        this.baseUrl = sb.toString();
+    }
+
+
+    public SRUVersion getDefaultVersion() {
+        return SRUVersion.VERSION_1_2;
+    }
+
+
+    public SRURecordPacking getDefaultRecordPacking() {
+        return SRURecordPacking.XML;
+    }
+
+
+    public boolean getEchoRequests() {
+        return echoRequests;
+    }
+
+
+    public String getTransports() {
+        return transport;
+    }
+
+
+    public String getHost() {
+        return host;
+    }
+
+
+    public int getPort() {
+        return port;
+    }
+
+
+    public String getDatabase() {
+        return database;
+    }
+
+
+    public String getBaseUrl() {
+        return baseUrl;
+    }
+
+
+    public int getNumberOfRecords() {
+        return numberOfRecords;
+    }
+
+
+    public int getMaximumRecords() {
+        return maximumRecords;
+    }
+
+
+    public int getNumberOfTerms() {
+        return numberOfTerms;
+    }
+
+
+    public int getMaximumTerms() {
+        return maximumTerms;
+    }
+
+
+    public int getIndentResponse() {
+        return indentResponse;
+    }
+
+
+    public boolean allowOverrideMaximumRecords() {
+        return allowOverrideMaximumRecords;
+    }
+
+
+    public boolean allowOverrideMaximumTerms() {
+        return allowOverrideMaximumTerms;
+    }
+
+
+    public boolean allowOverrideIndentResponse() {
+        return allowOverrideIndentResponse;
+    }
+
+
+    public int getResponseBufferSize() {
+        return responseBufferSize;
+    }
+
+
+    public DatabaseInfo getDatabaseInfo() {
+        return databaseInfo;
+    }
+
+
+    public IndexInfo getIndexInfo() {
+        return indexInfo;
+    }
+
+
+    public List<SchemaInfo> getSchemaInfo() {
+        return schemaInfo;
+    }
+
+
+    public String getRecordSchemaIdentifier(String recordSchemaName) {
+        if (recordSchemaName != null) {
+            if ((schemaInfo != null) && !schemaInfo.isEmpty()) {
+                for (SchemaInfo schema : schemaInfo) {
+                    if (schema.getName().equals(recordSchemaName)) {
+                        return schema.getIdentifier();
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+
+    public String getRecordSchemaName(String schemaIdentifier) {
+        if (schemaIdentifier != null) {
+           if ((schemaInfo != null) && !schemaInfo.isEmpty()) {
+               for (SchemaInfo schema : schemaInfo) {
+                   if (schema.getIdentifier().equals(schemaIdentifier)) {
+                       return schema.getName();
+                   }
+               }
+           }
+        }
+        return null;
+    }
+
+
+    public SchemaInfo findSchemaInfo(String value) {
+        if (value != null) {
+            if ((schemaInfo != null) && !schemaInfo.isEmpty()) {
+                for (SchemaInfo schema : schemaInfo) {
+                    if (schema.getIdentifier().equals(value) ||
+                            schema.getName().equals(value)) {
+                        return schema;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+
+    /**
+     * Parse a SRU server XML configuration file and create an configuration
+     * object from it.
+     *
+     * @param params
+     *            additional settings
+     * @param configFile
+     *            an {@link URL} pointing to the XML configuration file
+     * @return a initialized <code>SRUEndpointConfig</code> instance
+     * @throws NullPointerException
+     *             if <em>params</em> or <em>configFile</em> is
+     *             <code>null</code>
+     * @throws SRUConfigException
+     *             if an error occurred
+     */
+    public static SRUServerConfig parse(Map<String, String> params,
+            URL configFile) throws SRUConfigException {
+        if (params == null) {
+            throw new NullPointerException("params == null");
+        }
+        if (configFile == null) {
+            throw new NullPointerException("in == null");
+        }
+        try {
+            URL url = SRUServerConfig.class.getClassLoader()
+                    .getResource(CONFIG_FILE_SCHEMA_URL);
+            if (url == null) {
+                throw new SRUConfigException("cannot open \"" +
+                        CONFIG_FILE_SCHEMA_URL + "\"");
+            }
+            SchemaFactory sfactory =
+                SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+            Schema schema = sfactory.newSchema(url);
+            DocumentBuilderFactory factory =
+                DocumentBuilderFactory.newInstance();
+            factory.setNamespaceAware(true);
+            factory.setSchema(schema);
+            factory.setIgnoringElementContentWhitespace(true);
+            factory.setIgnoringComments(true);
+            factory.setValidating(false);
+
+            // parse input
+            DocumentBuilder builder = factory.newDocumentBuilder();
+            InputSource input = new InputSource(configFile.openStream());
+            input.setPublicId(CONFIG_FILE_NAMESPACE_URI);
+            input.setSystemId(CONFIG_FILE_NAMESPACE_URI);
+            Document doc = builder.parse(input);
+
+            // validate
+            Source source = new DOMSource(doc);
+            Validator validator = schema.newValidator();
+            validator.setErrorHandler(new DefaultHandler() {
+                @Override
+                public void error(SAXParseException e) throws SAXException {
+                    fatalError(e);
+                }
+
+                @Override
+                public void fatalError(SAXParseException e) throws SAXException {
+                    throw new SAXException(
+                            "error parsing endpoint configuration file", e);
+                }
+            });
+            validator.validate(source);
+
+
+            XPathFactory xfactory = XPathFactory.newInstance();
+            XPath xpath = xfactory.newXPath();
+            xpath.setNamespaceContext(new NamespaceContext() {
+                @Override
+                public Iterator<?> getPrefixes(String namespaceURI) {
+                    throw new UnsupportedOperationException();
+                }
+
+                @Override
+                public String getPrefix(String namespaceURI) {
+                    throw new UnsupportedOperationException();
+                }
+
+                @Override
+                public String getNamespaceURI(String prefix) {
+                    if (prefix == null) {
+                        throw new NullPointerException("prefix == null");
+                    }
+                    if (prefix.equals("sru")) {
+                        return CONFIG_FILE_NAMESPACE_URI;
+                    } else if (prefix.equals(XMLConstants.XML_NS_PREFIX)) {
+                        return XMLConstants.XML_NS_URI;
+                    } else {
+                        return XMLConstants.NULL_NS_URI;
+                    }
+                }
+            });
+
+            DatabaseInfo databaseInfo = buildDatabaseInfo(xpath, doc);
+
+            IndexInfo indexInfo = buildIndexInfo(xpath, doc);
+
+            List<SchemaInfo> schemaInfo = buildSchemaInfo(xpath, doc);
+
+            /*
+             * convert legacy parameters
+             */
+            convertLegacyParameter(params);
+
+            /*
+             * fetch parameters more parameters
+             */
+            String transport = params.get(SRU_TRANSPORT);
+            if ((transport == null) || transport.isEmpty()) {
+                throw new SRUConfigException("parameter \"" + SRU_TRANSPORT +
+                        "\" is mandatory");
+            } else {
+                StringBuilder sb = new StringBuilder();
+                StringTokenizer st = new StringTokenizer(transport);
+                while (st.hasMoreTokens()) {
+                    String s = st.nextToken().trim().toLowerCase();
+                    if (!("http".equals(s) || "https".equals(s))) {
+                        throw new SRUConfigException(
+                                "unsupported transport \"" + s + "\"");
+                    }
+                    if (sb.length() > 0) {
+                        sb.append(" ");
+                    }
+                    sb.append(s);
+                } // while
+                transport = sb.toString();
+            }
+
+            String host = params.get(SRU_HOST);
+            if ((host == null) || host.isEmpty()) {
+                throw new SRUConfigException("parameter \"" + SRU_HOST +
+                        "\" is mandatory");
+            }
+
+            int port = parseNumber(params, SRU_PORT, true, -1, 1, 65535);
+
+            String database = params.get(SRU_DATABASE);
+            if ((database == null) || database.isEmpty()) {
+                throw new SRUConfigException("parameter \"" + SRU_DATABASE +
+                        "\" is mandatory");
+            }
+
+            // cleanup: remove leading slashed
+            while (database.startsWith("/")) {
+                database = database.substring(1);
+            }
+
+
+            int numberOfRecords = parseNumber(params, SRU_NUMBER_OF_RECORDS,
+                    false, DEFAULT_NUMBER_OF_RECORDS, 1, -1);
+
+            int maximumRecords = parseNumber(params, SRU_MAXIMUM_RECORDS,
+                    false, DEFAULT_MAXIMUM_RECORDS, numberOfRecords, -1);
+
+            int numberOfTerms = parseNumber(params, SRU_NUMBER_OF_TERMS,
+                    false, DEFAULT_NUMBER_OF_TERMS, 0, -1);
+
+            int maximumTerms = parseNumber(params, SRU_MAXIMUM_TERMS, false,
+                        DEFAULT_MAXIMUM_TERMS, numberOfTerms, -1);
+
+            boolean echoRequests = parseBoolean(params, SRU_ECHO_REQUESTS,
+                    false, true);
+
+            int indentResponse = parseNumber(params, SRU_INDENT_RESPONSE,
+                    false, -1, -1, 8);
+
+            boolean allowOverrideMaximumRecords = parseBoolean(params,
+                    SRU_ALLOW_OVERRIDE_MAXIMUM_RECORDS, false, false);
+
+            boolean allowOverrideMaximumTerms = parseBoolean(params,
+                    SRU_ALLOW_OVERRIDE_MAXIMUM_TERMS, false, false);
+
+            boolean allowOverrideIndentResponse = parseBoolean(params,
+                    SRU_ALLOW_OVERRIDE_INDENT_RESPONSE, false, false);
+
+            int responseBufferSize = parseNumber(params,
+                    SRU_RESPONSE_BUFFER_SIZE, false,
+                    DEFAULT_RESPONSE_BUFFER_SIZE, 0, -1);
+
+            return new SRUServerConfig(transport, host, port, database,
+                    numberOfRecords, maximumRecords, numberOfTerms,
+                    maximumTerms, echoRequests, indentResponse,
+                    responseBufferSize, allowOverrideMaximumRecords,
+                    allowOverrideMaximumTerms, allowOverrideIndentResponse,
+                    databaseInfo, indexInfo, schemaInfo);
+        } catch (IOException e) {
+            throw new SRUConfigException("error reading configuration file", e);
+        } catch (XPathException e) {
+            throw new SRUConfigException("error parsing configuration file", e);
+        } catch (ParserConfigurationException e) {
+            throw new SRUConfigException("error parsing configuration file", e);
+        } catch (SAXException e) {
+            throw new SRUConfigException("error parsing configuration file", e);
+        }
+    }
+
+
+    private static int parseNumber(Map<String, String> params, String name,
+            boolean mandatory, int defaultValue, int minValue, int maxValue)
+            throws SRUConfigException {
+        String value = params.get(name);
+        if ((value == null) || value.isEmpty()) {
+            if (mandatory) {
+                throw new SRUConfigException("parameter \"" + name +
+                        "\" is mandatory");
+            } else {
+                return defaultValue;
+            }
+        } else {
+            try {
+                int num = Integer.parseInt(value);
+
+                // sanity checks
+                if ((minValue != -1) && (maxValue != -1)) {
+                    if ((num < minValue) || (num > maxValue)) {
+                        throw new SRUConfigException("parameter \"" + name +
+                                "\" must be between " + minValue + " and " +
+                                maxValue + ": " + num);
+                    }
+                } else {
+                    if ((minValue != -1) && (num < minValue)) {
+                        throw new SRUConfigException("parameter \"" + name +
+                                "\" must be larger than " + minValue + ": " +
+                                num);
+
+                    }
+                    if ((maxValue != -1) && (num > maxValue)) {
+                        throw new SRUConfigException("parameter \"" + name +
+                                "\" must be smaller than " + maxValue + ": " +
+                                num);
+                    }
+                }
+                return num;
+            } catch (NumberFormatException e) {
+                throw new SRUConfigException("parameter \"" + name +
+                        "\" must be nummerical and less than " +
+                        Integer.MAX_VALUE + ": " + value);
+            }
+        }
+    }
+
+
+    private static boolean parseBoolean(Map<String, String> params,
+            String name, boolean mandatory, boolean defaultValue)
+            throws SRUConfigException {
+        String value = params.get(name);
+        if ((value == null) || value.isEmpty()) {
+            if (mandatory) {
+                throw new SRUConfigException("parameter \"" + name +
+                        "\" is mandatory");
+            } else {
+                return defaultValue;
+            }
+        } else {
+            return Boolean.valueOf(value);
+        }
+    }
+
+
+    private static DatabaseInfo buildDatabaseInfo(XPath xpath, Document doc)
+            throws SRUConfigException, XPathExpressionException {
+        List<LocalizedString> title = buildList(xpath, doc,
+                "//sru:databaseInfo/sru:title");
+        List<LocalizedString> description = buildList(xpath, doc,
+                "//sru:databaseInfo/sru:description");
+        List<LocalizedString> author = buildList(xpath, doc,
+                "//sru:databaseInfo/sru:author");
+        List<LocalizedString> extent = buildList(xpath, doc,
+                "//sru:databaseInfo/sru:extent");
+        List<LocalizedString> history = buildList(xpath, doc,
+                "//sru:databaseInfo/sru:history");
+        List<LocalizedString> langUsage = buildList(xpath, doc,
+                "//sru:databaseInfo/sru:langUsage");
+        List<LocalizedString> restrictions = buildList(xpath, doc,
+                "//sru:databaseInfo/sru:restrictions");
+        List<LocalizedString> subjects = buildList(xpath, doc,
+                "//sru:databaseInfo/sru:subjects");
+        List<LocalizedString> links = buildList(xpath, doc,
+                "//sru:databaseInfo/sru:links");
+        List<LocalizedString> implementation = buildList(xpath, doc,
+                "//sru:databaseInfo/sru:implementation");
+        return new DatabaseInfo(title, description, author, extent, history,
+                langUsage, restrictions, subjects, links, implementation);
+    }
+
+
+    private static IndexInfo buildIndexInfo(XPath xpath, Document doc)
+            throws SRUConfigException, XPathExpressionException {
+        List<IndexInfo.Set> sets      = null;
+        List<IndexInfo.Index> indexes = null;
+
+        XPathExpression expr = xpath.compile("//sru:indexInfo/sru:set");
+        NodeList result = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
+        if (result.getLength() > 0) {
+            sets = new ArrayList<IndexInfo.Set>(result.getLength());
+            for (int i = 0; i < result.getLength(); i++) {
+                Element e = (Element) result.item(i);
+                String identifier = e.getAttribute("identifier");
+                String name       = e.getAttribute("name");
+                if (identifier.isEmpty()) {
+                    throw new SRUConfigException("attribute 'identifier' may "+
+                            "on element '/indexInfo/set' may not be empty");
+                }
+                if (name.isEmpty()) {
+                    throw new SRUConfigException("attribute 'name' may on " +
+                            "element '/indexInfo/set' may not be empty");
+                }
+                List<LocalizedString> title =
+                        fromNodeList(e.getElementsByTagName("title"));
+                sets.add(new IndexInfo.Set(identifier, name, title));
+            }
+        } // sets
+
+        expr = xpath.compile("//sru:indexInfo/sru:index");
+        result = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
+        if (result.getLength() > 0) {
+            indexes = new ArrayList<IndexInfo.Index>(result.getLength());
+            for (int i = 0; i < result.getLength(); i++) {
+                Element e = (Element) result.item(i);
+                List<LocalizedString> title =
+                        fromNodeList(e.getElementsByTagName("title"));
+                boolean can_search = getBooleanAttr(e, "search", false);
+                boolean can_scan   = getBooleanAttr(e, "scan", false);
+                boolean can_sort   = getBooleanAttr(e, "sort", false);
+                List<IndexInfo.Index.Map> maps = null;
+                NodeList result2 = e.getElementsByTagName("map");
+                if ((result2 != null) && (result2.getLength() > 0)) {
+                    maps = new ArrayList<IndexInfo.Index.Map>(
+                            result2.getLength());
+                    boolean foundPrimary = false;
+                    for (int j = 0; j < result2.getLength(); j++) {
+                        Element e2 = (Element) result2.item(j);
+                        boolean primary = getBooleanAttr(e2, "primary", false);
+                        if (primary) {
+                            if (foundPrimary) {
+                                throw new SRUConfigException("only one map " +
+                                        "may be 'primary' in index");
+                            }
+                            foundPrimary = true;
+                        }
+                        String set  = null;
+                        String name = null;
+                        NodeList result3 = e2.getElementsByTagName("name");
+                        if ((result3 != null) && (result3.getLength() > 0)) {
+                            Element e3 = (Element) result3.item(0);
+                            set  = e3.getAttribute("set");
+                            name = e3.getTextContent();
+                            if (set.isEmpty()) {
+                                throw new SRUConfigException("attribute 'set'" +
+                                        " on element '/indexInfo/index/map/" +
+                                        "name' may not be empty");
+                            }
+                            if ((name == null) || name.isEmpty()) {
+                                throw new SRUConfigException("element " +
+                                        "'/indexInfo/index/map/name' may not " +
+                                        "be empty");
+                            }
+                        }
+                        maps.add(new IndexInfo.Index.Map(primary, set, name));
+                    }
+                }
+                indexes.add(new IndexInfo.Index(title, can_search, can_scan,
+                        can_sort, maps));
+            } // for
+
+            // sanity check (/index/map/name/@set exists in any set/@name)
+            if (sets != null) {
+                for (IndexInfo.Index index : indexes) {
+                    if (index.getMaps() != null) {
+                        for (IndexInfo.Index.Map maps : index.getMaps()) {
+                            if (findSetByName(sets, maps.getSet()) == null) {
+                                throw new SRUConfigException("/index/map/" +
+                                        "name refers to nonexitsing set (" +
+                                        maps.getSet() + ")");
+                            }
+                        }
+                    }
+                }
+            }
+        } // if
+        return new IndexInfo(sets, indexes);
+    }
+
+
+    private static List<SchemaInfo> buildSchemaInfo(XPath xpath, Document doc)
+            throws SRUConfigException, XPathExpressionException {
+        List<SchemaInfo> schemaInfos = null;
+        XPathExpression expr = xpath.compile("//sru:schemaInfo/sru:schema");
+        NodeList result = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
+        if (result.getLength() > 0) {
+            schemaInfos = new ArrayList<SchemaInfo>(result.getLength());
+            for (int i = 0; i < result.getLength(); i++) {
+                Element e = (Element) result.item(i);
+                String identifier = e.getAttribute("identifier");
+                String name       = e.getAttribute("name");
+                String location   = e.getAttribute("location");
+                if ((location != null) && location.isEmpty()) {
+                    location = null;
+                }
+                boolean sort     = getBooleanAttr(e, "sort", false);
+                boolean retrieve = getBooleanAttr(e, "retrieve", true);
+                List<LocalizedString> title =
+                        fromNodeList(e.getElementsByTagName("title"));
+                schemaInfos.add(new SchemaInfo(identifier, name, location,
+                        sort, retrieve, title));
+            }
+        }
+        return schemaInfos;
+    }
+
+
+    private static IndexInfo.Set findSetByName(List<IndexInfo.Set> sets,
+            String name) {
+        for (IndexInfo.Set set : sets) {
+            if (set.getName().equals(name)) {
+                return set;
+            }
+        }
+        return null;
+    }
+
+
+    private static List<LocalizedString> buildList(XPath xpath, Document doc,
+            String expression) throws SRUConfigException,
+            XPathExpressionException {
+        XPathExpression expr = xpath.compile(expression);
+        NodeList result = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
+        return fromNodeList(result);
+    }
+
+
+    private static List<LocalizedString> fromNodeList(NodeList nodes)
+            throws SRUConfigException {
+        List<LocalizedString> list = null;
+        if (nodes.getLength() > 0) {
+            list = new ArrayList<LocalizedString>(nodes.getLength());
+            boolean foundPrimary = false;
+            for (int i = 0; i < nodes.getLength(); i++) {
+                Element e = (Element) nodes.item(i);
+                boolean primary = getBooleanAttr(e, "primary", false);
+                if (primary) {
+                    if (foundPrimary) {
+                        throw new SRUConfigException("list may only contain "
+                                + "one element as primary");
+                    }
+                    foundPrimary = true;
+                }
+                list.add(new LocalizedString(e.getTextContent(),
+                        e.getAttributeNS(XMLConstants.XML_NS_URI, "lang"),
+                        primary));
+            }
+        }
+        return list;
+    }
+
+
+    private static boolean getBooleanAttr(Element e, String localName,
+            boolean defaultValue) {
+        boolean result = defaultValue;
+        Attr attr = e.getAttributeNode(localName);
+        if ((attr != null) && attr.getSpecified()) {
+            result = Boolean.valueOf(attr.getValue());
+        }
+        return result;
+    }
+
+
+    public static void convertLegacyParameter(Map<String, String> params) {
+        if ((params != null) && !params.isEmpty()) {
+            convertLegacyParameter1(params,
+                    LEGACY_SRU_TRANSPORT,
+                    SRU_TRANSPORT);
+            convertLegacyParameter1(params,
+                    LEGACY_SRU_HOST,
+                    SRU_HOST);
+            convertLegacyParameter1(params,
+                    LEGACY_SRU_PORT,
+                    SRU_PORT);
+            convertLegacyParameter1(params,
+                    LEGACY_SRU_DATABASE,
+                    SRU_DATABASE);
+            convertLegacyParameter1(params,
+                    LEGACY_SRU_NUMBER_OF_RECORDS,
+                    SRU_NUMBER_OF_RECORDS);
+            convertLegacyParameter1(params,
+                    LEGACY_SRU_MAXIMUM_RECORDS,
+                    SRU_MAXIMUM_RECORDS);
+            convertLegacyParameter1(params,
+                    LEGACY_SRU_ECHO_REQUESTS,
+                    SRU_ECHO_REQUESTS);
+            convertLegacyParameter1(params,
+                    LEGACY_SRU_INDENT_RESPONSE,
+                    SRU_INDENT_RESPONSE);
+            convertLegacyParameter1(params,
+                    LEGACY_SRU_ALLOW_OVERRIDE_MAXIMUM_RECORDS,
+                    SRU_ALLOW_OVERRIDE_MAXIMUM_RECORDS);
+            convertLegacyParameter1(params,
+                    LEGACY_SRU_ALLOW_OVERRIDE_INDENT_RESPONSE,
+                    SRU_ALLOW_OVERRIDE_INDENT_RESPONSE);
+        }
+    }
+
+
+    private static void convertLegacyParameter1(Map<String, String> params,
+            String legacyName, String name) {
+        final String value = params.get(legacyName);
+        if (value != null) {
+            params.put(name, value);
+            params.remove(legacyName);
+            logger.warn("parameter '{}' is deprecated, please use "
+                    + "parameter '{}' instead!", legacyName, name);
+        }
+    }
+
+} // class SRUEndpointConfig
Index: SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUVersion.java
===================================================================
--- SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUVersion.java	(revision 5925)
+++ SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUVersion.java	(revision 5925)
@@ -0,0 +1,60 @@
+/**
+ * This software is copyright (c) 2011-2013 by
+ *  - Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ * This is free software. You can redistribute it
+ * and/or modify it under the terms described in
+ * the GNU General Public License v3 of which you
+ * should have received a copy. Otherwise you can download
+ * it from
+ *
+ *   http://www.gnu.org/licenses/gpl-3.0.txt
+ *
+ * @copyright Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ *
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt
+ *  GNU General Public License v3
+ */
+package eu.clarin.sru.server;
+
+/**
+ * SRU version
+ */
+public enum SRUVersion {
+    /**
+     * SRU/CQL version 1.1
+     */
+    VERSION_1_1 {
+        @Override
+        int getVersionNumber() {
+            return ((1 << 16) | 1);
+        }
+
+
+        @Override
+        String getVersionString() {
+            return "1.1";
+        }
+    },
+
+    /**
+     * SRU/CQL version 1.2
+     */
+    VERSION_1_2 {
+        @Override
+        int getVersionNumber() {
+            return ((1 << 16) | 2);
+        }
+
+
+        @Override
+        String getVersionString() {
+            return "1.2";
+        }
+    };
+
+    abstract int getVersionNumber();
+
+
+    abstract String getVersionString();
+
+} // enum SRUVersion
Index: SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUXMLStreamWriter.java
===================================================================
--- SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUXMLStreamWriter.java	(revision 5925)
+++ SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/SRUXMLStreamWriter.java	(revision 5925)
@@ -0,0 +1,533 @@
+/**
+ * This software is copyright (c) 2011-2013 by
+ *  - Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ * This is free software. You can redistribute it
+ * and/or modify it under the terms described in
+ * the GNU General Public License v3 of which you
+ * should have received a copy. Otherwise you can download
+ * it from
+ *
+ *   http://www.gnu.org/licenses/gpl-3.0.txt
+ *
+ * @copyright Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ *
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt
+ *  GNU General Public License v3
+ */
+package eu.clarin.sru.server;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringReader;
+import java.io.Writer;
+import java.util.ArrayDeque;
+import java.util.Deque;
+
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+import org.z3950.zing.cql.CQLNode;
+
+final class SRUXMLStreamWriter implements XMLStreamWriter {
+    private enum IndentingState {
+        SEEN_NOTHING,
+        SEEN_ELEMENT,
+        SEEN_DATA
+    }
+    private static final SAXParserFactory factory;
+    private final SRURecordPacking packing;
+    private final Writer writer;
+    private final XMLStreamWriter xmlwriter;
+    private int indent = -1;
+    private int depth = 0;
+    private Deque<IndentingState> stateStack;
+    private IndentingState state;
+    private boolean writingRecord = false;
+
+
+    SRUXMLStreamWriter(OutputStream stream, XMLOutputFactory factory,
+            SRURecordPacking recordPacking, int indent) throws IOException,
+            XMLStreamException {
+        this.packing = recordPacking;
+        this.writer = new OutputStreamWriter(stream,
+                SRUServer.RESPONSE_ENCODING) {
+            @Override
+            public void write(int c) throws IOException {
+                if (writingRecord && (packing == SRURecordPacking.STRING)) {
+                    /*
+                     * NOTE: need to write single characters here, because
+                     * super.write(String) will call us again, and we would
+                     * create and endless loop here until stack blows up ...
+                     */
+                    switch (c) {
+                    case '<':
+                        super.write('&');
+                        super.write('l');
+                        super.write('t');
+                        super.write(';');
+                        return;
+                    case '>':
+                        super.write('&');
+                        super.write('g');
+                        super.write('t');
+                        super.write(';');
+                        return;
+                    case '&':
+                        super.write('&');
+                        super.write('a');
+                        super.write('m');
+                        super.write('p');
+                        super.write(';');
+                        return;
+                    default:
+                        /* $FALL-THROUGH$ */
+                    }
+                }
+                super.write(c);
+            }
+
+            @Override
+            public void write(char[] c, int off, int len) throws IOException {
+                if (writingRecord && (packing == SRURecordPacking.STRING)) {
+                    for (int i = off; i < len; i++) {
+                        this.write(c[i]);
+                    }
+                } else {
+                    super.write(c, off, len);
+                }
+            }
+
+            @Override
+            public void write(String s, int off, int len) throws IOException {
+                if (writingRecord && (packing == SRURecordPacking.STRING)) {
+                    for (int i = off; i < len; i++) {
+                        this.write(s.charAt(i));
+                    }
+                } else {
+                    super.write(s, off, len);
+                }
+            }
+        };
+
+        /*
+         * It is believed, that XMLWriterFactories, once configured,
+         * are thread-save ...
+         */
+        this.xmlwriter = factory.createXMLStreamWriter(this.writer);
+
+        if (indent > 0) {
+            this.indent = indent;
+            this.state = IndentingState.SEEN_NOTHING;
+            this.stateStack = new ArrayDeque<IndentingState>(16);
+        }
+    }
+
+
+    @Override
+    public void writeStartElement(String localName) throws XMLStreamException {
+        if (indent > 0) {
+            onStartElement();
+        }
+        xmlwriter.writeStartElement(localName);
+    }
+
+
+    @Override
+    public void writeStartElement(String namespaceURI, String localName)
+            throws XMLStreamException {
+        if (indent > 0) {
+            onStartElement();
+        }
+        xmlwriter.writeStartElement(namespaceURI, localName);
+    }
+
+
+    @Override
+    public void writeStartElement(String prefix, String localName,
+            String namespaceURI) throws XMLStreamException {
+        if (indent > 0) {
+            onStartElement();
+        }
+        xmlwriter.writeStartElement(prefix, localName, namespaceURI);
+    }
+
+
+    @Override
+    public void writeEmptyElement(String namespaceURI, String localName)
+            throws XMLStreamException {
+        if (indent > 0) {
+            onEmptyElement();
+        }
+        xmlwriter.writeEmptyElement(namespaceURI, localName);
+    }
+
+
+    @Override
+    public void writeEmptyElement(String prefix, String localName,
+            String namespaceURI) throws XMLStreamException {
+        if (indent > 0) {
+            onEmptyElement();
+        }
+        xmlwriter.writeEmptyElement(prefix, localName, namespaceURI);
+    }
+
+
+    @Override
+    public void writeEmptyElement(String localName) throws XMLStreamException {
+        if (indent > 0) {
+            onEmptyElement();
+        }
+        xmlwriter.writeEmptyElement(localName);
+    }
+
+
+    @Override
+    public void writeEndElement() throws XMLStreamException {
+        if (indent > 0) {
+            onEndElement();
+        }
+        xmlwriter.writeEndElement();
+    }
+
+
+    @Override
+    public void writeEndDocument() throws XMLStreamException {
+        xmlwriter.writeEndDocument();
+    }
+
+
+    @Override
+    public void close() throws XMLStreamException {
+        xmlwriter.close();
+    }
+
+
+    @Override
+    public void flush() throws XMLStreamException {
+        xmlwriter.flush();
+    }
+
+
+    @Override
+    public void writeAttribute(String localName, String value)
+            throws XMLStreamException {
+        xmlwriter.writeAttribute(localName, value);
+    }
+
+
+    @Override
+    public void writeAttribute(String prefix, String namespaceURI,
+            String localName, String value) throws XMLStreamException {
+        xmlwriter.writeAttribute(prefix, namespaceURI, localName, value);
+    }
+
+
+    @Override
+    public void writeAttribute(String namespaceURI, String localName,
+            String value) throws XMLStreamException {
+        xmlwriter.writeAttribute(namespaceURI, localName, value);
+    }
+
+
+    @Override
+    public void writeNamespace(String prefix, String namespaceURI)
+            throws XMLStreamException {
+        xmlwriter.writeNamespace(prefix, namespaceURI);
+    }
+
+
+    @Override
+    public void writeDefaultNamespace(String namespaceURI)
+            throws XMLStreamException {
+        xmlwriter.writeDefaultNamespace(namespaceURI);
+    }
+
+
+    @Override
+    public void writeComment(String data) throws XMLStreamException {
+        xmlwriter.writeComment(data);
+    }
+
+
+    @Override
+    public void writeProcessingInstruction(String target)
+            throws XMLStreamException {
+        xmlwriter.writeProcessingInstruction(target);
+    }
+
+
+    @Override
+    public void writeProcessingInstruction(String target, String data)
+            throws XMLStreamException {
+        xmlwriter.writeProcessingInstruction(target, data);
+    }
+
+
+    @Override
+    public void writeCData(String data) throws XMLStreamException {
+        if (indent > 0) {
+            state = IndentingState.SEEN_DATA;
+        }
+        xmlwriter.writeCData(data);
+    }
+
+
+    @Override
+    public void writeDTD(String dtd) throws XMLStreamException {
+        xmlwriter.writeDTD(dtd);
+    }
+
+
+    @Override
+    public void writeEntityRef(String name) throws XMLStreamException {
+        xmlwriter.writeEntityRef(name);
+    }
+
+
+    @Override
+    public void writeStartDocument() throws XMLStreamException {
+        xmlwriter.writeStartDocument();
+        if (indent > 0) {
+            xmlwriter.writeCharacters("\n");
+        }
+    }
+
+
+    @Override
+    public void writeStartDocument(String version) throws XMLStreamException {
+        xmlwriter.writeStartDocument(version);
+        if (indent > 0) {
+            xmlwriter.writeCharacters("\n");
+        }
+    }
+
+
+    @Override
+    public void writeStartDocument(String encoding, String version)
+            throws XMLStreamException {
+        xmlwriter.writeStartDocument(encoding, version);
+        if (indent > 0) {
+            xmlwriter.writeCharacters("\n");
+        }
+    }
+
+
+    @Override
+    public void writeCharacters(String text) throws XMLStreamException {
+        if (indent > 0) {
+            state = IndentingState.SEEN_DATA;
+        }
+        xmlwriter.writeCharacters(text);
+    }
+
+
+    @Override
+    public void writeCharacters(char[] text, int start, int len)
+            throws XMLStreamException {
+        if (indent > 0) {
+            state = IndentingState.SEEN_DATA;
+        }
+        xmlwriter.writeCharacters(text, start, len);
+    }
+
+
+    @Override
+    public String getPrefix(String uri) throws XMLStreamException {
+        return xmlwriter.getPrefix(uri);
+    }
+
+
+    @Override
+    public void setPrefix(String prefix, String uri) throws XMLStreamException {
+        xmlwriter.setPrefix(prefix, uri);
+    }
+
+
+    @Override
+    public void setDefaultNamespace(String uri) throws XMLStreamException {
+        xmlwriter.setDefaultNamespace(uri);
+    }
+
+
+    @Override
+    public void setNamespaceContext(NamespaceContext context)
+            throws XMLStreamException {
+        xmlwriter.setNamespaceContext(context);
+    }
+
+
+    @Override
+    public NamespaceContext getNamespaceContext() {
+        return xmlwriter.getNamespaceContext();
+    }
+
+
+    @Override
+    public Object getProperty(String name) throws IllegalArgumentException {
+        return xmlwriter.getProperty(name);
+    }
+
+
+    public Writer getWriter() {
+        return writer;
+    }
+
+
+    public void startRecord() throws XMLStreamException {
+        if (writingRecord) {
+            throw new IllegalStateException("was already writing record");
+        }
+        xmlwriter.flush();
+        /*
+         *  abuse writeCharacters to force writer to close finish
+         *  any pending start or end elements
+         */
+        xmlwriter.writeCharacters("");
+        writingRecord = true;
+    }
+
+
+    public void endRecord() throws XMLStreamException {
+        if (!writingRecord) {
+            throw new IllegalStateException("was not writing record");
+        }
+        /*
+         *  abuse writeCharacters to force writer to close finish
+         *  any pending start or end elements
+         */
+        xmlwriter.writeCharacters("");
+        xmlwriter.flush();
+        writingRecord = false;
+    }
+
+
+    public void writeXCQL(CQLNode query, final boolean searchRetrieveMode)
+            throws XMLStreamException {
+        /*
+         * HACK: Parsing the XCQL to serialize is wasting resources.
+         * Alternative would be to serialize to XCQL from CQLNode, but
+         * I'm not yet enthusiastic on writing the serializer myself.
+         */
+        try {
+            SAXParser parser = factory.newSAXParser();
+            InputSource input =
+                    new InputSource(new StringReader(query.toXCQL()));
+            parser.parse(input, new DefaultHandler() {
+                @Override
+                public void startElement(String uri, String localName,
+                        String qName, Attributes attributes)
+                                throws SAXException {
+                    if (!searchRetrieveMode && qName.equals("searchClause")) {
+                        return;
+                    }
+                    try {
+                        SRUXMLStreamWriter.this.writeStartElement(qName);
+                        for (int i = 0; i < attributes.getLength(); i++) {
+                            SRUXMLStreamWriter.this.writeAttribute(attributes.getQName(i),
+                                    attributes.getValue(i));
+                        }
+                    } catch (XMLStreamException e) {
+                        throw new SAXException(e);
+                    }
+                }
+
+                @Override
+                public void endElement(String uri, String localName,
+                        String qName) throws SAXException {
+                    if (!searchRetrieveMode && qName.equals("searchClause")) {
+                        return;
+                    }
+                    try {
+                        SRUXMLStreamWriter.this.writeEndElement();
+                    } catch (XMLStreamException e) {
+                        throw new SAXException(e);
+                    }
+                }
+
+                @Override
+                public void characters(char[] text, int start, int length)
+                        throws SAXException {
+                    try {
+                        boolean isWhitespaceOnly = true;
+                        for (int i = start; i < start + length; i++) {
+                            if (!Character.isWhitespace(text[i])) {
+                                isWhitespaceOnly = false;
+                                break;
+                            }
+                        }
+                        if (!isWhitespaceOnly) {
+                            SRUXMLStreamWriter.this.writeCharacters(text, start, length);
+                        }
+                    } catch (XMLStreamException e) {
+                        throw new SAXException(e);
+                    }
+                }
+            });
+        } catch (Exception e) {
+            throw new XMLStreamException("cannot write XCQL", e);
+        }
+    }
+
+
+    private void onStartElement() throws XMLStreamException {
+        if (!(writingRecord && (packing == SRURecordPacking.STRING))) {
+            stateStack.push(IndentingState.SEEN_ELEMENT);
+            state = IndentingState.SEEN_NOTHING;
+            if (depth > 0) {
+                xmlwriter.writeCharacters("\n");
+            }
+            doIndent();
+            depth++;
+        }
+    }
+
+
+    private void onEndElement() throws XMLStreamException {
+        if (!(writingRecord && (packing == SRURecordPacking.STRING))) {
+            depth--;
+            if (state == IndentingState.SEEN_ELEMENT) {
+                xmlwriter.writeCharacters("\n");
+                doIndent();
+            }
+            state = stateStack.pop();
+        }
+    }
+
+
+    private void onEmptyElement() throws XMLStreamException {
+        if (!(writingRecord && (packing == SRURecordPacking.STRING))) {
+            state = IndentingState.SEEN_ELEMENT;
+            if (depth > 0) {
+                xmlwriter.writeCharacters("\n");
+            }
+            doIndent();
+        }
+    }
+
+
+    private void doIndent() throws XMLStreamException {
+        if (depth > 0) {
+            for (int i = 0; i < (depth * indent); i++) {
+                xmlwriter.writeCharacters(" ");
+            }
+        }
+    }
+
+
+    static {
+        factory = SAXParserFactory.newInstance();
+        factory.setNamespaceAware(false);
+        factory.setValidating(false);
+        factory.setXIncludeAware(false);
+    }
+
+} // class SRUXMLStreamWriter
Index: SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/utils/SRUSearchEngineBase.java
===================================================================
--- SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/utils/SRUSearchEngineBase.java	(revision 5925)
+++ SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/utils/SRUSearchEngineBase.java	(revision 5925)
@@ -0,0 +1,108 @@
+/**
+ * This software is copyright (c) 2011-2013 by
+ *  - Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ * This is free software. You can redistribute it
+ * and/or modify it under the terms described in
+ * the GNU General Public License v3 of which you
+ * should have received a copy. Otherwise you can download
+ * it from
+ *
+ *   http://www.gnu.org/licenses/gpl-3.0.txt
+ *
+ * @copyright Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ *
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt
+ *  GNU General Public License v3
+ */
+package eu.clarin.sru.server.utils;
+
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+
+import eu.clarin.sru.server.SRUConfigException;
+import eu.clarin.sru.server.SRUDiagnosticList;
+import eu.clarin.sru.server.SRUException;
+import eu.clarin.sru.server.SRUExplainResult;
+import eu.clarin.sru.server.SRURequest;
+import eu.clarin.sru.server.SRUScanResultSet;
+import eu.clarin.sru.server.SRUSearchEngine;
+import eu.clarin.sru.server.SRUSearchResultSet;
+import eu.clarin.sru.server.SRUServerConfig;
+
+
+/**
+ * Base class required for an {@link SRUSearchEngine} implementation to be used
+ * with the {@link SRUServerServlet} Servlet.
+ */
+public abstract class SRUSearchEngineBase implements SRUSearchEngine {
+
+    public SRUSearchEngineBase() {
+    }
+
+
+    /**
+     * Handle a <em>explain</em> operation. The default implementation is a
+     * no-op. Override this method, if you want to provide a custom behavior.
+     *
+     * @see SRUSearchEngine#explain(SRUServerConfig, SRURequest,
+     *      SRUDiagnosticList)
+     */
+    @Override
+    public SRUExplainResult explain(SRUServerConfig config, SRURequest request,
+            SRUDiagnosticList diagnostics) throws SRUException {
+        return null;
+    }
+
+
+    /**
+     * Handle a <em>scan</em> operation. The default implementation is a no-op.
+     * Override this method, if you want to provide a custom behavior.
+     *
+     * @see SRUSearchEngine#scan(SRUServerConfig, SRURequest, SRUDiagnosticList)
+     */
+    @Override
+    public SRUScanResultSet scan(SRUServerConfig config, SRURequest request,
+            SRUDiagnosticList diagnostics) throws SRUException {
+        return null;
+    }
+
+
+    /**
+     * Handle a <em>searchRetrieve</em> operation.
+     *
+     * @see SRUSearchEngine#search(SRUServerConfig, SRURequest,
+     *      SRUDiagnosticList)
+     */
+    @Override
+    public abstract SRUSearchResultSet search(SRUServerConfig config,
+            SRURequest request, SRUDiagnosticList diagnostics)
+            throws SRUException;
+
+
+    /**
+     * Initialize the search engine.
+     *
+     * @param context
+     *            the {@link ServletContext} for the Servlet
+     * @param config
+     *            the {@link SRUServerConfig} object for this search engine
+     * @param params
+     *            additional parameters gathered from the Servlet configuration
+     *            and Servlet context.
+     * @throws SRUConfigException
+     *             an error occurred during initialization of the search engine
+     */
+    public void init(ServletContext context, SRUServerConfig config,
+            Map<String, String> params) throws SRUConfigException {
+    }
+
+
+    /**
+     * Destroy the search engine. Use this method for any cleanup the search
+     * engine needs to perform upon termination.
+     */
+    public void destroy() {
+    }
+
+} // abstract class SRUSearchEngineBase
Index: SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/utils/SRUServerServlet.java
===================================================================
--- SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/utils/SRUServerServlet.java	(revision 5925)
+++ SRUServer/tags/SRUServer-1.6.0/src/main/java/eu/clarin/sru/server/utils/SRUServerServlet.java	(revision 5925)
@@ -0,0 +1,369 @@
+/**
+ * This software is copyright (c) 2011-2013 by
+ *  - Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ * This is free software. You can redistribute it
+ * and/or modify it under the terms described in
+ * the GNU General Public License v3 of which you
+ * should have received a copy. Otherwise you can download
+ * it from
+ *
+ *   http://www.gnu.org/licenses/gpl-3.0.txt
+ *
+ * @copyright Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
+ *
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt
+ *  GNU General Public License v3
+ */
+package eu.clarin.sru.server.utils;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import eu.clarin.sru.server.SRUConfigException;
+import eu.clarin.sru.server.SRUException;
+import eu.clarin.sru.server.SRUServer;
+import eu.clarin.sru.server.SRUServerConfig;
+
+
+/**
+ * A Servlet implementation, which provides an environment for running a
+ * {@link SRUServer} in a Servlet container. Your search engine <b>must</b> use
+ * {@link SRUSearchEngineBase} as base class.
+ *
+ * <p>
+ * Add the following to the web.xml of your web applications web.xml to define a
+ * SRU server. Of course, the value of the Servlet initialization parameter
+ * "eu.clarin.sru.server.utils.sruServerSearchEngineClass" must be adapted to
+ * match the name of your search engine implementation. Furthermore, you can
+ * choose different url-pattern, to match your needs.
+ * </p>
+ *
+ * <p>
+ * For example, if your implementation of {@link SRUSearchEngineBase} is
+ * "com.acme.MySearchEngine" and you want to map the Servlet to the URI "/sru"
+ * the following snippet in your "web.xml" should accomplish the task:
+ * </p>
+ *
+ * <pre>
+ * &lt;servlet&gt;
+ *   &lt;servlet-name&gt;SRUServerServlet&lt;/servlet-name&gt;
+ *   &lt;servlet-class&gt;eu.clarin.sru.server.utils.SRUServerServlet&lt;/servlet-class&gt;
+ *   &lt;init-param&gt;
+ *     &lt;param-name&gt;eu.clarin.sru.server.utils.sruServerSearchEngineClass&lt;/param-name&gt;
+ *     &lt;param-value&gt;com.acme.MySearchEngine&lt;/param-value&gt;
+ *   &lt;/init-param&gt;
+ * &lt;/servlet&gt;
+ * &lt;servlet-mapping&gt;
+ *   &lt;servlet-name&gt;SRUServerServlet&lt;/servlet-name&gt;
+ *   &lt;url-pattern&gt;/sru&lt;/url-pattern&gt;
+ * &lt;/servlet-mapping&gt;
+ * </pre>
+ */
+public final class SRUServerServlet extends HttpServlet {
+    /**
+     * Servlet initialization parameter name for the location of the SRU server
+     * configuration.
+     */
+    public static final String SRU_SERVER_CONFIG_LOCATION_PARAM =
+            "eu.clarin.sru.server.utils.sruServerConfigLocation";
+    /**
+     * @deprecated use {@link #SRU_SERVER_CONFIG_LOCATION_PARAM}
+     */
+    @Deprecated
+    private static final String LEGACY_SRU_SERVER_CONFIG_LOCATION_PARAM =
+            "sruServerConfigLocation";
+    /**
+     * Servlet initialization parameter name for the class that implements the
+     * SRU search engine.
+     */
+    public static final String SRU_SERVER_SEARCH_ENGINE_CLASS_PARAM =
+            "eu.clarin.sru.server.utils.sruServerSearchEngineClass";
+    /**
+     * @deprecated use {@link #SRU_SERVER_SEARCH_ENGINE_CLASS_PARAM}
+     */
+    @Deprecated
+    private static final String LEGACY_SRU_SERVER_SEARCH_ENGINE_CLASS_PARAM_1 =
+            "sruServerSerachEngineClass";
+    /**
+     * @deprecated use {@link #SRU_SERVER_SEARCH_ENGINE_CLASS_PARAM}
+     */
+    @Deprecated
+    private static final String LEGACY_SRU_SERVER_SEARCH_ENGINE_CLASS_PARAM_2 =
+            "eu.clarin.sru.server.utils.sruServerSerachEngineClass";
+    /**
+     * Default value for the location of the SRU server configuration.
+     */
+    public static final String SRU_SERVER_CONFIG_LOCATION_DEFAULT =
+            "/WEB-INF/sru-server-config.xml";
+    private static final long serialVersionUID = 1L;
+    private static final Logger logger =
+            LoggerFactory.getLogger(SRUServerServlet.class);
+    private SRUServer sruServer;
+    private SRUSearchEngineBase searchEngine;
+
+
+    /**
+     * Initialize the SRU server Servlet.
+     *
+     * @see javax.servlet.GenericServlet#init()
+     */
+    @Override
+    public void init() throws ServletException {
+        final ServletConfig cfg  = getServletConfig();
+        final ServletContext ctx = getServletContext();
+
+        String sruServerConfigLocation =
+                cfg.getInitParameter(SRU_SERVER_CONFIG_LOCATION_PARAM);
+        if (sruServerConfigLocation == null) {
+            sruServerConfigLocation = cfg.getInitParameter(
+                    LEGACY_SRU_SERVER_CONFIG_LOCATION_PARAM);
+            if (sruServerConfigLocation != null) {
+                logger.warn("init parameter '" +
+                        LEGACY_SRU_SERVER_CONFIG_LOCATION_PARAM +
+                        "' is deprecated, please use init-parameter '" +
+                        SRU_SERVER_CONFIG_LOCATION_PARAM + "' instead!");
+            } else {
+                sruServerConfigLocation = SRU_SERVER_CONFIG_LOCATION_DEFAULT;
+            }
+        }
+
+        URL sruServerConfigFile;
+        try {
+            sruServerConfigFile = ctx.getResource(sruServerConfigLocation);
+        } catch (MalformedURLException e) {
+            throw new ServletException("init-parameter '" +
+                    SRU_SERVER_CONFIG_LOCATION_PARAM + "' is not a valid URL",
+                    e);
+        }
+        if (sruServerConfigFile == null) {
+            throw new ServletException("init-parameter '" +
+                    SRU_SERVER_CONFIG_LOCATION_PARAM +
+                    "' points to non-existing resource (" +
+                    sruServerConfigLocation + ")");
+        }
+
+        /* get search engine class name from Servlet init-parameters */
+        String sruServerSearchEngineClass =
+                cfg.getInitParameter(SRU_SERVER_SEARCH_ENGINE_CLASS_PARAM);
+
+        /* legacy compatibility (first try) */
+        if (sruServerSearchEngineClass == null) {
+            sruServerSearchEngineClass = cfg.getInitParameter(
+                    LEGACY_SRU_SERVER_SEARCH_ENGINE_CLASS_PARAM_1);
+            if (sruServerSearchEngineClass != null) {
+                logger.warn("init-parameter '" +
+                        LEGACY_SRU_SERVER_SEARCH_ENGINE_CLASS_PARAM_1 +
+                        "' is deprecated, please use init-parameter '" +
+                        SRU_SERVER_SEARCH_ENGINE_CLASS_PARAM + "' instead!");
+            }
+        }
+
+        /* legacy compatibility (second try) */
+        if (sruServerSearchEngineClass == null) {
+            sruServerSearchEngineClass = cfg.getInitParameter(
+                    LEGACY_SRU_SERVER_SEARCH_ENGINE_CLASS_PARAM_2);
+            if (sruServerSearchEngineClass != null) {
+                logger.warn("init-parameter '" +
+                        LEGACY_SRU_SERVER_SEARCH_ENGINE_CLASS_PARAM_2 +
+                        "' is deprecated, please use init-parameter '" +
+                        SRU_SERVER_SEARCH_ENGINE_CLASS_PARAM + "' instead!");
+            }
+        }
+
+        /* if still nothing, give up */
+        if (sruServerSearchEngineClass == null) {
+            throw new ServletException("init-parameter '" +
+                    SRU_SERVER_SEARCH_ENGINE_CLASS_PARAM +
+                    "' not defined in servlet configuration");
+        }
+
+        /*
+         * get init-parameters from ServletConfig ...
+         */
+        final HashMap<String, String> params = new HashMap<String, String>();
+        for (Enumeration<?> i = cfg.getInitParameterNames();
+                i.hasMoreElements();) {
+            String key = (String) i.nextElement();
+            String value = cfg.getInitParameter(key);
+            if ((value != null) && !value.isEmpty()) {
+                params.put(key, value);
+            }
+        }
+
+        /*
+         * ... and get more init-parameters from ServletContext and potentially
+         * overriding parameters from Servlet configuration.
+         */
+        for (Enumeration<?> i = ctx.getInitParameterNames();
+                i.hasMoreElements();) {
+            String key = (String) i.nextElement();
+            String value = ctx.getInitParameter(key);
+            if ((value != null) && !value.isEmpty()) {
+                params.put(key, value);
+            }
+        }
+
+        /* convert legacy parameters */
+        SRUServerConfig.convertLegacyParameter(params);
+
+        /*
+         * Set some defaults (aka "plug and play" for development deployment)
+         * Override those for a production deployment through your Servlet
+         * container's context configuration!
+         */
+        if (!params.containsKey(SRUServerConfig.SRU_TRANSPORT)) {
+            setDefaultConfigParam(params, SRUServerConfig.SRU_TRANSPORT,
+                    "http");
+        }
+        if (!params.containsKey(SRUServerConfig.SRU_HOST)) {
+            setDefaultConfigParam(params, SRUServerConfig.SRU_HOST,
+                    "127.0.0.1");
+        }
+        if (!params.containsKey(SRUServerConfig.SRU_PORT)) {
+            setDefaultConfigParam(params, SRUServerConfig.SRU_PORT,
+                    "8080");
+        }
+        if (!params.containsKey(SRUServerConfig.SRU_DATABASE)) {
+            String contextPath;
+            try {
+                /*
+                 * this only works with Servlet 2.5 API ...
+                 */
+                contextPath = ctx.getContextPath();
+            } catch (NoSuchMethodError e) {
+                /*
+                 * if we fail, put at least something here ...
+                 */
+                contextPath = "/";
+                log("NOTE: auto-configuration for parameter '" +
+                        SRUServerConfig.SRU_DATABASE +
+                        "' failed and will contain only a dummy value!");
+            }
+            setDefaultConfigParam(params, SRUServerConfig.SRU_DATABASE,
+                    contextPath);
+        }
+
+        // parse configuration
+        SRUServerConfig sruServerConfig;
+        try {
+            sruServerConfig =
+                    SRUServerConfig.parse(params, sruServerConfigFile);
+        } catch (SRUConfigException e) {
+            throw new ServletException("sru server configuration is invalid", e);
+        }
+
+        /*
+         * create an instance of the search engine ...
+         */
+        try {
+            @SuppressWarnings("unchecked")
+            Class<SRUSearchEngineBase> clazz = (Class<SRUSearchEngineBase>)
+                Class.forName(sruServerSearchEngineClass);
+            Constructor<SRUSearchEngineBase> constructor =
+                    clazz.getConstructor();
+            searchEngine = constructor.newInstance();
+        } catch (ClassNotFoundException e) {
+            throw new ServletException("error initializing sru server", e);
+        } catch (ClassCastException e) {
+            throw new ServletException("error initializing sru server", e);
+        } catch (NoSuchMethodException e) {
+            throw new ServletException("error initializing sru server", e);
+        } catch (SecurityException e) {
+            throw new ServletException("error initializing sru server", e);
+        } catch (InstantiationException e) {
+            throw new ServletException("error initializing sru server", e);
+        } catch (IllegalAccessException e) {
+            throw new ServletException("error initializing sru server", e);
+        } catch (IllegalArgumentException e) {
+            throw new ServletException("error initializing sru server", e);
+        } catch (InvocationTargetException e) {
+            throw new ServletException("error initializing sru server", e);
+        }
+
+        /*
+         * finally initialize the SRU server ...
+         */
+        try {
+            searchEngine.init(ctx, sruServerConfig, params);
+            sruServer = new SRUServer(sruServerConfig, searchEngine);
+        } catch (SRUConfigException e) {
+            throw new ServletException("error initializing sru server", e);
+        } catch (SRUException e) {
+            throw new ServletException("error initializing sru server", e);
+        }
+    }
+
+
+    /**
+     * Destroy the SRU server Servlet.
+     *
+     * @see javax.servlet.GenericServlet#destroy()
+     */
+    @Override
+    public void destroy() {
+        if (searchEngine != null) {
+            searchEngine.destroy();
+        }
+        super.destroy();
+    }
+
+
+    /**
+     * Handle a HTTP get request.
+     *
+     * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest,
+     *      javax.servlet.http.HttpServletResponse)
+     */
+    @Override
+    protected void doGet(HttpServletRequest request,
+            HttpServletResponse response) throws ServletException, IOException {
+        if (sruServer == null) {
+            throw new ServletException("servlet is not properly initialized");
+        }
+        sruServer.handleRequest(request, response);
+    }
+
+
+    /**
+     * Handle a HTTP post request.
+     *
+     * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest,
+     *      javax.servlet.http.HttpServletResponse)
+     */
+    @Override
+    protected void doPost(HttpServletRequest request,
+            HttpServletResponse response) throws ServletException, IOException {
+        if (sruServer == null) {
+            throw new ServletException("servlet is not properly initalized");
+        }
+        sruServer.handleRequest(request, response);
+    }
+
+
+    private void setDefaultConfigParam(Map<String, String> params, String key,
+            String value) {
+        if (!params.containsKey(key)) {
+            params.put(key, value);
+            log("NOTE: using default '" + value + "' for parameter '" + key +
+                    "', because it was not defined in context configuration.");
+            log("THIS IS NOT RECOMMENDED FOR PRODUCTION DEPLOYMENT!");
+        }
+    }
+
+} // class SRUServerServlet
Index: SRUServer/tags/SRUServer-1.6.0/src/main/resources/META-INF/sru-server-config.xsd
===================================================================
--- SRUServer/tags/SRUServer-1.6.0/src/main/resources/META-INF/sru-server-config.xsd	(revision 5925)
+++ SRUServer/tags/SRUServer-1.6.0/src/main/resources/META-INF/sru-server-config.xsd	(revision 5925)
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+  xmlns="http://www.clarin.eu/sru-server/1.0/"
+  targetNamespace="http://www.clarin.eu/sru-server/1.0/"
+  elementFormDefault="qualified">
+
+  <xsd:import namespace="http://www.w3.org/XML/1998/namespace"
+    schemaLocation="xml.xsd" />
+
+  <xsd:element name="endpoint-config">
+    <xsd:complexType>
+      <xsd:sequence>
+        <xsd:element ref="databaseInfo" />
+        <xsd:element ref="indexInfo" />
+        <xsd:element ref="schemaInfo" />
+      </xsd:sequence>
+    </xsd:complexType>
+  </xsd:element>
+
+  <xsd:element name="databaseInfo">
+    <xsd:complexType>
+      <xsd:sequence>
+        <xsd:element name="title" type="text" maxOccurs="unbounded" />
+        <xsd:element name="description" type="text" minOccurs="0" maxOccurs="unbounded" />
+        <xsd:element name="author" type="text" minOccurs="0" maxOccurs="unbounded" />
+        <xsd:element name="extent" type="text" minOccurs="0" maxOccurs="unbounded" />
+        <xsd:element name="history" type="text" minOccurs="0" maxOccurs="unbounded" />
+        <xsd:element name="langUsage" type="text" minOccurs="0" maxOccurs="unbounded" />
+        <xsd:element name="restrictions" type="text" minOccurs="0" maxOccurs="unbounded" />
+        <xsd:element name="subjects" type="text" minOccurs="0" maxOccurs="unbounded" />
+        <xsd:element name="links" type="text" minOccurs="0" maxOccurs="unbounded" />
+        <xsd:element name="implementation" type="text" minOccurs="0" maxOccurs="unbounded" />
+      </xsd:sequence>
+    </xsd:complexType>
+  </xsd:element>
+
+  <xsd:element name="indexInfo">
+    <xsd:complexType>
+      <xsd:choice maxOccurs="unbounded">
+        <xsd:element ref="set" />
+        <xsd:element ref="index" />
+      </xsd:choice>
+    </xsd:complexType>
+  </xsd:element>
+
+  <xsd:element name="set">
+    <xsd:complexType>
+      <xsd:sequence>
+        <xsd:element name="title" type="text" minOccurs="0" maxOccurs="unbounded" />
+      </xsd:sequence>
+      <xsd:attribute name="name" type="xsd:string" use="required" />
+      <xsd:attribute name="identifier" type="xsd:string" use="required" />
+    </xsd:complexType>
+  </xsd:element>
+
+  <xsd:element name="index">
+    <xsd:complexType>
+      <xsd:sequence>
+        <xsd:element name="title" type="text" minOccurs="0" maxOccurs="unbounded" />
+        <xsd:element ref="map" maxOccurs="unbounded" />
+      </xsd:sequence>
+      <xsd:attribute name="id" type="xsd:string" />
+      <xsd:attribute name="search" type="xsd:boolean" use="required" />
+      <xsd:attribute name="scan" type="xsd:boolean" use="required" />
+      <xsd:attribute name="sort" type="xsd:boolean" use="required" />
+    </xsd:complexType>
+  </xsd:element>
+
+  <xsd:element name="map">
+    <xsd:complexType>
+      <xsd:choice>
+        <xsd:element name="name">
+          <xsd:complexType>
+            <xsd:simpleContent>
+              <xsd:extension base="xsd:string">
+                <xsd:attribute name="set" type="xsd:string" use="required" />
+              </xsd:extension>
+            </xsd:simpleContent>
+          </xsd:complexType>
+        </xsd:element>
+      </xsd:choice>
+      <xsd:attribute name="primary" type="xsd:boolean" default="false" />
+    </xsd:complexType>
+  </xsd:element>
+
+  <xsd:element name="schemaInfo">
+    <xsd:complexType>
+      <xsd:sequence>
+        <xsd:element name="schema" maxOccurs="unbounded">
+          <xsd:complexType>
+            <xsd:sequence>
+              <xsd:element name="title" type="text" minOccurs="0" maxOccurs="unbounded"/>
+            </xsd:sequence>
+            <xsd:attribute name="identifier" type="xsd:string" use="required" />
+            <xsd:attribute name="name" type="xsd:string" use="required" />
+            <xsd:attribute name="location" type="xsd:anyURI" />
+            <xsd:attribute name="sort" type="xsd:boolean" use="required" />
+            <xsd:attribute name="retrieve" type="xsd:boolean" use="required" />
+          </xsd:complexType>
+        </xsd:element>
+      </xsd:sequence>
+    </xsd:complexType>
+  </xsd:element>
+
+  <xsd:complexType name="text">
+    <xsd:simpleContent>
+      <xsd:extension base="xsd:string">
+        <xsd:attribute ref="xml:lang" use="required" />
+        <xsd:attribute name="primary" type="xsd:boolean" default="false" />
+      </xsd:extension>
+    </xsd:simpleContent>
+  </xsd:complexType>
+
+</xsd:schema>
Index: SRUServer/tags/SRUServer-1.6.0/src/main/resources/META-INF/xml.xsd
===================================================================
--- SRUServer/tags/SRUServer-1.6.0/src/main/resources/META-INF/xml.xsd	(revision 5925)
+++ SRUServer/tags/SRUServer-1.6.0/src/main/resources/META-INF/xml.xsd	(revision 5925)
@@ -0,0 +1,287 @@
+<?xml version='1.0'?>
+<?xml-stylesheet href="../2008/09/xsd.xsl" type="text/xsl"?>
+<xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace" 
+  xmlns:xs="http://www.w3.org/2001/XMLSchema" 
+  xmlns   ="http://www.w3.org/1999/xhtml"
+  xml:lang="en">
+
+ <xs:annotation>
+  <xs:documentation>
+   <div>
+    <h1>About the XML namespace</h1>
+
+    <div class="bodytext">
+     <p>
+      This schema document describes the XML namespace, in a form
+      suitable for import by other schema documents.
+     </p>
+     <p>
+      See <a href="http://www.w3.org/XML/1998/namespace.html">
+      http://www.w3.org/XML/1998/namespace.html</a> and
+      <a href="http://www.w3.org/TR/REC-xml">
+      http://www.w3.org/TR/REC-xml</a> for information 
+      about this namespace.
+     </p>
+     <p>
+      Note that local names in this namespace are intended to be
+      defined only by the World Wide Web Consortium or its subgroups.
+      The names currently defined in this namespace are listed below.
+      They should not be used with conflicting semantics by any Working
+      Group, specification, or document instance.
+     </p>
+     <p>   
+      See further below in this document for more information about <a
+      href="#usage">how to refer to this schema document from your own
+      XSD schema documents</a> and about <a href="#nsversioning">the
+      namespace-versioning policy governing this schema document</a>.
+     </p>
+    </div>
+   </div>
+  </xs:documentation>
+ </xs:annotation>
+
+ <xs:attribute name="lang">
+  <xs:annotation>
+   <xs:documentation>
+    <div>
+     
+      <h3>lang (as an attribute name)</h3>
+      <p>
+       denotes an attribute whose value
+       is a language code for the natural language of the content of
+       any element; its value is inherited.  This name is reserved
+       by virtue of its definition in the XML specification.</p>
+     
+    </div>
+    <div>
+     <h4>Notes</h4>
+     <p>
+      Attempting to install the relevant ISO 2- and 3-letter
+      codes as the enumerated possible values is probably never
+      going to be a realistic possibility.  
+     </p>
+     <p>
+      See BCP 47 at <a href="http://www.rfc-editor.org/rfc/bcp/bcp47.txt">
+       http://www.rfc-editor.org/rfc/bcp/bcp47.txt</a>
+      and the IANA language subtag registry at
+      <a href="http://www.iana.org/assignments/language-subtag-registry">
+       http://www.iana.org/assignments/language-subtag-registry</a>
+      for further information.
+     </p>
+     <p>
+      The union allows for the 'un-declaration' of xml:lang with
+      the empty string.
+     </p>
+    </div>
+   </xs:documentation>
+  </xs:annotation>
+  <xs:simpleType>
+   <xs:union memberTypes="xs:language">
+    <xs:simpleType>    
+     <xs:restriction base="xs:string">
+      <xs:enumeration value=""/>
+     </xs:restriction>
+    </xs:simpleType>
+   </xs:union>
+  </xs:simpleType>
+ </xs:attribute>
+
+ <xs:attribute name="space">
+  <xs:annotation>
+   <xs:documentation>
+    <div>
+     
+      <h3>space (as an attribute name)</h3>
+      <p>
+       denotes an attribute whose
+       value is a keyword indicating what whitespace processing
+       discipline is intended for the content of the element; its
+       value is inherited.  This name is reserved by virtue of its
+       definition in the XML specification.</p>
+     
+    </div>
+   </xs:documentation>
+  </xs:annotation>
+  <xs:simpleType>
+   <xs:restriction base="xs:NCName">
+    <xs:enumeration value="default"/>
+    <xs:enumeration value="preserve"/>
+   </xs:restriction>
+  </xs:simpleType>
+ </xs:attribute>
+ 
+ <xs:attribute name="base" type="xs:anyURI"> <xs:annotation>
+   <xs:documentation>
+    <div>
+     
+      <h3>base (as an attribute name)</h3>
+      <p>
+       denotes an attribute whose value
+       provides a URI to be used as the base for interpreting any
+       relative URIs in the scope of the element on which it
+       appears; its value is inherited.  This name is reserved
+       by virtue of its definition in the XML Base specification.</p>
+     
+     <p>
+      See <a
+      href="http://www.w3.org/TR/xmlbase/">http://www.w3.org/TR/xmlbase/</a>
+      for information about this attribute.
+     </p>
+    </div>
+   </xs:documentation>
+  </xs:annotation>
+ </xs:attribute>
+ 
+ <xs:attribute name="id" type="xs:ID">
+  <xs:annotation>
+   <xs:documentation>
+    <div>
+     
+      <h3>id (as an attribute name)</h3> 
+      <p>
+       denotes an attribute whose value
+       should be interpreted as if declared to be of type ID.
+       This name is reserved by virtue of its definition in the
+       xml:id specification.</p>
+     
+     <p>
+      See <a
+      href="http://www.w3.org/TR/xml-id/">http://www.w3.org/TR/xml-id/</a>
+      for information about this attribute.
+     </p>
+    </div>
+   </xs:documentation>
+  </xs:annotation>
+ </xs:attribute>
+
+ <xs:attributeGroup name="specialAttrs">
+  <xs:attribute ref="xml:base"/>
+  <xs:attribute ref="xml:lang"/>
+  <xs:attribute ref="xml:space"/>
+  <xs:attribute ref="xml:id"/>
+ </xs:attributeGroup>
+
+ <xs:annotation>
+  <xs:documentation>
+   <div>
+   
+    <h3>Father (in any context at all)</h3> 
+
+    <div class="bodytext">
+     <p>
+      denotes Jon Bosak, the chair of 
+      the original XML Working Group.  This name is reserved by 
+      the following decision of the W3C XML Plenary and 
+      XML Coordination groups:
+     </p>
+     <blockquote>
+       <p>
+	In appreciation for his vision, leadership and
+	dedication the W3C XML Plenary on this 10th day of
+	February, 2000, reserves for Jon Bosak in perpetuity
+	the XML name "xml:Father".
+       </p>
+     </blockquote>
+    </div>
+   </div>
+  </xs:documentation>
+ </xs:annotation>
+
+ <xs:annotation>
+  <xs:documentation>
+   <div xml:id="usage" id="usage">
+    <h2><a name="usage">About this schema document</a></h2>
+
+    <div class="bodytext">
+     <p>
+      This schema defines attributes and an attribute group suitable
+      for use by schemas wishing to allow <code>xml:base</code>,
+      <code>xml:lang</code>, <code>xml:space</code> or
+      <code>xml:id</code> attributes on elements they define.
+     </p>
+     <p>
+      To enable this, such a schema must import this schema for
+      the XML namespace, e.g. as follows:
+     </p>
+     <pre>
+          &lt;schema . . .>
+           . . .
+           &lt;import namespace="http://www.w3.org/XML/1998/namespace"
+                      schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+     </pre>
+     <p>
+      or
+     </p>
+     <pre>
+           &lt;import namespace="http://www.w3.org/XML/1998/namespace"
+                      schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
+     </pre>
+     <p>
+      Subsequently, qualified reference to any of the attributes or the
+      group defined below will have the desired effect, e.g.
+     </p>
+     <pre>
+          &lt;type . . .>
+           . . .
+           &lt;attributeGroup ref="xml:specialAttrs"/>
+     </pre>
+     <p>
+      will define a type which will schema-validate an instance element
+      with any of those attributes.
+     </p>
+    </div>
+   </div>
+  </xs:documentation>
+ </xs:annotation>
+
+ <xs:annotation>
+  <xs:documentation>
+   <div id="nsversioning" xml:id="nsversioning">
+    <h2><a name="nsversioning">Versioning policy for this schema document</a></h2>
+    <div class="bodytext">
+     <p>
+      In keeping with the XML Schema WG's standard versioning
+      policy, this schema document will persist at
+      <a href="http://www.w3.org/2009/01/xml.xsd">
+       http://www.w3.org/2009/01/xml.xsd</a>.
+     </p>
+     <p>
+      At the date of issue it can also be found at
+      <a href="http://www.w3.org/2001/xml.xsd">
+       http://www.w3.org/2001/xml.xsd</a>.
+     </p>
+     <p>
+      The schema document at that URI may however change in the future,
+      in order to remain compatible with the latest version of XML
+      Schema itself, or with the XML namespace itself.  In other words,
+      if the XML Schema or XML namespaces change, the version of this
+      document at <a href="http://www.w3.org/2001/xml.xsd">
+       http://www.w3.org/2001/xml.xsd 
+      </a> 
+      will change accordingly; the version at 
+      <a href="http://www.w3.org/2009/01/xml.xsd">
+       http://www.w3.org/2009/01/xml.xsd 
+      </a> 
+      will not change.
+     </p>
+     <p>
+      Previous dated (and unchanging) versions of this schema 
+      document are at:
+     </p>
+     <ul>
+      <li><a href="http://www.w3.org/2009/01/xml.xsd">
+	http://www.w3.org/2009/01/xml.xsd</a></li>
+      <li><a href="http://www.w3.org/2007/08/xml.xsd">
+	http://www.w3.org/2007/08/xml.xsd</a></li>
+      <li><a href="http://www.w3.org/2004/10/xml.xsd">
+	http://www.w3.org/2004/10/xml.xsd</a></li>
+      <li><a href="http://www.w3.org/2001/03/xml.xsd">
+	http://www.w3.org/2001/03/xml.xsd</a></li>
+     </ul>
+    </div>
+   </div>
+  </xs:documentation>
+ </xs:annotation>
+
+</xs:schema>
+
