blob: d4386086d58b08b49a6d0c9792a88e90d5bd8fbf [file] [log] [blame]
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title xmlns:d="http://docbook.org/ns/docbook">Chapter&nbsp;9.&nbsp;Converting to Web Application</title><link rel="stylesheet" type="text/css" href="css/cayenne-doc.css"><meta xmlns:d="http://docbook.org/ns/docbook" name="keywords" content="Cayenne 3.1 documentation"><meta xmlns:d="http://docbook.org/ns/docbook" name="description" content="User documentation for Apache Cayenne version 3.1"><link rel="home" href="index.html" title="Getting Started with Cayenne"><link rel="up" href="getting-started-part4.html" title="Part&nbsp;IV.&nbsp;Converting to Web Application"><link rel="prev" href="getting-started-part4.html" title="Part&nbsp;IV.&nbsp;Converting to Web Application"><script xmlns:d="http://docbook.org/ns/docbook" type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-7036673-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div xmlns:d="http://docbook.org/ns/docbook" class="navheader"><table width="100%" summary="Navigation header"><tr><th class="versioninfo">v.3.1 (3.1)</th><th align="center">Chapter&nbsp;9.&nbsp;Converting to Web Application</th><th></th></tr><tr><td width="20%" align="left"><a accesskey="p" href="getting-started-part4.html">Prev</a>&nbsp;</td><th width="60%" align="center"><a accesskey="u" href="getting-started-part4.html">Part&nbsp;IV.&nbsp;Converting to Web Application</a></th><td width="20%" align="right">&nbsp;</td></tr></table><hr></div><div class="chapter" title="Chapter&nbsp;9.&nbsp;Converting to Web Application"><div class="titlepage"><div><div><h2 class="title"><a name="d0e493"></a>Chapter&nbsp;9.&nbsp;Converting to Web Application</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="ch09.html#converting-to-webapp">Converting Tutorial to a Web Application</a></span></dt><dt><span class="section"><a href="ch09.html#running-webapp">Running Web Application</a></span></dt></dl></div><p>This chapter shows how to work with Cayenne in a web application.</p><div class="section" title="Converting Tutorial to a Web Application"><div class="titlepage"><div><div><h2 class="title"><a name="converting-to-webapp"></a>Converting Tutorial to a Web Application</h2></div></div></div><p>The web part of the web application tutorial is done in JSP, which is the least common
denominator of the Java web technologies, and is intentionally simplistic from the UI
perspective, to concentrate on Cayenne integration aspect, rather than the interface. A
typical Cayenne web application works like this:</p><div class="itemizedlist"><ul class="itemizedlist"><li class="listitem"><p>Cayenne configuiration is loaded when an application context is started, using
a special servlet filter.</p></li><li class="listitem"><p>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.</p></li><li class="listitem"><p>The same DataContext instance is reused within a single user session;
different sessions use different DataContexts (and therefore different sets of
objects). <span class="italic">The context can be scoped differently
depending on the app specifics. For the tutorial we'll be using a
session-scoped context.</span></p></li></ul></div><p>So let's convert the tutorial that we created to a web application:</p><div class="itemizedlist"><ul class="itemizedlist"><li class="listitem"><p>In Eclipse under "tutorial" project folder create a new folder
"src/main/webapp/WEB-INF".</p></li><li class="listitem"><p>Under "WEB-INF" create a new file "web.xml" (a standard web app descriptor): </p><p>
<span class="bold"><strong>web.xml</strong></span>
</p><pre class="programlisting">&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd"&gt;
&lt;web-app&gt;
&lt;display-name&gt;Cayenne Tutorial&lt;/display-name&gt;
&lt;!-- This filter bootstraps ServerRuntime and then provides each request thread
with a session-bound DataContext. Note that the name of the filter is important,
as it points it to the right named configuration file.
--&gt;
&lt;filter&gt;
&lt;filter-name&gt;cayenne-project&lt;/filter-name&gt;
&lt;filter-class&gt;org.apache.cayenne.configuration.web.CayenneFilter&lt;/filter-class&gt;
&lt;/filter&gt;
&lt;filter-mapping&gt;
&lt;filter-name&gt;cayenne-project&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></li><li class="listitem"><p>Create the artist browser page src/main/webapp/index.jsp file with the
following contents: </p><p><span class="bold"><strong>webapp/index.jsp</strong></span>
</p><pre class="programlisting">&lt;%@ page language="java" contentType="text/html" %&gt;
&lt;%@ page import="org.example.cayenne.persistent.*" %&gt;
&lt;%@ page import="org.apache.cayenne.*" %&gt;
&lt;%@ page import="org.apache.cayenne.query.*" %&gt;
&lt;%@ page import="org.apache.cayenne.exp.*" %&gt;
&lt;%@ page import="java.util.*" %&gt;
&lt;%
SelectQuery query = new SelectQuery(Artist.class);
query.addOrdering(Artist.NAME_PROPERTY, SortOrder.ASCENDING);
ObjectContext context = BaseContext.getThreadObjectContext();
List&lt;Artist&gt; 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;% if(artists.isEmpty()) {%&gt;
&lt;p&gt;No artists found&lt;/p&gt;
&lt;% } else {
for(Artist a : artists) {
%&gt;
&lt;p&gt;&lt;a href="detail.jsp?id=&lt;%=Cayenne.intPKForObject(a)%&gt;"&gt; &lt;%=a.getName()%&gt; &lt;/a&gt;&lt;/p&gt;
&lt;%
}
} %&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="detail.jsp"&gt;Create new artist...&lt;/a&gt;&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt; </pre></li><li class="listitem"><p>Create the artist editor page src/main/webapp/detail.jsp with the following
content: </p><p><span class="bold"><strong>webapp/detail.jsp</strong></span>
</p><pre class="programlisting">&lt;%@ page language="java" contentType="text/html" %&gt;
&lt;%@ page import="org.example.cayenne.persistent.*" %&gt;
&lt;%@ page import="org.apache.cayenne.*" %&gt;
&lt;%@ page import="java.util.*" %&gt;
&lt;%@ page import="java.text.*" %&gt;
&lt;%
ObjectContext context = BaseContext.getThreadObjectContext();
String id = request.getParameter("id");
// find artist for id
Artist artist = null;
if(id != null &amp;&amp; id.trim().length() &gt; 0) {
artist = Cayenne.objectForPK(context, Artist.class, Integer.parseInt(id));
}
if("POST".equals(request.getMethod())) {
// if no id is saved in the hidden field, we are dealing with
// create new artist request
if(artist == null) {
artist = context.newObject(Artist.class);
}
// note that in a real application we would so dome validation ...
// here we just hope the input is correct
artist.setName(request.getParameter("name"));
artist.setDateOfBirthString(request.getParameter("dateOfBirth"));
context.commitChanges();
response.sendRedirect("index.jsp");
}
if(artist == null) {
// create transient artist for the form response rendering
artist = new Artist();
}
String name = artist.getName() == null ? "" : artist.getName();
String dob = artist.getDateOfBirth() == null
? "" : new SimpleDateFormat("yyyyMMdd").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="EditArtist" action="detail.jsp" method="POST"&gt;
&lt;input type="hidden" name="id" value="&lt;%= id != null ? id : "" %&gt;" /&gt;
&lt;table border="0"&gt;
&lt;tr&gt;
&lt;td&gt;Name:&lt;/td&gt;
&lt;td&gt;&lt;input type="text" name="name" value="&lt;%= name %&gt;"/&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="text" name="dateOfBirth" value="&lt;%= dob %&gt;"/&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;input type="submit" value="Save" /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</pre></li></ul></div></div><div class="section" title="Running Web Application"><div class="titlepage"><div><div><h2 class="title"><a name="running-webapp"></a>Running Web Application</h2></div></div></div><p>To run the web application we'll use "maven-jetty-plugin". To activate it, let's add
the following piece of code to the "pom.xml" file, following the "dependencies" section
and save the POM:</p><pre class="programlisting">&lt;build&gt;
&lt;plugins&gt;
&lt;plugin&gt;
&lt;groupId&gt;org.mortbay.jetty&lt;/groupId&gt;
&lt;artifactId&gt;maven-jetty-plugin&lt;/artifactId&gt;
&lt;version&gt;6.1.22&lt;/version&gt;
&lt;/plugin&gt;
&lt;/plugins&gt;
&lt;/build&gt;</pre><div class="itemizedlist"><ul class="itemizedlist"><li class="listitem"><p>Go to "Run &gt; Run Configurations..." menu, select "Maven Build", right click
and select "New"</p></li><li class="listitem"><p>Make sure you fill "Name", "Base directory" and "Goals" fields as shown on the
screenshot:</p><p><span class="inlinemediaobject"><img src="images/eclipse-mvnrun.png"></span></p></li></ul></div><div class="itemizedlist"><ul class="itemizedlist"><li class="listitem"><p>Click "Apply" and "Run". On the first execution it may take a few minutes for
Jetty plugin to download all dependencies, but eventually you'll see the logs
like this:</p><pre class="programlisting">[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building tutorial 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
...
[INFO] Configuring Jetty for project: tutorial
[INFO] Webapp source directory = /.../tutorial/src/main/webapp
[INFO] Reload Mechanic: automatic
[INFO] Classes = /.../tutorial/target/classes
[INFO] Context path = /tutorial
[INFO] Tmp directory = determined at runtime
[INFO] Web defaults = org/mortbay/jetty/webapp/webdefault.xml
[INFO] Web overrides = none
[INFO] web.xml file = /.../tutorial/src/main/webapp/WEB-INF/web.xml
[INFO] Webapp directory = /.../tutorial/src/main/webapp
[INFO] Starting jetty 6.1.22 ...
INFO::jetty-6.1.22
INFO::No Transaction manager found - if your webapp requires one, please configure one.
INFO::Started SelectChannelConnector@0.0.0.0:8080
[INFO] Started Jetty Server</pre></li></ul></div><div class="itemizedlist"><ul class="itemizedlist"><li class="listitem"><p>So the Jetty container just started.</p></li><li class="listitem"><p>Now go to <span class="italic">http://localhost:8080/tutorial/</span>
URL. You should see "No artists found message" in the web browser and
the following output in the Eclipse console:</p><pre class="programlisting">INFO: Loading XML configuration resource from file:/.../tutorial/target/classes/cayenne-project.xml
INFO: loading user name and password.
INFO: Created connection pool: jdbc:derby:memory:testdb;create=true
Driver class: org.apache.derby.jdbc.EmbeddedDriver
Min. connections in the pool: 1
Max. connections in the pool: 1
INFO: Opening connection: jdbc:derby:memory:testdb;create=true
Login: null
Password: *******
INFO: +++ Connecting: SUCCESS.
INFO: Detected and installed adapter: org.apache.cayenne.dba.derby.DerbyAdapter
INFO: --- transaction started.
INFO: No schema detected, will create mapped tables
INFO: CREATE TABLE GALLERY (ID INTEGER NOT NULL, NAME VARCHAR (200), PRIMARY KEY (ID))
INFO: CREATE TABLE ARTIST (DATE_OF_BIRTH DATE, ID INTEGER NOT NULL, NAME VARCHAR (200), PRIMARY KEY (ID))
INFO: CREATE TABLE PAINTING (ARTIST_ID INTEGER, GALLERY_ID INTEGER, ID INTEGER NOT NULL,
NAME VARCHAR (200), PRIMARY KEY (ID))
INFO: ALTER TABLE PAINTING ADD FOREIGN KEY (ARTIST_ID) REFERENCES ARTIST (ID)
INFO: ALTER TABLE PAINTING ADD FOREIGN KEY (GALLERY_ID) REFERENCES GALLERY (ID)
INFO: CREATE TABLE AUTO_PK_SUPPORT (
TABLE_NAME CHAR(100) NOT NULL, NEXT_ID BIGINT NOT NULL, PRIMARY KEY(TABLE_NAME))
INFO: DELETE FROM AUTO_PK_SUPPORT WHERE TABLE_NAME IN ('ARTIST', 'GALLERY', 'PAINTING')
INFO: INSERT INTO AUTO_PK_SUPPORT (TABLE_NAME, NEXT_ID) VALUES ('ARTIST', 200)
INFO: INSERT INTO AUTO_PK_SUPPORT (TABLE_NAME, NEXT_ID) VALUES ('GALLERY', 200)
INFO: INSERT INTO AUTO_PK_SUPPORT (TABLE_NAME, NEXT_ID) VALUES ('PAINTING', 200)
INFO: SELECT t0.DATE_OF_BIRTH, t0.NAME, t0.ID FROM ARTIST t0 ORDER BY t0.NAME - prepared in 43 ms.
INFO: === returned 0 rows. - took 56 ms.
INFO: +++ transaction committed.</pre></li></ul></div><div class="itemizedlist"><ul class="itemizedlist"><li class="listitem"><p>You can click on "Create new artist" link to create artists. Existing artists
can be edited by clicking on their name:</p><p><span class="inlinemediaobject"><img src="images/firefox-webapp.png"></span></p></li></ul></div><p>You are done with the tutorial!</p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="getting-started-part4.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="getting-started-part4.html">Up</a></td><td width="40%" align="right">&nbsp;</td></tr><tr><td width="40%" align="left" valign="top">Part&nbsp;IV.&nbsp;Converting to Web Application&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;</td></tr></table></div></body></html>