-
Topics
Application Functionality
Dependency Injection
Coursework - Testing
- Links: Test functionality as black box testing
- List URLs (including pages, but also buttons and links)
- Follow the links
- State the outcome
- Take a small(!!!) screen shot as evidence
- Test working methods using unit tests
- Are the correct values returned?
Web Applications
The World Wide Web (WWW, or simply Web) is an information space in which the items of interest, referred to as resources, are identified by global identifiers called Uniform Resource Identifiers (URI)."[1]
Interaction occurs through the exchange of messages over the Hyper Text Transfer Protocol (http, https)
- Examples of resources are:
- Static web pages
- Dynamic Web Applications
Java provides support for web application through Servlets and Java Server Pages (JSP).
http Communication Pattern
Important Parts of http Requests
- HTTP Method
- The action to be performed, e.g GET, POST, PUT
- URL
- The address of the resource to access
- Form Parameters
- The data to be submitted with a request for further use by the application, e.g. user and password details from login page.
Important Parts of http Responses
- Status Code
- An integer to indicate whether the request was success or not, e.g. 200 - OK for success,
- 404 for Not Found
- 403 for Access Forbidden.
- Content Type
- text, html, image, pdf etc. Also known as MIME type
- Content
- The actual data that is rendered by client and shown to user.
Observe the http Communication
-
Web Applications (2)
- Web Server is a software that can process the client request and send the response back to the client.
- Static pages
- Dynamic web content, compiled by the web application (server) in order to provide a response to an http request
- Some of the data may come from a persistent data source, such as a database
- After the response has been put together it is passed to the server to be sent to the client, which sent the request
Typical programming languages for dynamic content generation: PHP, Python, Ruby on Rails, Java Servlets and JSPs.
Java Servlet and JSPs are Java-based server side technologies to extend the capability of web servers by providing support for dynamic response and data persistence.
Important Classes and Interfaces of the javax.servlet API
The Problem
- Application functionality has been developed as individual (http) servlets, e.g.
- ShopServlet
- ProductServlet
- CartServlet
A servlet provides a response to a request, often in the doGet(...), doPost(...) etc. methods
It might create other objects and run methods on these in the process.
Redirecting – Generating a Request from the Server
1. http request http://localhost:9000/login
2. Runner
3. LoginServlet.javaprivate static final String LOGIN_TEMPLATE = "login.mustache"; doPost() { ... } else if ("login".equals(action)) { if (!doLogin(request, userName, password)) { response.sendRedirect(response.encodeRedirectURL("/login?rejected=true")); return; } ....}
Producing the Response - login.mustache
<form method="POST" action="/login"> <div class="mdl-textfield mdl-js-textfield"> <input class="mdl-textfield__input" type="text" name="userName" id="userName"/> <label class="mdl-textfield__label" for="userName">Username</label> </div> <div class="mdl-textfield mdl-js-textfield"> <input class="mdl-textfield__input" type="password" name="password" id="password"/> <label class="mdl-textfield__label" for="password">Password</label> </div> <div> <button class="mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect" type="submit" name="act" value="login">Log in</button> <button class="mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect" type="submit" name="act" value="register">Register</button> </div> {#rejected}} <p>Try Again</p> {{/rejected}} </form>...
Re-directing on the Server
in response method doX(...){...}, i.e. doGet(){...}
send response.sendRedirect(response.encodeRedirectURL("/logout"));
This creates another request for URL:
localhost:9000/logout
Which will then be passed to Runner
Depending on which Servlet has been set up for the request for "/logout" using ContextHandler, that servlet will provide the response.
📷 Redirecting on the Client - Example
XX- doPost():
- doLogin():
- doRegister():
XXX -
Example - Generating the new Request
The "botton" is a link <a>
That is styled as a button using CSS
Its href attribute is the address of the next page
Example:
<a class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect store-cart__button" data-role="button" href="/shop" data-upgraded=",MaterialButton,MaterialRipple">Back to Store<a>
Defining which Servlet handles the Response
In class Runner.java set up a Context Handler
Example:
ServletContextHandler handler = new ServletContextHandler(server, "/", ServletContextHandler.SESSIONS); handler.setInitParameter("org.eclipse.jetty.servlet.Default." + "resourceBase", "src/main/resources/webapp"); SkuServlet skuServlet = new SkuServlet(); handler.addServlet(new ServletHolder(skuServlet), "/sku.mustache");
If in Doubt, look up the API
import 🔗 org.eclipse.jetty.servlet.ServletContextHandler;
Allows for simple construction of a context and optionally session and security handlers.
The Jetty Web Server
The Jetty Web Server provides an HTTP server and Servlet container
Is capable of serving static and dynamic content
either from a standalone or embedded instantiations.
Benefit: easy to configure
Alternative: configuration file web.xml
Using Jetty - Runner.java
//import the libraries - pom.xml //the Jetty HTTP Servlet Server import org.eclipse.jetty.server.Server; ... //define the port to listen to private static final int PORT = 9000; ... private void start() throws Exception { //instantiate the server Server server = new Server(PORT); ... }
Dependency Injection
By using the Java Dependency Injection design pattern we can remove the hard-coded dependencies and make our application loosely coupled, extendable and maintainable.
We move the dependency resolution from compile-time to runtime.
DI Example (1)
package com.journaldev.java.legacy; public class EmailService { public void sendEmail(String message, String receiver){ //logic to send email System.out.println("Email sent to " +receiver+ " with Message="+message); } }
DI Example (2)
package com.journaldev.java.legacy; public class MyApplication { private EmailService email = new EmailService(); public void processMessages(String msg, String rec){ //do some msg validation, manipulation logic etc this.email.sendEmail(msg, rec); } }
DI Example (3) Alternative
package com.journaldev.java.legacy; public class MyApplication { private EmailService email = null; public MyApplication(EmailService svc){ this.email=svc; } public void processMessages(String msg, String rec){ //do some msg validation, manipulation logic etc this.email.sendEmail(msg, rec); } }
-
Limitations
MyApplication class is responsible to initialize the email service and then use it. This leads to hard-coded dependency. If we want to switch to some other advanced email service in future, it will require code changes in MyApplication class.
If we want to extend our application to provide additional messaging feature, such as SMS or Facebook message then we would need to write another application for that. This will involve code changes in application classes and in client classes too.
our application is directly creating the email service instance. Testing is difficult. There is no way we can mock these objects in our test classes.
DI Requirements
Service components should be designed with base class or interface. It’s better to prefer interfaces or abstract classes that would define contract for the services.
Consumer classes should be written in terms of service interface.
Injector classes that will initialize the services and then the consumer classes.
DI - Service Components as interface
package com.journaldev.java.dependencyinjection.service; public interface MessageService { void sendMessage(String msg, String rec); }
Implementation - Guice
- Google Guice is the framework to automate the dependency injection in applications.
- Guice sets up one filter,
- in Runner:
- Guice.createInjector(new BindingModule(), new RouteModule());
Constructor Injection
- Constructor injection combines instantiation with injection.
- Annotate the constructor with the @Inject annotation.
- This constructor should accept class dependencies as parameters.
- Most constructors will then assign the parameters to final fields.
- Explicit annotation is preferable because it documents that the type participates in dependency injection.
- If there is no @Inject annotation on constructor, Guice will use a public, no-arguments constructor if it exists.
Constructor Injection Example
public class RealBillingService implements BillingService { private final CreditCardProcesor processorProvider; private final TransactionLog transactionLogProvider; @Inject public RealBillingService(CreditCardProcessor processorProvider, TransactionLog transactionLogProvider){ this.processorProvider = processorProvider; this.transactionLogProvider = transactionLogProvider; } }
Method Injection
Guice can inject methods that have the @Inject annotation.
Dependencies take the form of parameters,
which the injector resolves before invoking the method.
Injected methods may have any number of parameters, and the method name does not impact injection.
Method Injection Example
public class PayPalCreditCardProcessor implements CredtCardProcessor { private static final String DEFAULT_API_KEY = "development-use-only"; private String apiKey = DEFAULT_API_KEY; @Inject public void setApiKey(@Named9"PayPal API key") String apiKey){ this.apiKey = apiKey; } }
Field Injection Example
Annotate fields with the @Inject annotation.
This is the most concise injection,
but the least testable.
Field Injection Example
public class DatabaseTransactionLogProvider implements Provider<TransactionLog>{ @Inject Connection connection; public TransactionLog get() { return new DatabaseTransactionLog(connection); } }
-
Google Guice
Google Guice is one of the leading frameworks whose main work is to provide automatic implementation of dependency injection.
Google Guice support both setter-based and constructor-based dependency injection. Our application class that consumes the service looks like below.
Guice in Context
sub package guice, e.g.
package uk.ac.wpd2.group2.shop.guice;
sub sub package annotations
package uk.ac.wpd2.group2.shop.guice.annotations;
@BindingAnnotation @Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) public @interface DefaultShop { } public class BindingModule extends AbstractModule { @SuppressWarnings({"unused"}) static final Logger LOG = LoggerFactory.getLogger(BindingModule.class); public BindingModule() {} @Override protected void configure() { bind(Shop.class).annotatedWith(DefaultShop.class).toInstance(new ShopLoader().load()); bind(String.class).annotatedWith(CsrfParamName.class).toInstance("csrf"); bind(IUserLogin.class).toInstance(new H2User(H2User.DB.MEMORY)); } } public class RouteModule extends ServletModule { @SuppressWarnings({"unused"}) static final Logger LOG = LoggerFactory.getLogger(RouteModule.class.getName()); public RouteModule() { } @Override protected void configureServlets() { filter("/*").through(CsrfFilter.class, ImmutableMap.of("guardedPaths", "/cart.+")); serve("/login").with(LoginServlet.class); serve("/logout").with(LogoutServlet.class); serve("/shop").with(ShopServlet.class); serve("/shop/sku/*").with(SkuServlet.class); serve("/shop/cart").with(CartServlet.class); serve("/shop/cart/pay").with(CartServlet.class); serve("/shop/cart/deleteSku").with(DeleteSkuServlet.class); serve("*.html").with(MustacheServlet.class); // Needed for Jetty, and serves static content bind(DefaultServlet.class).in(Scopes.SINGLETON); serve("/*").with(DefaultServlet.class, configuredDefault()); }
RouteModule (2)
private Map<String,String> configuredDefault() { Map<String, String> params = new HashMap<>(); params.put("resourceBase", "src/main/resources/webapp"); params.put("dirAllowed", "true"); return params; }
-
In Runner - Guice Filter
import com.google.inject.Guice; import com.google.inject.servlet.GuiceFilter;
In Runner.java
private void start() throws Exception { Guice.createInjector(new BindingModule(), new RouteModule()); Server server = new Server(PORT); ... }
Adding a Guice Filter
handler.addFilter(GuiceFilter.class, "/*", EnumSet.allOf(DispatcherType.class));
Guice API (1)
The entry point to the Guice framework. Creates Injectors from Modules.
Guice supports a model of development that draws clear boundaries between APIs, Implementations of these APIs, Modules which configure these implementations, and finally Applications which consist of a collection of Modules. It is the Application, which typically defines your main() method, that bootstraps the Guice Injector using the Guice class, as in this example:
Guice Annotations
From com.google.inject or javax.inject packages
Construct an instance of ... servlet
- @Inject
- Use this constructor when you use the injector to create an instance of a class
- Either default or @Inject one (@Inject is preference if there is default constructor)
@Annotation
@BindingAnnotation - Guice
@Target({FIELD, PARAMETER, METHOD}) - java.lang
where can it be put
Parameter used now, parameter in servlet constructor
@Retention(RUNTIME) - java.lang
when do you use it, here at runtime
Guice Maven Dependency
Include in the pom file <dependency> <groupId>com.google.inject</groupId> <artifactId>guice</artifactId> <version>3.0</version> </dependency>
-
Further Reading
https://www.w3.org/TR/webarch/
http://www.journaldev.com/1854/java-web-application-tutorial-for-beginners
http://www.journaldev.com/1877/servlet-tutorial-java#servlet-api-hierarchy
http://www.eclipse.org/jetty/documentation/
https://github.com/google/guice/wiki/GettingStarted
http://www.journaldev.com/2394/java-dependency-injection-design-pattern-example-tutorial
http://www.journaldev.com/2403/google-guice-dependency-injection-example-tutorial