-
Notifications
You must be signed in to change notification settings - Fork 45
devon4j deployment
As mentioned already in the devon4j project section, apart from the core
and api
project, apps created with devon4j also provide a server
project that configures the packaging of the app.
In our JumpTheQueue app we can verify that this server project is available:
So — using Maven — we are going to be able to easily package our app in a .war
file to be deployed in an application server like Tomcat (the default server provided in devonfw).
The server project provided in devon4j applications is an almost empty Maven project. It only has a pom.xml
file that is used to configure the packaging of the core project. Taking a closer look at this pom.xml
file, we realize that it only contains a single dependency to the core project:
...
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jtqj-core</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
...
It also includes the Spring Boot Maven Plugin, that allows us to package the project in .jar
or .war
archives and run the application "in-place":
...
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
...
</plugin>
</plugins>
...
Since this is a basic tutorial and there is no security or permission handling, we have to modify the files SecurityRestServiceImplTest
and PermissionCheckTest
in jtqj-core
, to disable the tests for these features. This is done by adding the @Disabled
annotation to the affected test methods.
Your Eclipse should automatically add the required dependencies for the annotation once you save the files, so they should contain the same import
statements as shown below. More specifically org.junit.jupiter
will be replacing the older org.junit
test framework, which was used before.
First, in src/test/java/…/general/service/impl/rest/SecurityRestServiceImplTest.java
disable the testLogin()
method and make sure that the class is annotated with @ExtendWith(SpringExtension.class)
, instead of the older @RunWith(SpringRunner.class)
annotation:
ℹ️
|
If this class doesn’t exist, go to the next one |
package com.devonfw.application.jtqj.general.service.impl.rest;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.web.client.RestTemplate;
import com.devonfw.application.jtqj.general.common.api.to.UserProfileTo;
import com.devonfw.application.jtqj.general.service.api.rest.SecurityRestService;
import com.devonfw.application.jtqj.general.service.base.test.RestServiceTest;
import com.devonfw.module.service.common.api.client.config.ServiceClientConfigBuilder;
/**
* This class tests the login functionality of {@link SecurityRestServiceImpl}.
*/
@ExtendWith(SpringExtension.class)
public class SecurityRestServiceImplTest extends RestServiceTest {
/** Logger instance. */
private static final Logger LOG = LoggerFactory.getLogger(SecurityRestServiceImplTest.class);
/**
* Test the login functionality as it will be used from a JavaScript client.
*/
@Test
@Disabled // Security via Login is currently not implemented, so ignore this test
public void testLogin() {
String login = "waiter";
String password = "waiter";
ResponseEntity<String> postResponse = login(login, password);
LOG.debug("Body: " + postResponse.getBody());
assertThat(postResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(postResponse.getHeaders().containsKey(HttpHeaders.SET_COOKIE)).isTrue();
}
/**
* Test of {@code SecurityRestService.getCsrfToken()}.
*/
@Test
public void testGetCsrfToken() {
String login = "waiter";
String password = "waiter";
SecurityRestService securityService = getServiceClientFactory().create(SecurityRestService.class,
new ServiceClientConfigBuilder().host("localhost").authBasic().userLogin(login).userPassword(password)
.buildMap());
CsrfToken csrfToken = securityService.getCsrfToken(null, null);
assertThat(csrfToken.getHeaderName()).isEqualTo("X-CSRF-TOKEN");
assertThat(csrfToken.getParameterName()).isEqualTo("_csrf");
assertThat(csrfToken.getToken()).isNotNull();
LOG.debug("Csrf Token: {}", csrfToken.getToken());
}
/**
* Test of {@link SecurityRestService#getCurrentUser()}.
*/
@Test
public void testGetCurrentUser() {
String login = "waiter";
String password = "waiter";
SecurityRestService securityService = getServiceClientFactory().create(SecurityRestService.class,
new ServiceClientConfigBuilder().host("localhost").authBasic().userLogin(login).userPassword(password)
.buildMap());
UserProfileTo userProfile = securityService.getCurrentUser();
assertThat(userProfile.getLogin()).isEqualTo(login);
}
/**
* Performs the login as required by a JavaScript client.
*
* @param userName the username of the user
* @param tmpPassword the password of the user
* @return @ {@link ResponseEntity} containing containing a cookie in its header.
*/
private ResponseEntity<String> login(String userName, String tmpPassword) {
String tmpUrl = "http://localhost:" + String.valueOf(this.port) + "/services/rest/login";
HttpEntity<String> postRequest = new HttpEntity<>(
"{\"j_username\": \"" + userName + "\", \"j_password\": \"" + tmpPassword + "\"}", new HttpHeaders());
ResponseEntity<String> postResponse = new RestTemplate().exchange(tmpUrl, HttpMethod.POST, postRequest,
String.class);
return postResponse;
}
}
And in src/test/java/…/general/common/base/PermissionCheckTest.java
just disable the permissionCheckAnnotationPresent()
method:
package com.devonfw.application.jtqj.general.common.base;
import java.lang.reflect.Method;
import java.util.Set;
import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import net.sf.mmm.util.filter.api.Filter;
import net.sf.mmm.util.reflect.api.ReflectionUtil;
import net.sf.mmm.util.reflect.base.ReflectionUtilImpl;
import org.assertj.core.api.SoftAssertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import com.devonfw.module.test.common.base.ModuleTest;
/**
* Tests the permission check in logic layer.
*/
public class PermissionCheckTest extends ModuleTest {
/**
* Check if all relevant methods in use case implementations have permission checks i.e. {@link RolesAllowed},
* {@link DenyAll} or {@link PermitAll} annotation is applied. This is only checked for methods that are declared in
* the corresponding interface and thus have the {@link Override} annotations applied.
*/
@Test
@Disabled // Permission Checks are currently not implemented, so ignore this test
public void permissionCheckAnnotationPresent() {
String packageName = "com.devonfw.application.jtqj";
Filter<String> filter = new Filter<String>() {
@Override
public boolean accept(String value) {
return value.contains(".logic.impl.usecase.Uc") && value.endsWith("Impl");
}
};
ReflectionUtil ru = ReflectionUtilImpl.getInstance();
Set<String> classNames = ru.findClassNames(packageName, true, filter);
Set<Class<?>> classes = ru.loadClasses(classNames);
SoftAssertions assertions = new SoftAssertions();
for (Class<?> clazz : classes) {
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
Method parentMethod = ru.getParentMethod(method);
if (parentMethod != null) {
Class<?> declaringClass = parentMethod.getDeclaringClass();
if (declaringClass.isInterface() && declaringClass.getSimpleName().startsWith("Uc")) {
boolean hasAnnotation = false;
if (method.getAnnotation(RolesAllowed.class) != null || method.getAnnotation(DenyAll.class) != null
|| method.getAnnotation(PermitAll.class) != null) {
hasAnnotation = true;
}
assertions.assertThat(hasAnnotation)
.as("Method " + method.getName() + " in Class " + clazz.getSimpleName() + " is missing access control")
.isTrue();
}
}
}
}
assertions.assertAll();
}
}
This is going to allow our application to pass the tests and be built.
Thanks to Spring Boot and the Spring Boot Maven Plugin, we can run our app using Maven. To do so, just open a command prompt with access to Maven (in our devonfw project folder we can simply do so by right clicking and selecting Open Devon CMD shell here
).
Now we need to follow these steps:
1.- As is explained in the devon4j configuration guide, the default application.properties
file used for packaging is located in src/main/resources/
(don’t use the one located in src/main/resources/config/
). We need to modify some settings in this file in order to gain access to the app:
server.port=8081
spring.application.name=jtqj
server.servlet.context-path=/jumpthequeue
2.- Install the jtqj
project in our local Maven repository:
C:\...\workspaces\main\jumpthequeue\java\jtqj> mvn install
3.- Go to the jtqj/server
project and boot the application:
C:\...\workspaces\main\jumpthequeue\java\jtqj\server> mvn spring-boot:run
The app should be launched in the Spring Boot embedded Tomcat server. Wait a few seconds until you see a console message like this:
{"timestamp":"20XX-XX-XXTXX:XX:XX.XXX+00:00","message":"Tomcat started on port(s): 8081 (http) with context path '/jumpthequeue'","logger_name":"org.springframework.boot.web.embedded.tomcat.TomcatWebServer","thread_name":"main","level":"INFO","appname":"jtqj"}
{"timestamp":"20XX-XX-XXTXX:XX:XX.XXX+00:00","message":"Started SpringBootApp in XX.XXX seconds (JVM running for XX.XXX)","logger_name":"com.devonfw.application.jtqj.SpringBootApp","thread_name":"main","level":"INFO","appname":"jtqj"}
Now we can try to access the app resource.
Open Postman, select the service GET and send:
http://localhost:8081/jumpthequeue/services/rest/visitormanagement/v1/visitor/1
If you get a response similar to the one in the image, you have verified that the app is running fine.
In the same way, using Maven we can package our project in a .war
file. As in the previous section, open a command prompt with access to Maven (in our devonfw project folder we can simply do so by right clicking and selecting Open Devon CMD shell here
). Now execute the following command in the projects root directory:
C:\...\workspaces\main\jumpthequeue\java\jtqj> mvn clean package
The packaging process (which includes compilation, tests and generation of the .war
file) will be launched. Once the process is finished you should see a result like this:
[INFO] Packaging webapp
[INFO] Assembling webapp [jtqj-server] in [C:\...\workspaces\main\jump-the-queue\jump-the-queue\java\jtqj\server\target\jtqj-server-v4]
[INFO] Processing war project
[INFO] Webapp assembled in [XXXX msecs]
[INFO] Building war: C:\...\workspaces\main\jump-the-queue\jump-the-queue\java\jtqj\server\target\jtqj-server-v4.war
[INFO]
[INFO] --- spring-boot-maven-plugin:2.1.6.RELEASE:repackage (default) @ jtqj-server ---
[INFO] Attaching repackaged archive C:\...\workspaces\main\jump-the-queue\jump-the-queue\java\jtqj\server\target\jtqj-server-bootified.war with classifier bootified
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for jtqj v4:
[INFO]
[INFO] jtqj ............................................... SUCCESS [ X.XXX s]
[INFO] jtqj-api ........................................... SUCCESS [ XX.XXX s]
[INFO] jtqj-core .......................................... SUCCESS [XX:XX min]
[INFO] jtqj-server ........................................ SUCCESS [ XX.XXX s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: XX:XX min
[INFO] Finished at: 20XX-XX-XXTXX:XX:XX+0X:00
[INFO] ------------------------------------------------------------------------
The packaging process creates two .war
files, that are stored in the \java\jtqj\server\target
directory. They contain the web application and can be deployed on any Servlet/JSP container.
Next Chapter: devon4ng Introduction
This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).