At line 1 changed 1 line. |
__Part V:__ [Adding Validation and List Screen|ValidationAndList] - Adding validation logic to the personForm so that firstName and lastName are required fields and adding a list screen to display all person records in the database. |
__Part IV:__ [Adding Validation and List Screen|ValidationAndList] - Adding validation logic to the PersonForm so that firstName and lastName are required fields and adding a list screen to display all person records in the database. |
At line 3 changed 1 line. |
;:''This tutorial depends on __Part IV:__ [Configuring Tiles and Action CRUD methods|ConfiguringTiles].'' |
;:''This tutorial depends on __Part III:__ [Creating Actions and JSPs|CreateActions].'' |
At line 6 changed 1 line. |
This tutorial will show you how to add Validation logic (client and server-side) to the personForm object using Struts' Validator. We'll also create a list screen using the [Display Tag Library|http://displaytag.sf.net] to display all the people in the database. |
This tutorial will show you how to add Validation logic (client and server-side) to the PersonForm object using Struts' Validator. We'll also create a list screen using the [Display Tag Library|http://displaytag.sf.net] to display all the people in the database. |
At line 11 changed 1 line. |
* [1] Add XDoclet tags (@struts.validator) to Person.java |
* [1] Add XDoclet Validator tags to Person.java |
At line 13 changed 2 lines. |
* [3] Add ''testGetPersons'' methods to DAO and Manager Tests |
* [4] Add ''getPersons'' methods to DAO and Manager Interfaces and Implementation classes |
* [3] Add ''testGetPeople'' methods to DAO and Manager Tests |
* [4] Add ''getPeople'' methods to PersonDao and Manager |
At line 20 changed 2 lines. |
!!Add XDoclet tags (@struts.validator) to Person.java [#1] |
To use the Validator with Struts, normally you have to write a validation.xml file by hand. If you're not using AppFuse, you also have to configure the Validator Plugin and error keys in your ApplicationResources_en.properties. For more information on this, see the [Validation Made Easy Tutorial|http://www.reumann.net/do/struts/lesson3/step8]. |
!!Add XDoclet Validator tags to Person.java [#1] |
To use the Struts Validator, normally you have to write a validation.xml file by hand. If you're not using AppFuse, you also have to configure the Validator Plugin and error keys in your ApplicationResources.properties. For more information on this, see the [Validation Made Easy Tutorial|http://www.learntechnology.net/struts-lesson-3.do] (there's also a [rich set of tutorials|http://www.learntechnology.net/struts-lesson-1.do] for Struts itself). |
At line 23 changed 1 line. |
Using XDoclet, it's much easier - we just need to add a couple of ''@struts.validator'' tags to our POJO (Person.java). So open it up (src/dao/**/persistence/Person.java) and modify your setFirstName and setLastName methods to resemble the following: |
Thanks to XDoclet, it's much easier - you just need to add a couple of ''@struts.validator'' tags to the Person class. Open it up (src/dao/**/model/Person.java) and modify the getFirstName() and getLastName() methods to include ''@struts.validator type="required"'' tags. |
At line 28 removed 1 line. |
* @return Returns the firstName. |
At line 37 removed 1 line. |
* @return Returns the lastName. |
At line 46 changed 1 line. |
;:__Spring MVC:__ If you're using Spring for your MVC layer - use @spring.validator tags and put them on the ''setter'' methods, rather than the getters. |
You can also add a ''msgkey'' attribute to this tag to override the default message key for this error. |
At line 48 removed 2 lines. |
I should mention that you can also add a ''msgkey'' attribute to this tag to override the default message key for this error. |
|
At line 55 changed 1 line. |
The default key for type="required" is already ''errors.required'', so I usually leave it to the default. This key is defined in web/WEB-INF/classes/ApplicationResources.properties. You'll notice that we put these tags on the ''getters'' of this class even though the [XDoclet documentation|http://xdoclet.sourceforge.net/tags/apache-tags.html#@struts.validator%20(0..*)] says to put them on the setters. This is because we are generating our PersonForm.java - the template file (metadata/template/struts_form.xdt) takes care of putting these tags onto the setters in the generated file. |
The default key for type="required" is already ''errors.required'', so I usually leave it to the default. This key is defined in web/WEB-INF/classes/ApplicationResources_*.properties. You'll notice that we put these tags on the ''getters'' of this class even though the [XDoclet documentation|http://xdoclet.sourceforge.net/tags/apache-tags.html#@struts.validator%20(0..*)] says to put them on the setters. This is because we are generating our PersonForm.java - the template file (metadata/template/struts_form.xdt) takes care of putting these tags onto the setters in the generated file. |
At line 57 changed 1 line. |
Now if you save Person.java and run __ant webdoclet__, a validation.xml file will be generated in build/appfuse/WEB-INF/. It's contents should have now have an entry for "personForm". |
Now if you save Person.java and run __ant clean webdoclet__, a validation.xml file will be generated in build/appfuse/WEB-INF/. Its contents should have now have an entry for "personForm". |
At line 75 changed 1 line. |
To enable validation in our personForm.jsp, you'll need to make sure your personForm.jsp has the following at the bottom: |
Client-side validation is enabled by default in personForm.jsp. There is an <html:javascript> JSP tag and script at the bottom of this page that enables it. The following should already exist (thanks to AppGen) - but you might need to uncomment it if you commented it out in the last tutorial. |
At line 78 changed 1 line. |
dynamicJavascript="true" staticJavascript="false"/> |
dynamicJavascript="true" staticJavascript="false"/> |
At line 80 changed 1 line. |
src="<html:rewrite page="/scripts/validator.jsp"/>"></script>}}} |
src="<html:rewrite page="/scripts/validator.jsp"/>"></script>}}} |
At line 78 added 2 lines. |
%%note __NOTE:__ If you have nested objects with validation rules, those will be picked up and put into validation.xml. This is because an @struts.validator tag gets added to the setter of the nested object when the form is generated (using metadata/templates/struts_form.xdt). If you have many-to-many bi-directional relationships between objects, this can cause a problem. There are two solutions to fix this. The first is to remove the @struts.validator tag from struts_form.xdt and manually place it on the setter in your POJO. The second is [described here|https://appfuse.dev.java.net/issues/show_bug.cgi?id=88].%% |
|
At line 84 changed 1 line. |
Now that we have Validation configured for this form, whenever this form is used in an action-mapping with validate="true", these rules will be applied. In the [last tutorial|ConfiguringTiles], we added the "savePerson" action-mapping for PersonAction. The XDoclet tags for this action-mapping were: |
Now that you have Validation configured for this form, whenever this form is used in an action-mapping with validate="true", these rules will be applied. In the [last tutorial|CreateActions], we added the "savePerson" action-mapping for PersonAction. The XDoclet tags for this action-mapping were: |
At line 89 changed 1 line. |
* validate="true" parameter="action" input="editPerson.do?action=Edit" |
* validate="true" parameter="method" input="edit" |
At line 92 changed 1 line. |
So now, as long as your web/pages/personForm.jsp has <html:form action="savePerson">, validation should kick in when we try to save this form. Run __ant db-load deploy__, start Tomcat and go to [http://localhost:8080/appfuse/editPerson.do?id=1]. |
So now, as long as your web/pages/personForm.jsp has <html:form action="savePerson">, validation should kick in when we try to save this form. Run __ant db-load deploy__, start Tomcat and go to [http://localhost:8080/appfuse/editPerson.html?id=1]. |
At line 100 changed 1 line. |
To make sure things are ''really'' working as expected, you can turn off JavaScript and ensure the server-side validation is working. This is easy in [Mozilla Firebird|http://www.mozilla.org/products/firebird/] (my favorite browser), just go to Tools → Options → Web Features and uncheck "Enable JavaScript". Now if you clear the fields and save the form, you should see the following: |
To make sure things are ''really'' working as expected, you can turn off JavaScript and ensure the server-side validation is working. This is easy in [Firefox|http://www.mozilla.org/products/firefox/] (my favorite browser), just go to Tools → Options → Web Features and uncheck "Enable JavaScript". Now if you clear the fields and save the form, you should see the following: |
At line 102 changed 1 line. |
%%(border: 1px solid black; margin: 0 auto; height: 202px; width: 423px) |
%%(border: 1px solid black; margin: 0 auto; height: 215px; width: 521px) |
At line 123 changed 2 lines. |
!!Add testGetPersons methods to DAO and Manager Tests [#3] |
To create a List screen (also called a master screen), we need to create methods that will return all the rows from our ''person'' table. Let's start by adding tests for these methods to our PersonDaoTest and PersonManagerTest classes. I usually name this method ''getEntities'' (i.e. getPersons), but you could also use ''getAll'' or ''search'' - it's really just a matter of personal preference. |
!!Add testGetPeople methods to DAO and Manager Tests [#3] |
To create a List screen (also called a master screen), we need to create methods that will return all the rows from our ''person'' table. Let's start by adding tests for these methods to our PersonDaoTest and PersonManagerTest classes. I usually name this method ''getEntities'' (i.e. getUsers), but you could also use ''getAll'' or ''search'' - it's really just a matter of personal preference. |
At line 126 changed 1 line. |
Open test/dao/**/persistence/PersonDaoTest.java and add a ''testGetPersons'' method: |
Open test/dao/**/dao/PersonDaoTest.java and add a ''testGetPeople'' method: |
At line 130 changed 3 lines. |
public void testGetPersons() throws Exception { |
person = new Person(); |
List results = dao.getPersons(person); |
public void testGetPeople() { |
person = new Person(); |
List results = dao.getPeople(person); |
At line 137 changed 1 line. |
The reason I'm passing in a person object to the ''getPersons'' method is to allow for filtering (based on values in person) in the future. |
The reason I'm passing in a person object to the ''getPeople'' method is to allow for filtering (based on values in person) in the future. Adding this parameter in your getPeople() method signature is optional, but the rest of this tutorial assumes you have done this. |
At line 139 changed 1 line. |
Now open test/service/**/service/PersonManagerTest.java and add a ''testGetPersons'' method: |
Now open test/service/**/service/PersonManagerTest.java and add a ''testGetPeople'' method: |
At line 143 changed 4 lines. |
public void testGetPersons() throws Exception { |
//personForm = new PersonForm(); |
List results = mgr.getPersons(person); //Form); |
assertTrue(results.size() > 0); |
public void testGetPeople() throws Exception { |
List results = new ArrayList(); |
person = new Person(); |
results.add(person); |
|
// set expected behavior on dao |
personDao.expects(once()).method("getPeople") |
.will(returnValue(results)); |
|
List people = personManager.getPeople(null); |
assertTrue(people.size() == 1); |
personDao.verify(); |
At line 150 changed 1 line. |
Neither of these classes will compile at this point since the ''getPersons'' method does not exist on our Interfaces (PersonDao and PersonManager), not on our Implementations (PersonDaoHibernate and PersonManagerImpl). Let's giddyup and add those suckers. |
In order for these tests to compile, you need to add the ''getPeople()'' method to the PersonDao and PersonManager interfaces, and their implementations. |
At line 152 changed 2 lines. |
!!Add getPersons methods to DAO and Manager Interfaces and Implementation classes [#4] |
First, let's modify our interfaces to include a ''getPersons'' method. Open src/dao/**/persistence/PersonDao.java and add the following: |
!!Add getPeople() method to DAO and Manager [#4] |
Open src/dao/**/dao/PersonDao.java and add the getPeople() method signature: |
At line 155 removed 2 lines. |
;:''You will need to import java.util.List in each of these interfaces. In Eclipse, Ctrl+Shift+O is the fastest way.'' |
|
At line 159 changed 1 line. |
public List getPersons(Person person) throws DAOException; |
public List getPeople(Person person); |
At line 162 changed 1 line. |
Then, open src/service/**/service/PersonManager.java and add a similar method: |
Now add the same method signature to src/service/**/service/__PersonManager.java__. Save all your files and adjust the imports in your tests. Next you need to implement the getPeople() method in your implementation classes. Open src/dao/**/dao/hibernate/PersonDaoHibernate.java and add the following method: |
At line 166 changed 1 line. |
public List getPersons(Object person) throws Exception; |
public List getPeople(Person person) { |
return getHibernateTemplate().find("from Person"); |
} |
At line 169 changed 1 line. |
Now we need to add the implementation (a.k.a. "the meat") for these methods into our DAO and Manager implementations. Open src/dao/**/persistence/PersonDaoHibernate.java and add the following method: |
<div class="note" style="margin-left: 30px"> |
You'll notice here that nothing is being done with the ''person'' parameter. This is just a placeholder for now - in the future you may want to filter on it's properties using [Hibernate's Query Language|http://www.hibernate.org/hib_docs/reference/en/html/queryhql.html] (HQL) or using [Criteria Queries|http://www.hibernate.org/hib_docs/reference/en/html/querycriteria.html]. |
At line 171 changed 1 line. |
;:%%(color: blue)''I looked in UserDAOHibernate.java and used the getUsers method as a template for getPersons.'' |
''An example using a Criteria Query:'' |
At line 175 changed 6 lines. |
/** |
* @see org.appfuse.persistence.PersonDao#getPersons(org.appfuse.model.Person) |
*/ |
public List getPersons(Person person) throws DAOException { |
return getHibernateTemplate().find("from Person p order by upper(p.firstName)"); |
} |
// filter on properties set in the person object |
HibernateCallback callback = new HibernateCallback() { |
public Object doInHibernate(Session session) throws HibernateException { |
Example ex = Example.create(person).ignoreCase().ignoreZeroes() |
.enableLike(MatchMode.ANYWHERE); |
return session.createCriteria(Person.class).add(ex).list(); |
} |
}; |
return (List) getHibernateTemplate().execute(callback); |
At line 192 added 1 line. |
</div> |
At line 183 changed 3 lines. |
You'll notice here that we're doing nothing with the ''person'' parameter. This is just a placeholder for now - in the future you may want to filter on it's properties using [Hibernate's Query Language|http://www.hibernate.org/hib_docs/reference/html/query-language.html] (HQL) or using [Criteria Queries|http://www.hibernate.org/hib_docs/reference/html/query-criteria.html]. |
|
Also, add a ''getPersons'' method to src/service/**/service/PersonManagerImpl.java: |
Now implement the ''getPeople()'' method in src/service/**/impl/PersonManagerImpl.java: |
At line 189 changed 5 lines. |
/** |
* @see org.appfuse.webapp.service.PersonManager#getPersons(java.lang.Object) |
*/ |
public List getPersons(Object obj) throws Exception { |
return dao.getPersons((Person) person); |
public List getPeople(Person person) { |
return dao.getPeople(person); |
At line 197 changed 1 line. |
Save all your files and make sure everything compiles with __ant clean package-web__. Now you should be able to run both tests by running the following: |
After saving all your changes, you should be able to run both tests by executing the following: |
At line 202 changed 1 line. |
If everything works - ''nice job!'' Now we need to add this ''retrieve all'' functionality to the web tier. |
If everything works - ''nice job!'' Now you need to add this ''retrieve all'' functionality to the web tier. |
At line 204 changed 1 line. |
!!Add testSearch method to Action Test [#5] |
!!Add testSearch() method to Action Test [#5] |
At line 207 removed 2 lines. |
;:%%(color: blue)''I copied the testSearch method from UserActionTest.java and changed the "User" stuff to "Person".'' |
|
At line 211 changed 1 line. |
public void testSearch() throws Exception { |
public void testSearch() { |
At line 213 changed 1 line. |
addRequestParameter("action", "Search"); |
addRequestParameter("method", "Search"); |
At line 218 changed 2 lines. |
|
assertTrue(getRequest().getAttribute(Constants.PERSON_LIST) != null); |
assertNotNull(getRequest().getAttribute(Constants.PERSON_LIST)); |
At line 236 changed 1 line. |
Now save all your changes. You won't be able to run __ant test-cactus -Dtestcase=PersonAction__ yet since ''PersonAction.search()'' does not exist (yet). Let's add it. |
Now save all your changes. You won't be able to run __ant test-web -Dtestcase=PersonAction__ yet since ''PersonAction.search()'' does not exist (yet). |
At line 243 changed 1 line. |
* @struts.action-forward name="list" path=".personList" |
* @struts.action-forward name="list" path="/WEB-INF/pages/personList.jsp" |
At line 255 changed 1 line. |
throws Exception { |
throws Exception { |
At line 260 removed 3 lines. |
PersonForm personForm = (PersonForm) form; |
|
// Exceptions are caught by ActionExceptionHandler |
At line 264 changed 2 lines. |
List persons = mgr.getPersons(convert(personForm)); |
request.setAttribute(Constants.PERSON_LIST, persons); |
List people = mgr.getPeople(null); |
request.setAttribute(Constants.PERSON_LIST, people); |
At line 272 changed 1 line. |
Now if you run __ant test-cactus -Dtestcase=PersonAction__, you will get an error that the .personList definition does not exist. Or, at least that is what the following error is trying to say: |
Run __ant test-web -Dtestcase=PersonAction__. |
At line 274 changed 4 lines. |
{{{ |
Testcase: testSearch(org.appfuse.webapp.action.PersonActionTest): FAILED |
was expecting '/appfuse/.personList' but received '/appfuse.personList' |
}}} |
__Nice!__ |
%%(color:green)BUILD SUCCESSFUL\\ |
Total time: 1 minute 26 seconds%% |
At line 280 changed 1 line. |
Let's create the JSP to hold our list and a Tile's definition for it. The easiest way to do this is to use the JSPGen Tool to generate a basic list screen for us. To do this from the command-line, navigate to extras/jspgen and run __ant -Dform.name=PersonForm__. This will generate a PersonFormList.jsp in extras/jspgen/build. |
Open the personList.jsp file in ''web/pages''. You'll probably want to change the code to show the plural form of the items you're listing. The generated name in this example is "persons" and it should probably be people. At or near line 31, you should have the following line: |
At line 282 removed 40 lines. |
Copy PersonFormList.jsp to web/pages/personList.jsp and open it for editing. |
|
At the top of the file is a <bean:struts> tags that exposes the edit screen's forward as a page-scoped variable. This should already have a value of "editPerson". |
|
[{Java2HtmlPlugin |
|
<%-- For linking to edit screen --%> |
<bean:struts id="editURL" forward="editPerson"/> |
}] |
|
Now we need to add this to the metadata/web/global-forwards.xml, as well as one for viewing the list. This way, they will get included in our struts-config.xml file. |
|
[{Java2HtmlPlugin |
|
<forward name="editPerson" path="/editPerson.do"/> |
<forward name="viewPeople" path="/editPerson.do?action=Search"/> |
}] |
|
;:''You'll notice that I'm hardcoding the "action=''$button.name''" into the forwards. This kindof defeats the purpose of i18n and will cause other languages to fail when trying to use these forwards. A possible solution would be to use the Actions and the keys from ApplicationResources.properties to compose the URLs. Since I haven't used AppFuse to develop an application that required any languages other than English, I haven't been inclined to fix this. Solutions to this problem are encouraged!'' |
|
In the first <button> you find in personList.jsp, we need to populate another forward - this time to the Add screen. Make sure your button's onclick event matches the following: |
|
[{Java2HtmlPlugin |
|
onclick="location.href='<html:rewrite forward="editPerson"/>'"> |
}] |
|
The template we used to create this JSP has the column for the id property hard-coded, so XDoclet adds it twice. We need to remove this from personList.jsp - so delete the following from this file: |
|
[{Java2HtmlPlugin |
|
<display:column property="id" sort="true" |
headerClass="sortable" |
titleKey="personForm.id"/> |
}] |
|
;:''If anyone knows of a way to modify the extras/jspgen/src/DisplayTagList_jsp.xdt to not include this column tag, please let me know.'' |
|
Another thing you'll probably want to change is the plural form of the items you're listing. The generated name in this example is "persons" and it should probably be people. At or near line 31, you should have the following line: |
|
At line 328 changed 1 line. |
Now we need to add a new definition to tiles-config.xml for this list screen. Open web/WEB-INF/tiles-config.xml and add the following XML: |
Finally, add the title and heading keys (personList.title and personList.heading) to web/WEB-INF/classes/ApplicationResources.properties. Open this file and add the following: |
At line 330 removed 14 lines. |
;:%%(color: blue)''I usually copy an existing list definition - i.e. .userList.'' |
|
[{Java2HtmlPlugin |
|
<!-- Person List definition --> |
<definition name=".personList" extends=".mainMenu"> |
<put name="titleKey" value="personList.title" /> |
<put name="headingKey" value="personList.heading" /> |
<put name="content" value="/WEB-INF/pages/personList.jsp"/> |
</definition> |
}] |
|
Finally, we need to add these keys (personList.title and personList.heading) to web/WEB-INF/classes/ApplicationResources.properties. Open this file and add the following: |
|
At line 347 changed 1 line. |
personList.heading=All Persons |
personList.heading=All People |
At line 351 changed 1 line. |
{{personList.heading}} will be put into an <h1> tag before any page content. At this point, your PersonActionTest should succeed. Save all your changes, navigate to the basedir of your project and try it by executing __ant test-cactus -Dtestcase=PersonAction__. |
{{personList.heading}} will be put into an <h1> tag before any page content. |
At line 353 changed 5 lines. |
%%(color:green) |
''__Sweet!__''\\ |
BUILD SUCCESSFUL\\ |
Total time: 1 minute 0 seconds |
%% |
At this point, you should be able to run __ant clean deploy__, start Tomcat and view this page in your browser at [http://localhost:8080/appfuse/editPerson.html?method=Search]. |
At line 359 changed 1 line. |
At this point, you should be able to view this page in your browser at [http://localhost:8080/appfuse/editPerson.do?action=Search]. |
Now that we have a List Screen, let's change the pages that are displayed after adding and deleting a new Person. In src/web/**/action/PersonAction.java, change the ''mapping.findForward("mainMenu")'' in the ''save'', ''delete'' and ''cancel'' methods to be: |
At line 361 removed 2 lines. |
Now that we have a List Screen, let's change the pages that are displayed after adding and deleting a new Person. In src/web/**/PersonAction.java, change the ''mapping.findForward("mainMenu")'' in the ''save'', ''delete'' and ''cancel'' methods to be: |
|
At line 368 changed 1 line. |
You will also need to change ''verifyForward("list")'' to be ''verifyForward("viewPeople")'' in the testRemove method of test/web/**/PersonActionTest.java. Lastly, the Canoo tests "AddPerson" and "DeletePerson" need to be updated. Open test/web/web-tests.xml and change the following line in the "AddPerson" target: |
You will also need to change ''verifyForward("mainMenu")'' to be ''verifyForward("viewPeople")'' in the testRemove method of test/web/**/action/PersonActionTest.java. Lastly, the Canoo tests "AddPerson" and "DeletePerson" need to be updated. Open test/web/web-tests.xml and change the following line in the "AddPerson" target: |
At line 381 changed 1 line. |
text=".*$(mainMenu.title}.*" regex="true"/> |
text=".*$(mainMenu.title}.*" regex="true"/> |
At line 386 changed 2 lines. |
{{{<verifytitle description="display Person List" |
text=".*$(personList.title}.*" regex="true"/>}}} |
{{{<verifytitle description="display Person List" text=".*${personList.title}.*" regex="true"/>}}} |
At line 389 changed 1 line. |
We use "viewPeople" instead of "list" so that the search method will be executed, rather than simply forwarding to the .personList definition (which the "list" forward points to). |
Finally, declare the viewPeople forward in metadata/web/global-forwards.xml after viewUsers as below: |
At line 391 changed 1 line. |
To test that displaying this page works, we can create a new JSP test in test/web/web-tests.xml: |
{{{<forward name="viewPeople" path="/editPerson.html?method=Search"/>}}} |
At line 331 added 4 lines. |
The name "viewPeople" is used instead of "list" so that the search method will be executed, rather than simply forwarding to the personForm.jsp (which the "list" forward points to). |
|
To test that displaying this page works, create a new JSP test in test/web/web-tests.xml: |
|
At line 402 changed 2 lines. |
<invoke description="click View People link" |
url="/editPerson.do?action=Search"/> |
|
<invoke description="click View People link" url="/editPerson.html?method=Search"/> |
At line 371 added 4 lines. |
|
%%note __NOTE:__ The other links in mainMenu.jsp don't use <html:link> so this JSP can be shared among the various web framework implementations in AppFuse (i.e. Spring MVC and WebWork).%% |
|
|
At line 450 changed 1 line. |
Make sure the above XML is inside the <Menus> tag, but not within another <Menu>. Then add this new menu to web/common/menu.jsp - which should now looks as follows: |
Make sure the above XML is inside the <Menus> tag, but not within another <Menu>. Then add this new menu to web/common/menu.jsp - which should now look as follows: |
At line 457 changed 2 lines. |
<menu:useMenuDisplayer name="ListMenu" |
permissions="rolesAdapter"> |
<menu:useMenuDisplayer name="ListMenu" permissions="rolesAdapter"> |
At line 469 changed 1 line. |
Now if you run __ant clean deploy__ and start Tomcat, you should see something like the screenshot below. |
Now if you run __ant clean deploy__ start Tomcat and go to [http://localhost:8080/appfuse/mainMenu.html], you should see something like the screenshot below. |
At line 478 changed 1 line. |
You've completed the full lifecycle of developing a set of master-detail pages with AppFuse - __congratulations__. Now the real test is if you can run all the tests in your app without failure. To test, stop tomcat and run __ant clean test-all__. This will run all the unit tests within your project. As a reminder, it should be easy to setup and test AppFuse from scratch using __ant setup-db setup-tomcat test-all__. Also, if you're looking for more robust examples - checkout [StrutsResume]. |
You've completed the full lifecycle of developing a set of master-detail pages with AppFuse and Struts - __Congratulations__! Now the real test is if you can run all the tests in your app without failure. To test, stop tomcat and run __ant clean test-all__. This will run all the unit tests within your project. As a reminder, it should be easy to setup and test AppFuse from scratch using __ant setup-db setup-tomcat test-all__. Also, if you're looking for more robust examples - checkout [Struts Resume|StrutsResume]. |