OpenShift for Java Developers

This article written by Shekhar Gulati, the author of OpenShift Cookbook, covers how Java developers can openly use OpenShift to develop and deploy Java applications. It also teaches us how to deploy Java EE 6 and Spring applications on OpenShift.

(For more resources related to this topic, see here.)

Creating and deploying Java EE 6 applications using the JBoss EAP and PostgreSQL 9.2 cartridges

Gone are the days when Java EE or J2EE (as it was called in the olden days) was considered evil. Java EE now provides a very productive environment to build web applications. Java EE has embraced convention over configuration and annotations, which means that you are no longer required to maintain XML to configure each and every component. In this article, you will learn how to build a Java EE 6 application and deploy it on OpenShift. This article assumes that you have basic knowledge of Java and Java EE 6. If you are not comfortable with Java EE 6, please read the official tutorial at http://docs.oracle.com/javaee/6/tutorial/doc/.

In this article, you will build a simple job portal that will allow users to post job openings and view a list of all the persisted jobs in the system. These two functionalities will be exposed using two REST endpoints.

The source code for the application created in this article is on GitHub at https://github.com/OpenShift-Cookbook/chapter7-jobstore-javaee6-simple. The example application that you will build in this article is a simple version of the jobstore application with only a single domain class and without any application interface. You can get the complete jobstore application source code on GitHub as well at https://github.com/OpenShift-Cookbook/chapter7-jobstore-javaee6.

Getting ready

To complete this article, you will need the rhc command-line client installed on your machine. Also, you will need an IDE to work with the application code. The recommended IDE to work with OpenShift is Eclipse Luna, but you can also work with other IDEs, such as IntelliJ Idea and NetBeans. Download and install the Eclipse IDE for Java EE developers from the official website at https://www.eclipse.org/downloads/.

How to do it…

Perform the following steps to create the jobstore application:

  1. Open a new command-line terminal, and go to a convenient location. Create a new JBoss EAP application by executing the following command:
    $ rhc create-app jobstore jbosseap-6
  2. The preceding command will create a Maven project and clone it to your local machine.
  3. Change the directory to jobstore, and execute the following command to add the PostgreSQL 9.2 cartridge to the application:
    $ rhc cartridge-add postgresql-9.2
  4. Open Eclipse and navigate to the project workspace. Then, import the application created in step 1 as a Maven project. To import an existing Maven project, navigate to File|Import|Maven|Existing Maven Projects. Then, navigate to the location of your OpenShift Maven application created in step 1.
  5. Next, update pom.xml to use Java 7. The Maven project created by OpenShift is configured to use JDK 6. Replace the properties with the one shown in the following code:
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  6. Update the Maven project to allow the changes to take effect. You can update the Maven project by right-clicking on the project and navigating to Maven|Update Project.
  7. Now, let us write the domain classes for our application. Java EE uses JPA to define the data model and manage entities. The application has one domain class: Job. Create a new package called org.osbook.jobstore.domain, and then create a new Java class called Job inside it. Have a look at the following code:
    @Entity
    public class Job {
     
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
     
    @NotNull
    private String title;
     
    @NotNull
    @Size(max = 4000)
    private String description;
     
    @Column(updatable = false)
    @Temporal(TemporalType.DATE)
    @NotNull
    private Date postedAt = new Date();
     
    @NotNull
    private String company;
     
    //setters and getters removed for brevity
     
    }
  8. Create a META-INF folder at src/main/resources, and then create a persistence.xml file with the following code:
    <?xml version="1.0" encoding="UTF-8"?>
    <persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-
    instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
    http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
    version="2.0"
    xmlns="http://java.sun.com/xml/ns/persistence">
     
    <persistence-unit name="jobstore" transaction-type="JTA">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-
    source>java:jboss/datasources/PostgreSQLDS</jta-data-
    source>
     
    <exclude-unlisted-classes>false</exclude-unlisted-
    classes>
     
    <properties>
    <property name="hibernate.show_sql" value="true" />
    <property name="hibernate.hbm2ddl.auto"
    value="update" />
    </properties>
    </persistence-unit>
     
    </persistence>
  9. Now, we will create the JobService class that will use the JPA EntityManager API to work with the database. Create a new package called org.osbook.jobstore.services, and create a new Java class as shown in the following code. It defines the save and findAll operations on the Job entity.
    @Stateless
    public class JobService {
     
    @PersistenceContext(unitName = "jobstore")
    private EntityManager entityManager;
     
    public Job save(Job job) {
    entityManager.persist(job);
    return job;
    }
     
    public List<Job> findAll() {
    return entityManager
    .createQuery("SELECT j from
    org.osbook.jobstore.domain.Job j order by j.postedAt desc",
    Job.class)
    .getResultList();
    }
    }
  10. Next, enable Contexts and Dependency Injection (CDI) in the jobstore application by creating a file with the name beans.xml in the src/main/webapp/WEB-INF directory as follows:
    <?xml version="1.0"?>
    <beans xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://jboss.org/schema/cdi/beans_1_0.xsd"/>
  11. The jobstore application will expose the REST JSON web service. Before you can write the JAX-RS resources, you have to configure JAX-RS in your application. Create a new package called org.osbook.jobstore.rest and a new class called RestConfig, as shown in the following code:
    @ApplicationPath("/api/v1")
    public class RestConfig extends Application {
    }
  12. Create a JAX-RS resource to expose the create and findAll operations of JobService as REST endpoints as follows:
    @Path("/jobs")
    public class JobResource {
     
    @Inject
    private JobService jobService;
     
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public Response createNewJob(@Valid Job job) {
    job = jobService.save(job);
    return Response.status(Status.CREATED).build();
    }
     
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public List<Job> showAll() {
    return jobService.findAll();
    }
    }
  13. Commit the code, and push it to the OpenShift application as shown in the following commands:
    $ git add .
    $ git commit -am "jobstore application created"
    $ git push
  14. After the build finishes successfully, the application will be accessible at http://jobstore-{domain-name}.rhcloud.com. Please replace domain-name with your own domain name.
  15. To test the REST endpoints, you can use curl. curl is a command-line tool for transferring data across various protocols. We will use it to test our REST endpoints. To create a new job, you will run the following curl command:
    $ curl -i -X POST -H "Content-Type: application/json" -H 
    "Accept: application/json" -d '{"title":"OpenShift
    Evangelist","description":"OpenShift
    Evangelist","company":"Red Hat"}'http://jobstore-{domain-
    name}.rhcloud.com/api/v1/jobs
  16. To view all the jobs, you can run the following curl command:
    $ curl http://jobstore-{domain-name}.rhcloud.com/api/v1/jobs

