Chapter 5 Tutorials
Eliminating Hidden Fields
- Declare a bean with session scope in the main configuration file,
SimpleBean.java. The bean needs a new qualifier, uses session scope
and needs a new method to return an instance of the data bean.
@Bean("sessionDefaultBean")
@SessionScope
RequestDataDefault getSessionDefaultBean() {
return new RequestDataDefault();
}
- Copy the controller to a new class named
ControllerHelperP1AddModel
in the controller/ch3/restructured/p1_addModel folder.
- Modify the request mapping annotation to include the URL pattern
/ch3/restructured/p1_addModel/Controller .
- Change the qualifier for the bean to
sessionDefaultBean .
- Annotate the
getData method with ModelAttribute and the name data
to allow access to the bean.
- Copy the JSPs to a new folder named
ch3/restructured/twoconfirms .
- Update the return value from the
viewLocation method to return
the new location of the JSPs.
- Remove the hidden fields from each JSP.
- Modify the process view so that the name for the confirm button is
confirmNoDataButton .
- Modify the
doGet method.
- Remove the statement that calls
setAttribute to add the bean to the session.
This code is no longer needed, since Spring controls what is added to the session with the
ModelAttribute annotation on the getData method.
- Move the statements that copy data into the bean to the code block for the confirm button.
- Add a test for when
confirmNoDataButton is in the query string. Return the
address for the confirm view.
- Enter the URL for the controller. The application has the same functionality as before but uses the
session to store the request data.
Controller Logic
- Copy the controller from the last example to a new class named
ControllerHelperP2Mappings
in the controller/ch3/restructured/p2_mappings folder.
- Modify the request mapping annotation for the URL pattern
/ch3/restructured/p2_mappings/Controller .
-
Add a method for each button.
- Cut and paste the code from the if-clause for
a button into the new method for that button. Instead of setting
the value of the address variable, return that value from the method.
- Annotate each method with a
GetMapping
annotation. Set the params attribute with the name
of the button that is associated with the method.
- Add a parameter of type
HttpServletRequest to the method for the confirm button.
- Remove the
HttpServletRequest parameter from the doGet method.
- Replace the body of the
doGet method with return editMethod();
- The application has the same functionality as before but is better organized and is simpler.
Post Requests
- Copy the JSPs to a new location named
ch3/restructured/post .
- Modify the edit view so the form uses the POST method.
- Modify the process view so the name of the button to return to the confirm page is
confirmButton .
- Copy the controller from the last example to a new class named
ControllerHelperP3Post
in the controller/ch3/restructured/p3_post folder.
- Modify the request mapping annotation for the URL pattern
/ch3/restructured/p3_post/Controller .
- Modify the
viewLocation method for the new location of the JSPs.
- Change the annotation on the confirm method to
PostMapping .
Change the return value from the method to the string redirect:Controller?confirmButton=confirm .
- Change the button associated with the
confirmNoData method to confirmButton .
- The application has improved functionality with minimal changes to the code. The
data can only be posted once to the application.
Replacing the Request
- Copy the JSPs to a new location named
ch3/restructured/model .
- Modify the edit view so it uses the Spring tags for the form and input tags.
Do not use the input tag for the submit button.
- Add a new page for expired data. Include a link to the controller to start over.
- Copy the controller from the last example to a new class named
ControllerHelperP4Model
in the controller/ch3/restructured/p4_model folder.
- Modify the request mapping annotation for the URL pattern
/ch3/restructured/p4_model/Controller .
- Modify the
viewLocation method for the new location of the JSPs.
- Replace the current confirm method that handles post requests with
@PostMapping(params="confirmButton")
public String confirmMethod(
@ModelAttribute("data") Optional dataForm) {
if (! dataForm.isPresent()) return "redirect:expired";
return "redirect:Controller?confirmButton=Confirm";
}
- The functionality is the same as the last controller, but the code does not access
the HTTP request or session directly. The data is automatically copied from the
bean to the form data and from the query string back to the bean.
Navigation Without the Query String
- Copy the JSPs to a new location named
ch3/restructured/path .
- Remove the forms from the confirm and process views. Add links that use path information
to specify the next view to access.
- Modify the edit view so the data is sent to the confirm view.
- Copy the controller from the last example to a new class named
ControllerHelperP5Path
in the controller/ch3/restructured/p5_path folder.
- Modify the request mapping annotation for the URL pattern
/ch3/restructured/p5_path/collect/ .
- Modify the
viewLocation method for the new location of the JSPs.
- Replace the
params attribute for each mapping annotation with the
path attribute for the button. Since the path
attribute is the default attribute, the name of the attribute may be
omitted.
- Modify the confirm method that handles post requests so it returns the
string
redirect:confirm .
- The application no longer uses the query string to navigate from one page to the
next. Instead, it uses the path information. Path information is easier to read
and to write.
Session Attributes
- Add a declaration for a prototype bean to the configuration file, SimpleBean.java
@Bean("protoDefaultBean")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
RequestDataDefault getProtoRequiredBean() {
return new RequestDataDefault();
}
- Copy the JSPs to a new location named
ch3/restructured/sessattr .
- Add a restart link to the process view.
- Copy the controller from the last example to a new class named
ControllerHelperP6SesAttr
in the controller/ch3/restructured/p6_sessattr folder.
- Modify the request mapping annotation for the URL pattern
/ch3/restructured/p6_sessattr/collect/ .
- Annotate the class with
SessionAttributes and include the name data ,
which is the same name in the ModelAttribute annotation in the getData method.
- Modify the
viewLocation method for the new location of the JSPs.
- Remove the instance variable for the bean.
- Add an instance variable of type
ObjectFactory which wraps the RequestData interface.
Annotate it with a qualifier for the prototype bean that was added to the configuration file.
- Modify the
getData method so it calls getObject on the ObjectFactory
variable.
- The confirm method does not have to be modified. It does not required a parameter
for the session attribute. The model attribute will parameter will access the session
variable.
Adding a Logger
- Add the dependency to the groovy classes in the pom file. Do not include the
groovy-all dependency, as that will prevent the JUnit tests from
running.
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
<version>2.5.11</version>
</dependency>
- Add a file named
logback.groovy to the src/main/resources folder.
- Start the file with the scan time and the location of the log files.
scan("1 minute")
String LOG_PATH = System.getenv("logpath") ?: "logs"
- Add the code for the console appender.
appender ("Console-Appender", ConsoleAppender) {
encoder(PatternLayoutEncoder) {
pattern=String.format("%s%n%s%n",
"highlight(%-5level\t%msg)",
"\t%gray(%date(ISO8601) - %logger{65})")
}
}
- Add the code for the file appender.
appender ("RollingFile-Appender", RollingFileAppender) {
file = "${LOG_PATH}/current.log"
rollingPolicy(TimeBasedRollingPolicy) {
fileNamePattern = "${LOG_PATH}/backup/log%d{yyyy-MM-dd}.zip"
maxHistory = 10
totalSizeCap = "50MB"
}
encoder(PatternLayoutEncoder) {
pattern = "%-5level\t%msg%n\t%date{ISO8601} - %logger%n"
}
}
- Define the root logger and file logger. The first parameter to the file logger is a string
that should match the beginning of the package for classes that can send
messages to the log file. In these examples, all the classes descend from
the
com.bytesizebook package. Set each to a different level. General messages
from Spring and Tomcat will not include DEBUG messages. Messages from the controller will
include DEBUG messages.
logger ("com.bytesizebook", DEBUG, ["Console-Appender", "RollingFile-Appender"], false)
root(INFO, ["Console-Appender"])
- Copy the controller from the last example to a new class named
ControllerHelperP7Logger
in the controller/ch3/restructured/p7_logger folder.
- Modify the request mapping annotation for the URL pattern
/ch3/restructured/p7_logger/collect/ .
- Add each of the five error message types to different methods in the controller.
- Run the application and open the log file to see the results.
- Modify the logger configuration file and access the application again.
- View the log file to see that different level messages are written to the log file. EOF
Enhanced Controller
- Copy the interface and bean from the last example to a new folder
src/main/java/data/ch5/enhanced
- Add a declaration for a prototype bean to the configuration file, SimpleBean.java
@Bean("protoEnhancedBean")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
com.bytesizebook.data.ch5.enhanced.RequestDataDefault getProtoEnhancedBean() {
return new com.bytesizebook.data.ch5.enhanced.RequestDataDefault();
}
- Copy the JSPs to a new location named
ch5/enhanced .
- Copy the controller from the last example to a new class named
ControllerEnhanced
in the controller/ch5/enhanced folder.
- Change the import for the bean to
com.bytesizebook.data.ch5.enhanced .
- Modify the request mapping annotation for the URL pattern
/ch5/enhanced/collect/ .
- Modify the instance variable of type
ObjectFactory which wraps the RequestData interface.
Annotate it with a qualifier for the prototype bean that was added to the configuration file for this example.
- Modify the
viewLocation method for the new location of the JSPs.
- This example is referenced in future chapters
Testing Enhanced Controller
- Copy the test class from the last chapter to the
src/test/java/com/bytesizebook/ch5/enhanced folder.
- Rename the class.
- Create constants for the controller mapping, view location, and session bean name.
- Remove the autowired bean instance.
- Autowire an instance of
WebApplicationContext .
- Create an additional method that will be run before each test. Annotate it with
BeforeEach .
- Initialize an instance of the Mock Mvc using
MockMvcBuilders .
- Call the
webAppContextSetup method for the instance with the web application context.
- Call
apply with the sharedHttpSession method.
- Build the instance.
- Add an additional
MultiValueMap that contains invalid parameters.
- Create additional instance variables for default values and invalid values.
- Remove references to adding buttons as parameters to query string.
- Modify the
SetUpAll method to initialize the new instance variables.
- Modify the
makeRequestTestContent method so it returns the results from the mock Mvc.
- Create a method that returns the
MvcResult for when the application is called for the first time, without a button click.
- Call the method when testing for the first call to the application.
- Call the method when testing the confirm button, then make the call for the confirm button with the appropriate query string.
The test does not have direct access to the session, but can only retrieve it after the fact.
- Create a helper method that retrieves the session from the
MvcResult and tests that it
has the correct values.
- Make similar tests for other transitions.
|