XPages to Java EE, Part 5: Web Pages
Thu Jan 24 12:19:20 EST 2019
- XPages to Java EE, Part 1: Overview
- XPages to Java EE, Part 2: Terminology
- XPages to Java EE, Part 3: Hello, World
- XPages to Java EE, Part 4: Application Servers
- XPages to Java EE, Part 5: Web Pages
- XPages to Java EE, Part 6: Dependencies
- XPages to Java EE, Part 7: MVC
- XPages to Java EE, Part 8: IDE Server Integration
- XPages to Java EE, Part 9: IDE Features Grab Bag
- XPages to Java EE, Part 10: Data Storage
- XPages to Java EE, Part 11: Mixing MVC and an API
- XPages to Java EE, Part 12: Container Authentication
- XPages to Java EE, Part 13: Why Do This, Anyway?
Once upon a time, web pages were easy: you'd write some HTML directly or, if you're fancy, use some Server Side Includes or PHP. Now, though, it's a rat's nest of decisions and stacks - fortunately for me, going into the pros and cons of each approach is beyond the scope of this series. Suffice it to say that Java EE can cover your needs whatever approach you take: it can do basic dynamic HTML generation, run server-persisted frameworks like JSF, and work splendidly as a backend for a client JS app thanks to JAX-RS.
For this post, I'm going to stick to some basics: a JSP page pulling in values from a translation resource bundle. This should provide a nice introduction to a couple more core technologies that will come in handy in app development, in particular CDI.
Translation File
Localization and internationalization are huge topics and there are many approaches to take, both in technology and in style within individual technologies. I won't weigh in too much on that in general, but I'll keep it simple here. Using the Java ResourceBundle
class, you can use a set of .properties
files in your project to provide translations for different locales, as well as a default. Even though almost everything I write ends up US-English-only anyway, I like to have something like this for good practice.
Open the Java Resources
node of the tutorial project, right-click on src/main/resources
, and choose New
-> Other
:
For the type, choose "File" under the "General" category:
Set the name of the file to "translation.properties":
In the new file, set the contents to this (or feel free to customize at will):
_lang=en
appTitle=Java EE Tutorial
welcome=Hello, {0}
Index Page
Now, right-click on the project root and choose New
-> JSP File
:
If you don't see that entry in the New menu, pick Other...
and do a search in that dialog for "JSP".
Name this file "index.jsp" and hit Finish
:
If you hit Next >
, you'll have the option to pick from some pre-made templates, which is good to know about, but we don't need it now.
Replace the new file's contents with this:
<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true"%>
<!DOCTYPE html>
<html lang="${translation._lang}">
<head>
<title>${translation.appTitle}</title>
</head>
<body>
${messages.format("welcome", param.name)}
</body>
</html>
Unlike with XPages, we're starting with bare bones here: no HTML will show up on the page unless you explicitly put it in here, either via HTML you write or via programmatic tags you bring in. The binding language, though, should be familiar: that's EL 3, which is (among other things) the current form of the expression language found in XPages. Since we're working with a JSP page, there's no such thing as a "run-time binding", so everything is the ${}
-style "on page load" format. The language is also fancier nowadays, and you can see a method expression at work.
Translation Bean
So now we have the backing translation file and the front-end page using it, but we're missing the glue that connects the two. For that, we'll create some CDI managed beans.
Create a new Java class in the bean
package named TranslationBean
and set its contents to this:
package bean;
import java.text.MessageFormat;
import java.util.ResourceBundle;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.http.HttpServletRequest;
@RequestScoped
public class TranslationBean {
@Inject
HttpServletRequest request;
@Produces @Named("translation")
public ResourceBundle getTranslation() {
return ResourceBundle.getBundle("translation", request.getLocale()); //$NON-NLS-1$
}
@RequestScoped
@Named("messages")
public static class Messages {
@Inject
HttpServletRequest request;
@Inject @Named("translation")
ResourceBundle translation;
public String format(String key, Object... params) {
String message = translation.getString(key);
return MessageFormat.format(message, params);
}
}
}
There are a few weird bits going on here, but some of them are direct analogues to what we do in XPages.
For starters, the @RequestScoped
annotation is exactly what you might think: it marks the class as being a request-scoped managed bean, and the semantics of this are the same as in XPages, just without having to add an explicit definition like in faces-config.xml
. This is paired with @Named("messages")
, which defines the name of the bean as used by the JSP page.
Next up is the @Inject
annotation, which really dips into the magic going behind the scenes. In a Java EE app, CDI acts as a sort of general object-management layer, and it picks up on annotations like these and sets the values of object properties based on providers. In the case of HttpServletRequest
, the app has implicit knowledge of the current request, and so can inject it there - this is kind of similar to how an XPages app always has a session
object implicitly available. This is also similar to "managed properties" in JSF/XPages, though it's a rare XPages app that uses those.
The part that's entirely new is the @Produces
annotation. This tells CDI that a given method can churn out an object matching a specific characteristic, and we use that here for getTranslation()
. Because ResourceBundle
on its own doesn't have an implicit instance like HttpServletRequest
does, we declare the method as producing a ResourceBundle
specifically with the name "translation". This name is used both on the JSP page as a normal managed-bean name and also within the inner Messages
class, which asks for the named ResourceBundle
as its own property. There's actually a whole world of different qualifiers and matching patterns that can apply here, and you can also have implementation classes that are actually proxy classes created on the fly. We may deal with those eventually, but not here.
Loading It Up
As before, run the application by executing a Maven build with the goals "install tomee:run". Once it's done, you should be able to go to http://localhost:9091/javaeetutorial/?name=World and be greeted with this visual splendor:
The HTML will be pleasantly sparse:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Java EE Tutorial</title>
</head>
<body>
Hello, World</body>
</html>
An actual application page would naturally get much more complex (for one, it shouldn't have the giant script-injection hole this one has via the direct inclusion of the query parameter), but you get the idea.
Next Steps
Next, I think that I'll show a quick example of bringing in a third-party dependency. The process for that is pretty quick, but I think it will be useful to show the mechanics and use it as an example of something that is a couple steps easier outside of Domino.
There are also a number of big topics remaining: user security, database access, and so forth. I don't know how deep I'll go into each of those, since the implementations vary so much in practice, but I think it'll be useful to at least have an example to start with.
Karl - Fri Mar 01 19:25:22 EST 2019
Great series!! What is preferred jsf implementation? Myfaces vs Mojarra. Jakarta uses Mojarra; however open liberty has Myfaces as default implementation. Reasoning? Preferred UI library? Primefaces, etc. Jakarta have any plans to advance UI components in JSF? Hope you keep the series going.
Jesse Gallagher - Wed Mar 06 08:10:41 EST 2019
Personally, I haven't used non-XPages JSF enough to have a preference for the different toolkits. It does seem, though, that JSF has a future in Jakarta: https://www.eclipse.org/community/eclipse_newsletter/2019/february/Jakarta_EE_9.php