How it works…

In the preceding steps, we created a Java EE application and deployed it on OpenShift. In step 1, you used the rhc create-app command to create a JBoss EAP web cartridge application. The rhc command-line tool makes a request to the OpenShift broker and asks it to create a new application using the JBoss EAP cartridge. Every OpenShift web cartridge specifies a template application that will be used as the default source code of the application. For Java web cartridges (JBoss EAP, JBoss AS7, Tomcat 6, and Tomcat 7), the template is a Maven-based application. After the application is created, it is cloned to the local machine using Git. The directory structure of the application is shown in the following command:

$ ls -a
.git .openshift README.md pom.xml deployments src

As you can see in the preceding command, apart from the .git and .openshift directories, this looks like a standard Maven project. OpenShift uses Maven to manage application dependencies and build your Java applications.

Let us take a look at what's inside the jobstore directory to better understand the layout of the application:

  • The src directory: This directory contains the source code for the template application generated by OpenShift. You need to add your application source code here. The src folder helps in achieving source code deployment when following the standard Maven directory conventions.
  • The pom.xml file: The Java applications created by OpenShift are Maven-based projects. So, a pom.xml file is required when you do source code deployment on OpenShift. This pom.xml file has a profile called openshift, which will be executed when you push code to OpenShift as shown in the following code. This profile will create a ROOT WAR file based upon your application source code.
    <profiles>
    <profile>
    <id>openshift</id>
    <build>
    <finalName>jobstore</finalName>
    <plugins>
    <plugin>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.1.1</version>
    <configuration>
    <outputDirectory>deployments</outputDirectory>
    <warName>ROOT</warName>
    </configuration>
    </plugin>
    </plugins>
    </build>
    </profile>
    </profiles>
  • The deployments directory: You should use this directory if you want to do binary deployments on OpenShift, that is, you want to deploy a WAR or EAR file directly instead of pushing the source code.
  • The .git directory: This is a local Git repository. This directory contains the complete history of the repository. The config file in.git/ contains the configuration for the repository. It defines a Git remote origin that points to the OpenShift application gear SSH URL. This makes sure that when you do git push, the source code is pushed to the remote Git repository hosted on your application gear. You can view the details of the origin Git remote by executing the following command:
    $ git remote show origin
  • The .openshift directory: This is an OpenShift-specific directory, which can be used for the following purposes:
    • The files under the action_hooks subdirectory allow you to hook onto the application lifecycle.
    • The files under the config subdirectory allow you to make changes to the JBoss EAP configuration. The directory contains the standalone.xml JBoss EAP-specific configuration file.
    • The files under the cron subdirectory are used when you add the cron cartridge to your application. This allows you to run scripts or jobs on a periodic basis.
    • The files under the markers subdirectory allow you to specify whether you want to use Java 6 or Java 7 or you want to do hot deploy or debug the application running in the Cloud, and so on.

