Fix link
[yaz4j-moved-to-github.git] / examples / zgate / doc / ARTICLE
1 BUILDING A SIMPLE HTTP-TO-Z3950 GATEWAY USING YAZ4J AND TOMCAT
2 ==============================================================
3
4 [Yaz4J](http://www.indexdata.com/yaz4j) is a wrapper library over the 
5 client-specific parts of YAZ, a C-based Z39.50 toolkit, and allows you to use 
6 the ZOOM API directly from Java. Initial version of Yaz4j has been written
7 by Rob Styles from [Talis](http://www.talis.com) and the project is now 
8 developed and maintained at IndexData.
9 [ZOOM](http://zoom.z3950.org/api/zoom-1.4.html) is a relatively straightforward 
10 API and with a few lines of code you can write a basic application that can 
11 establish connection to a Z39.50 server. 
12 Here we will try to build a very simple HTTP-to-Z3950 gateway using yaz4j and 
13 the Java Servlet technology.
14
15 ### COMPILING AND INSTALLING YAZ4J
16
17 Yaz4j is still an experimental piece of software and as such is not distributed
18 via Index Data's public Debian Apt repository and there is no Windows build (yet)
19 either. While it is possible to use the pre-built Linux binaries, users of 
20 other OSes will have to compile yaz4j from source. No need to worry (yet) - the 
21 process of compiling yaz4j is quite simple and we will be up and running in no 
22 time :).
23
24 As a prerequisite, to complete th build process you will need JDK, Maven, Swig
25 and Yaz (development package) installed on your machine. On Debian/Ubuntu you
26 can get those easily via apt:
27
28     apt-get install sun-java6-jdk maven2 libyaz4-dev swig
29
30
31 The Yaz4j's source code can be checked-out out from our 
32 [Git repository](http://git.indexdata.com/?p=yaz4j.git;a=summary), and assuming
33 you have Git installed on your machine you can do that with:
34
35     git clone git://git.indexdata.com/yaz4j
36
37 The compilation of both native and Java source code is controlled by Maven2, 
38 to build the library, invoke the following commands:
39
40     cd yaz4j
41     mvn install
42
43 That's it. If the build has completed successfully you end up with two files: 
44 os-independent jar archive with Java ZOOM API classes 
45 (yaz4j/any/target/yaz4j-any-VERSION.jar) and os-dependent shared library 
46 (yaz4j/linux/target/libyaz4j.so or yaz4j/win32/target/yaz4j.dll) that contains 
47 all necessary JNI "glue" to make the native calls possible from Java. If we were
48 writing a command line Java application, like any other external Java library, 
49 yaz4j-any-VERSION.jar would have to be placed on your application classpath 
50 and the native, shared library would have to be added to your system shared 
51 library path (LD_LIBRARY_PATH on linux, PATH on Windows) or specified as a 
52 Java system property (namely the java.library.path) just before your 
53 application is executed:
54
55     java -cp /path/to/yaz4j-*.jar -Djava.library.path=/path/to/libyaz4j.so MyApp
56
57 ### SETTING UP THE DEVELOPMENT ENVIRONMENT
58
59 Setting up a development/runtime environment for a web (servlet) application is 
60 a bit more complicated. First, you are not invoking the JVM directly, but the 
61 servlet container (e.g Tomcat) run-script is doing that for you. At this 
62 point the shared library (so or dll) has to be placed on the servlet container's
63 shared libraries load path. Unless your library is deployed to the standard 
64 system location for shared libs (`/usr/lib` on Linux) or it's location is 
65 already added to the path, the easiest way to do this in Tomcat is by editing 
66 (create it if it does not exist) the `CATALINA_HOME/bin/setenv.sh` (setenv.bat 
67 on Windows) script and putting the following lines in there:
68
69     LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/path/to/libyaz4j.so
70     export LD_LIBRARY_PATH
71
72 on Windows (though no Windows build is yet provided)
73
74      set PATH=%PATH;X:\path\to\yaz4j.dll
75
76 That's one way of doing it, another would be to alter the standard set of
77 arguments passed to the JVM before the Tomcat starts and add 
78 `-Djava.library.path=/path/to/lib there`. Depending on a situation this might 
79 be preferable/easier (on Debian/Ubuntu you can specify JVM arguments in the
80 `/etc/default/tomcat6` file).
81
82 With the shared library installed we need to install the pure-Java yaz4j-any*jar
83 with ZOOM API classes by placing it in Tomcat's `lib` directory 
84 (CATALINA_HOME/lib). As this library makes the Java System call to load the 
85 native library into the JVM you cannot simply package it along with your web
86 application (inside the .war file) - it would try to load the library each time
87 you deploy the webapp and all consecutive deployments would fail.
88
89 ### WRITING A SERVLET-BASED GATEWAY
90
91 With your servlet environment set up all that is left is to write the actual 
92 application (peanuts :)). At IndexData we use Maven for managing builds of our 
93 Java software components but Maven is also a great tool for quickly starting up 
94 a project. To generate a skeleton for our webapp use the Maven archetype plugin:
95
96     mvn -DarchetypeVersion=1.0.1 -Darchetype.interactive=false \
97     -DarchetypeArtifactId=webapp-jee5 -DarchetypeGroupId=org.codehaus.mojo.archetypes \ 
98     -Dpackage=com.indexdata.zgate -DgroupId=com.indexdata -DartifactId=zgate \ 
99     archetype:generate --batch-mode
100
101 This will generate a basic webapp project structure:
102
103 <blockcode>
104
105 |-- pom.xml
106 `-- src
107 |-- main
108 |   |-- java
109 |   |   `-- com
110 |   |       `-- indexdata
111 |   |           `-- zgate
112 |   `-- webapp
113 |       |-- WEB-INF
114 |       |   `-- web.xml
115 |       `-- index.jsp
116 `-- test
117     `-- java
118         `-- com
119             `-- indexdata
120                 `-- zgate
121
122 </blockcode>
123
124 Maven has already added basic JEE APIs for web development as the project 
125 dependencies, we need to do the same for yaz4j, so edit the `pom.xml` and 
126 add the following lines in the `dependencies` section:
127
128 <blockcode type="xml">
129
130 <dependency>
131   <groupId>org.yaz4j</groupId>
132   <artifactId>yaz4j-any</artifactId>
133   <version>VERSION</version>
134   <scope>provided</scope>
135 </dependency>
136
137 </blockcode>
138
139 It's crucial that the scope of this dependency is set to `provided` otherwise
140 the library would end up packaged in the .war archive and we don't want that.
141
142 The implementation of our simple gateway will be contained in a single servlet -
143 `ZGateServlet` - which we need to place under `src/main/webapp/com/indexdata/zgate.` 
144 The gateway will work by answering HTTP GET requests and will be controlled 
145 solely by HTTP parameters, the servlet doGet method is shown below:
146
147 <blockcode type="java">
148
149 protected void doGet(HttpServletRequest request, HttpServletResponse response)
150   throws ServletException, IOException {
151     String zurl = request.getParameter("zurl");
152     if (zurl == null || zurl.isEmpty()) { 
153       response.sendError(400, "Missing parameter 'zurl'"); 
154       return; 
155     }
156
157     String query = request.getParameter("query");
158     if (query == null || query.isEmpty()) { 
159       response.sendError(400, "Missing parameter 'query'"); 
160       return; 
161     }
162
163     String syntax = request.getParameter("syntax");
164     if (syntax == null || syntax.isEmpty()) { 
165       response.sendError(400, "Missing parameter 'syntax'"); 
166       return; 
167     }
168
169     int maxrecs=10;
170     if (request.getParameter("maxrecs") != null 
171       && !request.getParameter("maxrecs").isEmpty()) {
172       try {
173         maxrecs = Integer.parseInt(request.getParameter("maxrecs"));
174       } catch (NumberFormatException nfe) {
175         response.sendError(400, "Malformed parameter 'maxrecs'");
176         return;
177       }
178     }
179
180     response.getWriter().println("SEARCH PARAMETERS");
181     response.getWriter().println("zurl: " + zurl);
182     response.getWriter().println("query: " + query);
183     response.getWriter().println("syntax: " + syntax);
184     response.getWriter().println("maxrecs: " + maxrecs);
185     response.getWriter().println();
186
187     Connection con = new Connection(zurl, 0);
188     con.setSyntax(syntax);
189     try {
190       con.connect();
191       ResultSet set = con.search(query, Connection.QueryType.PrefixQuery);
192       response.getWriter().println("Showing " + maxrecs + " of "+set.getSize());
193       response.getWriter().println();
194       for(int i=0; i<set.getSize() && i<maxrecs; i++) {
195         Record rec = set.getRecord(i);
196         response.getWriter().print(rec.render());
197       }
198     } catch (ZoomException ze) {
199       throw new ServletException(ze);
200     } finally {
201       con.close();
202     }
203   }
204
205 </blockcode>
206
207 With the code in-place we can try to compile the project:
208
209     mvn compile
210
211 If all is OK, the next step is to register our servlet and map it to an URL in 
212 src/main/webapp/WEB-INF/web.xml:
213
214 <blockcode type="xml">
215
216 <servlet>
217   <servlet-name>ZgateServlet</servlet-name>
218   <servlet-class>com.indexdata.zgate.ZgateServlet</servlet-class>
219 </servlet>
220 <servlet-mapping>
221   <servlet-name>ZgateServlet</servlet-name>
222   <url-pattern>/zgate</url-pattern>
223 </servlet-mapping>
224
225 </blockcode>
226
227 On top of that, we will also  make sure that our servlet is automatically 
228 triggered when accessing the root path of our application:
229
230 <blockcode type="xml">
231
232 <welcome-file-list>
233   <welcome-file>zgate</welcome-file>
234   <welcome-file>index.jsp</welcome-file>
235 </welcome-file-list>
236
237 </blockcode>
238
239 Now we are ready to build our webapp:
240
241     mvn package
242
243 The resulting .war archive is located under `target/zgate.war`, we can deploy
244 it on tomcat (e.g by using the `/admin` Tomcat admin  console) and test by 
245 issuing the following request with your browser or curl 
246 (assuming Tomcat is running on localhost:8080):
247
248     http://localhost:8080/zgate/?zurl=z3950.loc.gov:7090/voyager&query=@attr%201=7%200253333490&syntax=usmarc
249
250   
251 That's it! You just build yourself a HTTP-to-Z3950 gateway! Just be careful
252 with exposing it to the outside world - it's not very secure and could be 
253 easily exploited. The source code and the gateway's Maven project is available 
254 in the Yaz4j's Git repo under `examples/zgate`. In the meantime, IndexData 
255 is working on a Debian/Ubuntu package to make the installation of Yaz4j and 
256 Tomcat configuration greatly simplified - so stay tuned!. If you are interested 
257 in Windows support - e.g. Visual Studio based build or an installer - please 
258 let us know.