blob: 92196fd632bbb03b1f9e238d990bb7993cc04549 [file] [log] [blame]
Title: Tutorial Webapp
<P>Now we'll convert the tutorial that we created to a web application. The web part of it is done in JSP and is intentionally primitive from the UI perspective, to concentrate on Cayenne integration aspect. There may be some specifics depending on the web framework used (JSP, Struts, JSF, Click, WebWork, Tapestry, etc.), still the procedure outlined here is quite universal as it relies on the J2EE servlet specification.</P>
<P>A typical Cayenne web application works like this:</P>
<UL>
<LI>Cayenne files are loaded when an application context is started, using a special servlet filter.</LI>
<LI>User requests are intercepted by the filter, and the DataContext is bound to the request thread, so the application can access it easily from anywhere.</LI>
<LI>The same DataContext instance is reused within a single user session; different sessions use different DataContexts (and therefore different sets of objects).</LI>
</UL>
<P>For detailed discussion of web application deployment, check <A href="web-applications.html" title="Web Applications">this page</A>.</P>
<P>To run the web application from Eclipse, we will use <A href="http://jettylauncher.sourceforge.net/" class="external-link" rel="nofollow">JettyLauncher plugin</A>. If you are using something else (e.g. a Tomcat plugin) it should probably work as well.</P>
<UL>
<LI><A href="http://jetty.mortbay.org/" class="external-link" rel="nofollow">Download</A> and unpack Jetty web container (tested version is 5.1.3).</LI>
<LI><A href="http://sourceforge.net/project/showfiles.php?group_id=118870" class="external-link" rel="nofollow">Download</A> and unpack JettyLauncher to a local directory. Install it in Eclipse (<TT>&quot;Help &gt; Find and Install&quot;</TT>).</LI>
</UL>
<UL>
<LI>In Eclipse under &quot;cayenne-tutorial&quot; project folder create a new folder called &quot;webapp&quot; and a subfolder called &quot;WEB-INF&quot;.</LI>
<LI>Under &quot;WEB-INF&quot; create a new file &quot;web.xml&quot;. This is a standard web deployment descriptor:</LI>
</UL>
<P><SPAN class="image-wrap" style=""><IMG src="tutorial-webapp.data/webapp1.jpg" style="border: 0px solid black"></SPAN></P>
<P>The following is the contents of <TT>web.xml</TT>. All the DataContext magic described above is achieved by WebApplicationContextFilter.</P>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeHeader panelHeader" style="border-bottom-width: 1px;"><B>web.xml</B></DIV><DIV class="codeContent panelContent">
<PRE class="code-java">&lt;?xml version=<SPAN class="code-quote">&quot;1.0&quot;</SPAN> encoding=<SPAN class="code-quote">&quot;utf-8&quot;</SPAN>?&gt;
&lt;!DOCTYPE web-app
PUBLIC <SPAN class="code-quote">&quot;-<SPAN class="code-comment">//Sun Microsystems, Inc.//DTD Web Application 2.3//EN&quot;</SPAN>
</SPAN> <SPAN class="code-quote">&quot;http:<SPAN class="code-comment">//java.sun.com/dtd/web-app_2_3.dtd&quot;</SPAN>&gt;
</SPAN>&lt;web-app&gt;
&lt;display-name&gt;Cayenne Tutorial&lt;/display-name&gt;
&lt;filter&gt;
&lt;filter-name&gt;CayenneFilter&lt;/filter-name&gt;
&lt;filter-class&gt;org.objectstyle.cayenne.conf.WebApplicationContextFilter&lt;/filter-class&gt;
&lt;/filter&gt;
&lt;filter-mapping&gt;
&lt;filter-name&gt;CayenneFilter&lt;/filter-name&gt;
&lt;url-pattern&gt;/*&lt;/url-pattern&gt;
&lt;/filter-mapping&gt;
&lt;welcome-file-list&gt;
&lt;welcome-file&gt;index.jsp&lt;/welcome-file&gt;
&lt;/welcome-file-list&gt;
&lt;/web-app&gt;</PRE>
</DIV></DIV>
<UL>
<LI>Create the artist browser page - <TT>webapp/index.jsp</TT> file with the following contents:</LI>
</UL>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeHeader panelHeader" style="border-bottom-width: 1px;"><B>webapp/index.jsp</B></DIV><DIV class="codeContent panelContent">
<PRE class="code-java">&lt;%@ page language=<SPAN class="code-quote">&quot;java&quot;</SPAN> contentType=<SPAN class="code-quote">&quot;text/html&quot;</SPAN> %&gt;
&lt;%@ page <SPAN class="code-keyword">import</SPAN>=<SPAN class="code-quote">&quot;cayenne.tutorial.*&quot;</SPAN> %&gt;
&lt;%@ page <SPAN class="code-keyword">import</SPAN>=<SPAN class="code-quote">&quot;org.objectstyle.cayenne.*&quot;</SPAN> %&gt;
&lt;%@ page <SPAN class="code-keyword">import</SPAN>=<SPAN class="code-quote">&quot;org.objectstyle.cayenne.query.*&quot;</SPAN> %&gt;
&lt;%@ page <SPAN class="code-keyword">import</SPAN>=<SPAN class="code-quote">&quot;org.objectstyle.cayenne.exp.*&quot;</SPAN> %&gt;
&lt;%@ page <SPAN class="code-keyword">import</SPAN>=<SPAN class="code-quote">&quot;org.objectstyle.cayenne.access.*&quot;</SPAN> %&gt;
&lt;%@ page <SPAN class="code-keyword">import</SPAN>=<SPAN class="code-quote">&quot;java.util.*&quot;</SPAN> %&gt;
&lt;%
SelectQuery query = <SPAN class="code-keyword">new</SPAN> SelectQuery(Artist.class);
query.addOrdering(Artist.NAME_PROPERTY, <SPAN class="code-keyword">true</SPAN>);
DataContext context = DataContext.getThreadDataContext();
List artists = context.performQuery(query);
%&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Main&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h2&gt;Artists:&lt;/h2&gt;
&lt;% <SPAN class="code-keyword">if</SPAN>(artists.isEmpty()) {%&gt;
&lt;p&gt;No artists found&lt;/p&gt;
&lt;% } <SPAN class="code-keyword">else</SPAN> {
Iterator it = artists.iterator();
<SPAN class="code-keyword">while</SPAN>(it.hasNext()) {
Artist a = (Artist) it.next();
%&gt;
&lt;p&gt;&lt;a href=<SPAN class="code-quote">&quot;detail.jsp?id=&lt;%=DataObjectUtils.intPKForObject(a)%&gt;&quot;</SPAN>&gt; &lt;%=a.getName()%&gt; &lt;/a&gt;&lt;/p&gt;
&lt;%
}
} %&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href=<SPAN class="code-quote">&quot;detail.jsp&quot;</SPAN>&gt;Create <SPAN class="code-keyword">new</SPAN> artist...&lt;/a&gt;&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;</PRE>
</DIV></DIV>
<UL>
<LI>Create the artist editor page <TT>webapp/detail.jsp</TT> with the following content:</LI>
</UL>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeHeader panelHeader" style="border-bottom-width: 1px;"><B>webapp/detail.jsp</B></DIV><DIV class="codeContent panelContent">
<PRE class="code-java">&lt;%@ page language=<SPAN class="code-quote">&quot;java&quot;</SPAN> contentType=<SPAN class="code-quote">&quot;text/html&quot;</SPAN> %&gt;
&lt;%@ page <SPAN class="code-keyword">import</SPAN>=<SPAN class="code-quote">&quot;cayenne.tutorial.*&quot;</SPAN> %&gt;
&lt;%@ page <SPAN class="code-keyword">import</SPAN>=<SPAN class="code-quote">&quot;org.objectstyle.cayenne.*&quot;</SPAN> %&gt;
&lt;%@ page <SPAN class="code-keyword">import</SPAN>=<SPAN class="code-quote">&quot;org.objectstyle.cayenne.access.*&quot;</SPAN> %&gt;
&lt;%@ page <SPAN class="code-keyword">import</SPAN>=<SPAN class="code-quote">&quot;java.util.*&quot;</SPAN> %&gt;
&lt;%@ page <SPAN class="code-keyword">import</SPAN>=<SPAN class="code-quote">&quot;java.text.*&quot;</SPAN> %&gt;
&lt;%
DataContext context = DataContext.getThreadDataContext();
<SPAN class="code-object">String</SPAN> id = request.getParameter(<SPAN class="code-quote">&quot;id&quot;</SPAN>);
<SPAN class="code-comment">// find artist <SPAN class="code-keyword">for</SPAN> id
</SPAN> Artist artist = <SPAN class="code-keyword">null</SPAN>;
<SPAN class="code-keyword">if</SPAN>(id != <SPAN class="code-keyword">null</SPAN> &amp;&amp; id.trim().length() &gt; 0) {
artist = (Artist) DataObjectUtils.objectForPK(context, Artist.class, <SPAN class="code-object">Integer</SPAN>.parseInt(id));
}
<SPAN class="code-keyword">if</SPAN>(<SPAN class="code-quote">&quot;POST&quot;</SPAN>.equals(request.getMethod())) {
<SPAN class="code-comment">// <SPAN class="code-keyword">if</SPAN> no id is saved in the hidden field, we are dealing with
</SPAN> <SPAN class="code-comment">// create <SPAN class="code-keyword">new</SPAN> artist request
</SPAN> <SPAN class="code-keyword">if</SPAN>(artist == <SPAN class="code-keyword">null</SPAN>) {
artist = (Artist) context.newObject(Artist.class);
}
<SPAN class="code-comment">// note that in a real application we would so dome validation ...
</SPAN> <SPAN class="code-comment">// here we just hope the input is correct
</SPAN> artist.setName(request.getParameter(<SPAN class="code-quote">&quot;name&quot;</SPAN>));
artist.setDateOfBirthString(request.getParameter(<SPAN class="code-quote">&quot;dateOfBirth&quot;</SPAN>));
context.commitChanges();
response.sendRedirect(<SPAN class="code-quote">&quot;index.jsp&quot;</SPAN>);
}
<SPAN class="code-keyword">if</SPAN>(artist == <SPAN class="code-keyword">null</SPAN>) {
<SPAN class="code-comment">// create <SPAN class="code-keyword">transient</SPAN> artist <SPAN class="code-keyword">for</SPAN> the form response rendering
</SPAN> artist = <SPAN class="code-keyword">new</SPAN> Artist();
}
<SPAN class="code-object">String</SPAN> name = artist.getName() == <SPAN class="code-keyword">null</SPAN> ? &quot;&quot; : artist.getName();
<SPAN class="code-object">String</SPAN> dob = artist.getDateOfBirth() == <SPAN class="code-keyword">null</SPAN>
? <SPAN class="code-quote">&quot;&quot; : <SPAN class="code-keyword">new</SPAN> SimpleDateFormat(&quot;</SPAN>yyyyMMdd&quot;).format(artist.getDateOfBirth());
%&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Artist Details&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h2&gt;Artists Details&lt;/h2&gt;
&lt;form name=<SPAN class="code-quote">&quot;EditArtist&quot;</SPAN> action=<SPAN class="code-quote">&quot;detail.jsp&quot;</SPAN> method=<SPAN class="code-quote">&quot;POST&quot;</SPAN>&gt;
&lt;input type=<SPAN class="code-quote">&quot;hidden&quot;</SPAN> name=<SPAN class="code-quote">&quot;id&quot;</SPAN> value=<SPAN class="code-quote">&quot;&lt;%= id != <SPAN class="code-keyword">null</SPAN> ? id : &quot;</SPAN><SPAN class="code-quote">&quot; %&gt;&quot;</SPAN> /&gt;
&lt;table border=<SPAN class="code-quote">&quot;0&quot;</SPAN>&gt;
&lt;tr&gt;
&lt;td&gt;Name:&lt;/td&gt;
&lt;td&gt;&lt;input type=<SPAN class="code-quote">&quot;text&quot;</SPAN> name=<SPAN class="code-quote">&quot;name&quot;</SPAN> value=<SPAN class="code-quote">&quot;&lt;%= name %&gt;&quot;</SPAN>/&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Date of Birth (yyyyMMdd):&lt;/td&gt;
&lt;td&gt;&lt;input type=<SPAN class="code-quote">&quot;text&quot;</SPAN> name=<SPAN class="code-quote">&quot;dateOfBirth&quot;</SPAN> value=<SPAN class="code-quote">&quot;&lt;%= dob %&gt;&quot;</SPAN>/&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td align=<SPAN class="code-quote">&quot;right&quot;</SPAN>&gt;&lt;input type=<SPAN class="code-quote">&quot;submit&quot;</SPAN> value=<SPAN class="code-quote">&quot;Save&quot;</SPAN> /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</PRE>
</DIV></DIV>
<UL>
<LI>Run the application. Go to &quot;Run &gt; Run...&quot; menu, select &quot;Jetty Web&quot;, right click and select &quot;New&quot;</LI>
<LI>Make sure project is &quot;cayenne-tutorial&quot;, webapp root is &quot;webapp&quot; and Jetty home points to a directory where you unpacked Jetty:</LI>
</UL>
<P><SPAN class="image-wrap" style=""><IMG src="tutorial-webapp.data/jetty-run.jpg" style="border: 0px solid black"></SPAN></P>
<UL>
<LI>Click &quot;Apply&quot; and &quot;Run&quot;.</LI>
<LI>You will see some Cayenne logs, indicating that the filter was loaded and Cayenne is successfully runing in web app.</LI>
<LI>Now go to <A href="http://localhost:8080/" class="external-link" rel="nofollow">http://localhost:8080/</A> URL. You should see &quot;No artists found message&quot; in the web browser and the following output in the Eclipse console:</LI>
</UL>
<DIV class="preformatted panel" style="border-width: 1px;"><DIV class="preformattedContent panelContent">
<PRE>INFO QueryLogger: --- will run 1 query.
INFO QueryLogger: Opening connection: jdbc:derby:/Users/andrus/Desktop/testdb;create=true
Login: null
Password: *******
INFO QueryLogger: +++ Connecting: SUCCESS.
INFO QueryLogger: --- transaction started.
INFO QueryLogger: Detected and installed adapter: org.objectstyle.cayenne.dba.derby.DerbyAdapter
INFO QueryLogger: SELECT t0.DATE_OF_BIRTH, t0.NAME, t0.ID FROM ARTIST t0 ORDER BY t0.NAME - prepared in 32 ms.
INFO QueryLogger: === returned 0 rows. - took 461 ms.
INFO QueryLogger: +++ transaction committed.</PRE>
</DIV></DIV>
<UL>
<LI>You can click on &quot;Create new artist&quot; link to create artists. Existing artists can be edited by clicking on their name:</LI>
</UL>
<P><SPAN class="image-wrap" style=""><IMG src="tutorial-webapp.data/artists.jpg" style="border: 0px solid black"></SPAN></P>
<P>You are done with the tutorial!</P>