In step 2, you added the PostgreSQL 9.2 cartridge to the application using the rhc cartridge-add command. We will use the PostgreSQL database to store the jobstore application data. Then, in step 3, you imported the project in the Eclipse IDE as a Maven project. Eclipse Kepler has inbuilt support for Maven applications, which makes it easier to work with Maven-based applications.

From step 3 through step 5, you updated the project to use JDK 1.7 for the Maven compiler plugin. All the OpenShift Java applications use OpenJDK 7, so it makes sense to update the application to also use JDK 1.7 for compilation.

In step 6, you created the job domain class and annotated it with JPA annotations. The @Entity annotation marks the class as a JPA entity. An entity represents a table in the relational database, and each entity instance corresponds to a row in the table. Entity class fields represent the persistent state of the entity. You can learn more about JPA by reading the official documentation at http://docs.oracle.com/javaee/6/tutorial/doc/bnbpz.html.

The @NotNull and @Size annotation marks are Bean Validation annotations. Bean Validation is a new validation model available as a part of the Java EE 6 platform. The @NotNull annotation adds a constraint that the value of the field must not be null. If the value is null, an exception will be raised. The @Size annotation adds a constraint that the value must match the specified minimum and maximum boundaries. You can learn more about Bean Validation by reading the official documentation at http://docs.oracle.com/javaee/6/tutorial/doc/gircz.html.

In JPA, entities are managed within a persistence context. Within the persistence context, the entity manager manages the entities. The configuration of the entity manager is defined in a standard configuration XML file called persitence.xml. In step 7, you created the persistence.xml file. The most important configuration option is the jta-datasource-source configuration tag. It points to java:jboss/datasources/PostgreSQLDS. When a user creates a JBoss EAP 6 application, then OpenShift defines a PostgreSQL datasource in the standalone.xml file. The standalone.xml file is a JBoss configuration file, which includes the technologies required by the Java EE 6 full profile specification plus Java Connector 1.6 architecture, Java XML API for RESTful web services, and OSGi. Developers can override the configuration by making changes to the standalone.xml file in the .openshift/config location of your application directory. So, if you open the standalone.xml file in .openshift/config/ in your favorite editor, you will find the following PostgreSQL datasource configuration:

<datasource jndi-name="java:jboss/datasources/PostgreSQLDS"
enabled="${postgresql.enabled}" use-java-context="true" pool-
name="PostgreSQLDS"
use-ccm="true">
<connection-
url>jdbc:postgresql://${env.OPENSHIFT_POSTGRESQL_DB_HOST}:${env.OP
ENSHIFT_POSTGRESQL_DB_PORT}/${env.OPENSHIFT_APP_NAME}
</connection-url>
<driver>postgresql</driver>
<security>
<user-name>${env.OPENSHIFT_POSTGRESQL_DB_USERNAME}</user-name>
<password>${env.OPENSHIFT_POSTGRESQL_DB_PASSWORD}</password>
</security>
<validation>
<check-valid-connection-sql>SELECT 1</check-valid-connection-
sql>
<background-validation>true</background-validation>
<background-validation-millis>60000</background-validation-
millis>
<!--<validate-on-match>true</validate-on-match> -->
</validation>
<pool>
<flush-strategy>IdleConnections</flush-strategy>
<allow-multiple-users />
</pool>
</datasource>

In step 8, you created stateless Enterprise JavaBeans (EJBs) for our application service layer. The service classes work with the EntityManager API to perform operations on the Job entity.

In step 9, you configured CDI by creating the beans.xml file in the src/main/webapp/WEB-INF directory. We are using CDI in our application so that we can use dependency injection instead of manually creating the objects ourselves. The CDI container will manage the bean life cycle, and the developer just has to write the business logic. To let the JBoss application server know that we are using CDI, we need to create a file called beans.xml in our WEB-INF directory. The file can be completely blank, but its presence tells the container that the CDI framework needs to be loaded.

In step 10 and step 11, you configured JAX-RS and defined the REST resources for the Job entity. You activated JAX-RS by creating a class that extends javax.ws.rs.ApplicationPath. You need to specify the base URL under which your web service will be available. This is done by annotating the RestConfig class with the ApplicationPath annotation. You used /api/v1 as the application path.

In step 12, you added and committed the changes to the local repository and then pushed the changes to the application gear. After the bits are pushed, OpenShift will stop all the cartridges and then invoke the mvn -e clean package -Popenshift -DskipTests command to build the project. Maven will build a ROOT.war file, which will be copied to the JBoss EAP deployments folder. After the build successfully finishes, all the cartridges are started. Then the new updated ROOT.war file will be deployed. You can view the running application at http://jobstore-{domain-name}.rhcloud.com. Please replace {domain-name} with your account domain name.

Finally, you tested the REST endpoints using curl in step 14.

There's more…

You can perform all the aforementioned steps with just a single command as follows:

$ rhc create-app jobstore jbosseap postgresql-9.2 --from-code
   https://github.com/OpenShift-Cookbook/chapter7-jobstore-javaee6-simple.git
   --timeout 180

Configuring application security by defining the database login module in standalone.xml

The application allows you to create company entities and then assign jobs to them. The problem with the application is that it is not secured. The Java EE specification defines a simple, role-based security model for EJBs and web components. JBoss security is an extension to the application server and is included by default with your OpenShift JBoss applications. You can view the extension in the JBoss standalone.xml configuration file. The standalone.xml file exists in the .openshift/config location. The following code shows the extension:

<extension module="org.jboss.as.security" />

OpenShift allows developers to update the standalone.xml configuration file to meet their application needs. You make a change to the standalone.xml configuration file, commit the change to the local Git repository, then push the changes to the OpenShift application gear. Then, after the successful build, OpenShift will replace the existing standalone.xml file with your updated configuration file and then finally start the server. But please make sure that your changes are valid; otherwise, the application will fail to start.

In this article, you will learn how to define the database login module in standalone.xml to authenticate users before they can perform any operation with the application.

The source code for the application created in this article is on GitHub at https://github.com/OpenShift-Cookbook/chapter7-jobstore-security.

Getting ready

This article builds on the Java EE 6 application.

How to do it…

Perform the following steps to add security to your web application:

  1. Create the OpenShift application using the following command:
    $ rhc create-app jobstore jbosseap postgresql-9.2 --from-code
       https://github.com/OpenShift-Cookbook/chapter7-jobstore- javaee6-simple.git
       --timeout 180
  2. After the application creation, SSH into the application gear, and connect with the PostgreSQL database using the psql client. Then, create the following tables and insert the test data:
    $ rhc ssh
    $ psql
    jobstore=# CREATE TABLE USERS(email VARCHAR(64) PRIMARY KEY,
    password VARCHAR(64));
    jobstore=# CREATE TABLE USER_ROLES(email VARCHAR(64), role
    VARCHAR(32));
    jobstore=# INSERT into USERS values('admin@jobstore.com',
    'ISMvKXpXpadDiUoOSoAfww==');
    jobstore=# INSERT into USER_ROLES values('admin@jobstore.com',
    'admin');
  3. Exit from the SSH shell, and open the standalone.xml file in the.openshift/config directory. Update the security domain with the following code:
    <security-domain name="other" cache-type="default">
    <authentication>
    <login-module code="Remoting" flag="optional">
    <module-option name="password-stacking"
    value="useFirstPass" /> </login-module> <login-module code="Database" flag="required"> <module-option name="dsJndiName" value="java:jboss/datasources/PostgreSQLDS" /> <module-option name="principalsQuery" value="select password from USERS where email=?" /> <module-option name="rolesQuery" value="select role, 'Roles' from USER_ROLES where
    email=?" /> <module-option name="hashAlgorithm" value="MD5" /> <module-option name="hashEncoding" value="base64" /> </login-module> </authentication> </security-domain>
  4. Create the web deployment descriptor (that is, web.xml) in the src/main/webapp/WEB-INF folder. Add the following content to it:
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="3.0"
    xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">   <security-constraint> <web-resource-collection> <web-resource-name>WebAuth</web-resource-name> <description>application security constraints </description> <url-pattern>/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>FORM</auth-method> <realm-name>jdbcRealm</realm-name> <form-login-config> <form-login-page>/login.html</form-login-
    page> <form-error-page>/error.html</form-error-
    page> </form-login-config> </login-config> <security-role> <role-name>admin</role-name> </security-role>   </web-app>
  5. Create the login.html file in the src/main/webapp directory. The login.html page will be used for user authentication. The following code shows the contents of this file:
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Login</title>
    <link href="//cdnjs.cloudflare.com/ajax/libs/twitter-
    bootstrap/3.1.1/css/bootstrap.css" rel="stylesheet"> </head> <body> <div class="container"> <form class="form-signin" role="form" method="post"
    action="j_security_check"> <h2 class="form-signin-heading">Please sign in</h2> <input type="text" id="j_username"
    name="j_username" class="form-control" placeholder="Email
    address" required autofocus> <input type="password" id="j_password"
    name="j_password" class="form-control"
    placeholder="Password" required> <button class="btn btn-lg btn-primary btn-block"
    type="submit">Sign in</button> </form> </div> </body> </html>
  6. Create an error.html file in the src/main/webapp directory. The error.html page will be shown after unsuccessful authentication. The following code shows the contents of this file:
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="US-ASCII">
    <title>Error page</title>
    </head>
    <body>
    <h2>Incorrect username/password</h2>
    </body>
    </html>
  7. Commit the changes, and push them to the OpenShift application gear:
    $ git add .
    $ git commit –am "enabled security"
    $ git push
  8. Go to the application page at http://jobstore-{domain-name}.rhcloud.com, and you will be asked to log in before you can view the application. Use admin@jobstore.com/admin as the username-password combination to log in to the application.

How it works…

Let's now understand what you did in the preceding steps. In step 1, you recreated the jobstore application we developed previously. Next, in step 2, you performed an SSH into the application gear and created the USERS and USER_ROLES tables. These tables will be used by the JBoss database login module to authenticate users. As our application does not have the user registration functionality, we created a default user for the application. Storing the password as a clear text string is a bad practice, so we have stored the MD5 hash of the password. The MD5 hash of the admin password is ISMvKXpXpadDiUoOSoAfww==. If you want to generate the hashed password in your application, I have included a simple Java class, which uses org.jboss.crypto.CryptoUtil to generate the MD5 hash of any string. The CryptoUtil class is part of the picketbox library. The following code depicts this:

import org.jboss.crypto.CryptoUtil;
 
public class PasswordHash {
 
public static String getPasswordHash(String password) {
return CryptoUtil.createPasswordHash("MD5", 
CryptoUtil.BASE64_ENCODING, null, null, password); }   public static void main(String[] args) throws Exception { System.out.println(getPasswordHash("admin")); } }

In step 3, you logged out of the SSH session and updated the standalone.xml JBoss configuration file with the database login module configuration. There are several login module implementations available out of the box. This article will only talk about the database login module, as discussing all the modules is outside the scope of this article. You can read about all the login modules at https://docs.jboss.org/author/display/AS7/Security+subsystem+configuration. The database login module checks the user credentials against a relational database. To configure the database login module, you have to specify a few configuration options. The dsJndiName option is used to specify the application datasource. As we are using a configured PostgreSQL datasource for our application, you specified the same dsJndiName option value. Next, you have to specify the SQL queries to fetch the user and its roles. Then, you have specified that the password will be hashed against an MD5 hash algorithm by specifying the hashAlgorithm configuration.

In step 4, you applied the database login module to the jobstore application by defining the security constraints in web.xml. This configuration will add a security constraint on all the web resources of the application that will restrict access to authenticated users with role admin. You have also configured your application to use FORM-based authentication. This will make sure that when unauthenticated users visit the website, they will be redirected to the login.html page created in step 5. If the user enters a wrong e-mail/password combination, then they will be redirected to the error.html page created in step 6.

Finally, in step 7, you committed the changes to the local Git repository and pushed the changes to the application gear. OpenShift will make sure that the JBoss EAP application server uses the updated standalone.xml configuration file. Now, the user will be asked to authenticate before they can work with the application.

Summary

This article showed us how to configure application security. In, this article, we also learned about the different ways in which Java applications can be developed on OpenShift. The article explained the database login module.


Further resources on this subject:


You've been reading an excerpt of:

OpenShift Cookbook

Explore Title