I am trying to benchmark an application running in the Cloud with JMeter. The underlying protocol uses websockets, but there are some proprietary libraries that I need to use to make those calls. I looked at some websocket plugins for JMeter. But more than just testing I want to use the ability of JMeter to be able to make distributed load generation for my server. I would like to use my own client (written in Java) to make the actual request for the server. Is this some how possible?
There are different implementations of WebSocket Client API, i.e. Jetty Websocket API or Java API for WebSocket
Recently I've been investigating on how WebSockets can be tested using JMeter Load Testing Cloud Solution. Kindly see proof of concept details below. It's using JavaSamplerClient API so extension being packaged as a .jar and put under JMeter Classpath will be available as Java Request Sampler.
First of all you'll need Tyrus – open source JSR-356 implementation. Tyrus requires Java 7 so make sure that you use Java SE 7 to build and execute the extension. The most convenient way of getting dependencies is using following Apache Maven configuration file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>blazemeter-websocket</groupId>
<artifactId>blazemeter-websocket</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-client-api</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.tyrus</groupId>
<artifactId>tyrus-client</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.tyrus</groupId>
<artifactId>tyrus-container-grizzly</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>javax.json</groupId>
<artifactId>javax.json-api</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.json</artifactId>
<version>1.0.1</version>
</dependency>
</dependencies>
</project>
Invokation of target “mvn dependency:copy-dependencies” will download all required jars into /target/dependency folder. However you may with to continue with build, package, etc. maven plugins.
Source code of the extension is follows:
package com.blazemeter;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient;
import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;
import org.glassfish.tyrus.client.ClientManager;
import javax.websocket.*;
import java.io.IOException;
import java.net.URI;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
#ClientEndpoint
public class BlazemeterWebsocketRequest extends AbstractJavaSamplerClient {
private static String ws_uri;
private static String ws_message;
private static String response_message;
private static CountDownLatch latch;
private static final Logger log = LoggingManager.getLoggerForClass();
#Override
public Arguments getDefaultParameters() {
Arguments params = new Arguments();
params.addArgument("URI", "ws://echo.websocket.org");
params.addArgument("Message", "Blazemeter rocks!");
return params;
}
#Override
public void setupTest(JavaSamplerContext context) {
ws_uri = context.getParameter("URI");
ws_message = context.getParameter("Message");
}
#Override
public SampleResult runTest(JavaSamplerContext javaSamplerContext) {
SampleResult rv = new SampleResult();
rv.sampleStart();
latch = new CountDownLatch(1);
ClientManager client = ClientManager.createClient();
try {
client.connectToServer(BlazemeterWebsocketRequest.class, new URI(ws_uri));
latch.await(1L, TimeUnit.SECONDS);
} catch (Throwable e) {
throw new RuntimeException(e);
}
rv.setSuccessful(true);
rv.setResponseMessage(response_message);
rv.setResponseCode("200");
if (response_message != null) {
rv.setResponseData(response_message.getBytes());
}
rv.sampleEnd();
return rv;
}
#OnOpen
public void onOpen(Session session) {
log.info("Connected ... " + session.getId());
try {
session.getBasicRemote().sendText(ws_message);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#OnMessage
public String onMessage(String message, Session session) {
log.info("Received ... " + message + " on session " + session.getId());
response_message = message;
try {
session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE,""));
} catch (IOException e) {
e.printStackTrace();
}
return response_message;
}
#OnClose
public void onClose(Session session, CloseReason closeReason) {
log.info(String.format("Session %s close because of %s", session.getId(), closeReason));
}
}
Hope this helps,
D.
Related
As a RPGLE programmer I was asked to convert an IMAP mail poller to EWS. So I'm a bit out of my comfort zone.
With all documentation I managed to cobble a lot of pieces together, but it looks like I'm missing a vital piece because the program keep crashing at Folder folder = service.bindToFolder(folderId, propertySet);
It first I thought it had something to do with authorization. But then I found a little program EwsEditor at Github which works just fine.
Could someone point me to what I am missing?
With all documentation I managed to cobble a lot of pieces together. My testing code:
package test.ewstest;
import com.microsoft.aad.msal4j.ClientCredentialFactory;
import com.microsoft.aad.msal4j.ClientCredentialParameters;
import com.microsoft.aad.msal4j.ConfidentialClientApplication;
import java.io.File;
import java.io.FileInputStream;
import java.net.URI;
import java.util.Collections;
import java.util.Properties;
import microsoft.exchange.webservices.data.core.ExchangeService;
import microsoft.exchange.webservices.data.core.PropertySet;
import microsoft.exchange.webservices.data.core.enumeration.misc.ConnectingIdType;
import microsoft.exchange.webservices.data.core.enumeration.misc.ExchangeVersion;
import microsoft.exchange.webservices.data.core.enumeration.property.BasePropertySet;
import microsoft.exchange.webservices.data.core.enumeration.property.WellKnownFolderName;
import microsoft.exchange.webservices.data.core.enumeration.search.FolderTraversal;
import microsoft.exchange.webservices.data.core.service.schema.FolderSchema;
import microsoft.exchange.webservices.data.credential.TokenCredentials;
import microsoft.exchange.webservices.data.misc.ImpersonatedUserId;
import microsoft.exchange.webservices.data.property.complex.FolderId;
import microsoft.exchange.webservices.data.property.complex.Mailbox;
import microsoft.exchange.webservices.data.search.FindFoldersResults;
import microsoft.exchange.webservices.data.search.FolderView;
import microsoft.exchange.webservices.data.core.service.folder.Folder;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
public class EwsTest {
static String exchangeUser;
static String exchangeTenant;
static String exchangeClientId;
static String exchangeClientSecret;
static ExchangeService service;
static Logger logger = LogManager.getLogger(EwsTest.class.getName());
static Properties properties = new Properties();
public static void main(String[] args) {
try {
properties.load(new FileInputStream(System.getProperty("user.dir") + File.separator + "EwsTest.properties"));
exchangeUser = properties.getProperty("user");
exchangeTenant = properties.getProperty("tenant");
exchangeClientId = properties.getProperty("client");
exchangeClientSecret = properties.getProperty("secret");
createConnection();
displayFolders();
}
catch (Exception ex) {
logger.error(ex.getMessage(), ex);
}
}
static void createConnection() throws Exception {
service = new ExchangeService(ExchangeVersion.Exchange2010_SP2);
service.getHttpHeaders().put("X-AnchorMailbox",exchangeUser);
service.getHttpHeaders().put("X-PublicFolderMailbox",exchangeUser);
service.setCredentials(new TokenCredentials(createOauthToken()));
service.setImpersonatedUserId(new ImpersonatedUserId(ConnectingIdType.SmtpAddress, exchangeUser));
service.setUrl(new URI("https://outlook.office365.com/EWS/Exchange.asmx"));
}
static String createOauthToken() throws Exception {
ConfidentialClientApplication app = ConfidentialClientApplication.builder(
exchangeClientId,
ClientCredentialFactory.createFromSecret(exchangeClientSecret))
.authority("https://login.microsoftonline.com/" + exchangeTenant + "/")
.build();
ClientCredentialParameters clientCredentialParam = ClientCredentialParameters.builder(
Collections.singleton("https://outlook.office365.com/.default"))
.build();
return app.acquireToken(clientCredentialParam).get().accessToken();
}
static void displayFolders() throws Exception {
PropertySet propertySet = new PropertySet(BasePropertySet.IdOnly);
propertySet.add(FolderSchema.DisplayName);
FolderView view = new FolderView(100);
view.setPropertySet(propertySet);
view.setTraversal(FolderTraversal.Deep);
Mailbox mailbox = new Mailbox(exchangeUser);
FolderId folderId = new FolderId(WellKnownFolderName.MsgFolderRoot, mailbox);
logger.info("service.bindToFolder");
Folder folder = service.bindToFolder(folderId, propertySet);
logger.info("Ok");
logger.info("service.findFolders");
FindFoldersResults findFolderResults = service.findFolders(folder.getId(), view);
logger.info("Ok");
// find specific folder
for (Folder f : findFolderResults)
{
logger.info(f.getId());
}
}
}
And my Netbeans maven dependencies:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>EwsTest</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-1.2-api</artifactId>
<version>2.11.2</version>
</dependency>
<dependency>
<groupId>javax.xml.ws</groupId>
<artifactId>jaxws-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>msal4j</artifactId>
<version>1.13.3</version>
</dependency>
<dependency>
<groupId>com.microsoft.ews-java-api</groupId>
<artifactId>ews-java-api</artifactId>
<version>2.0</version>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>19</maven.compiler.source>
<maven.compiler.target>19</maven.compiler.target>
<exec.mainClass>test.ewstest.EwsTest</exec.mainClass>
</properties>
</project>
I created my EwsTest.properties file and filled it with the keys:
user=example#example.nl
client=9999x9xx-xx99-x99x-xx99-xx99x9999x9x
tenant=x99x9999-xx99-9999-xx99-9999xx9x9999
secret=XXxxX!9xx-...-
When I run the program, I see that the connection is being build and a token is being exchanged. But it crashes at the statement:
Folder folder = service.bindToFolder(folderId, propertySet);
With an error: The request failed. An internal server error occurred. The operation failed.
microsoft.exchange.webservices.data.core.exception.service.remote.ServiceRequestException: The request failed. An internal server error occurred. The operation failed.
at microsoft.exchange.webservices.data.core.request.SimpleServiceRequestBase.internalExecute(SimpleServiceRequestBase.java:74) ~[ews-java-api-2.0.jar:?]
at microsoft.exchange.webservices.data.core.request.MultiResponseServiceRequest.execute(MultiResponseServiceRequest.java:158) ~[ews-java-api-2.0.jar:?]
at microsoft.exchange.webservices.data.core.ExchangeService.bindToFolder(ExchangeService.java:504) ~[ews-java-api-2.0.jar:?]
at test.ewstest.EwsTest.displayFolders(EwsTest.java:91) ~[classes/:?]
at test.ewstest.EwsTest.main(EwsTest.java:49) [classes/:?]
Caused by: microsoft.exchange.webservices.data.core.exception.service.remote.ServiceResponseException: An internal server error occurred. The operation failed.
at microsoft.exchange.webservices.data.core.request.ServiceRequestBase.processWebException(ServiceRequestBase.java:548) ~[ews-java-api-2.0.jar:?]
at microsoft.exchange.webservices.data.core.request.ServiceRequestBase.validateAndEmitRequest(ServiceRequestBase.java:641) ~[ews-java-api-2.0.jar:?]
at microsoft.exchange.webservices.data.core.request.SimpleServiceRequestBase.internalExecute(SimpleServiceRequestBase.java:62) ~[ews-java-api-2.0.jar:?]
... 4 more
I tried several folders like inbox and root but they all give that same error:
FolderId folderId = new FolderId(WellKnownFolderName.MsgFolderRoot, mailbox);
But since EwsEditor seems to work, I'm clearly missing something. I tried looking at the code of EwsEditor but that's C# which is even harder to understand for me.
i am trying to embed a HTTP-Server into an existing java Application. My goal is to create a small rest API as an interface to send commands to the server application it runs in.
I planned using Jakarta and Jersey 3 with Jetty as embeded HTTP-Server. My starting point was the following topic which was for Jersey 2 but i tried my luck: Embed jersey in java application
My problem is that i get 404 Not Found back when i try to call http://localhost/login/status in my browser. The page is blank. When i switch to using Grizzly2 as embedded HTTP-Server and type the url in the browser, the result is the same. The only difference in Grizzly2 i could spot is when i only call http://localhost/ i get an error page additionally to the 404 Not Found response back. As soon as i add /login to the url, i get the 404 Not Found response without an error page. What could be the reason the server does not pick up my resources?
I am using the Eclipse IDE. First i created a clean Maven project, added the following dependencies and created my test code:
org.glassfish.jersey.core -> jersey-server
org.glassfish.jersey.containers -> jersey-container-jetty-http
On my first startup i got some missing class errors, searched for the dependencies they are in and added them to the pom. Following is my current test code.
<!-- pom.xml -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>test-ee</groupId>
<artifactId>test-ee-embed</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey</groupId>
<artifactId>jersey-bom</artifactId>
<version>3.0.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-bom</artifactId>
<version>3.0.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-jetty-http</artifactId>
</dependency>
</dependencies>
</project>
// LoginserverRestApi.java
package de.l2d;
import org.glassfish.jersey.server.ResourceConfig;
import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
#ApplicationPath("/login")
public class LoginserverRestApi extends ResourceConfig {
#GET #Path("/status")
public String status() {
// TODO Return real server statistics.
return "{\"status\":\"ok\"}";
}
}
// RestApiServer.java
package de.l2d;
import java.net.URI;
import org.eclipse.jetty.server.Server;
import org.glassfish.jersey.jetty.JettyHttpContainerFactory;
import jakarta.ws.rs.core.UriBuilder;
public class RestApiServer {
private Server server;
private RestApiServer() {
URI baseUri = UriBuilder.fromUri("http://localhost/").build();
server = JettyHttpContainerFactory.createServer(baseUri, new LoginserverRestApi(), false);
}
public void start() throws Exception {
server.start();
}
public void stop() throws Exception {
server.stop();
}
private static final class SingletonHolder {
protected static RestApiServer instance = new RestApiServer();
}
public static RestApiServer getInstance() {
return SingletonHolder.instance;
}
}
// Main.java
package de.l2d;
public class Main {
public static void main(String[] args) throws Exception {
RestApiServer.getInstance().start();
Thread.currentThread().join();
RestApiServer.getInstance().stop();
}
}
I found the solution to my problem. However, i do not know why it behaves like this.
I had to use the Path annotation instead of the ApplicationPath annotation on the LoginserverRestApi class and additionally construct a ResourceConfig with LoginserverRestApi.class as parameter and pass that Object to the JettyHttpContainerFactory.createServer static method. Following are the two files which differ from the initial post:
// LoginserverRestApi.java
package de.l2d;
import org.glassfish.jersey.server.ResourceConfig;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
#Path("/login")
public class LoginserverRestApi extends ResourceConfig {
#GET #Path("/status")
public String status() {
// TODO Return real server statistics.
return "{\"status\":\"ok\"}";
}
}
// RestApiServer.java
package de.l2d;
import java.net.URI;
import org.eclipse.jetty.server.Server;
import org.glassfish.jersey.jetty.JettyHttpContainerFactory;
import org.glassfish.jersey.server.ResourceConfig;
import jakarta.ws.rs.core.UriBuilder;
public class RestApiServer {
private Server server;
private RestApiServer() {
URI baseUri = UriBuilder.fromUri("http://localhost/").build();
ResourceConfig config = new ResourceConfig(LoginserverRestApi.class);
server = JettyHttpContainerFactory.createServer(baseUri, config, false);
}
public void start() throws Exception {
server.start();
}
public void stop() throws Exception {
server.stop();
}
private static final class SingletonHolder {
protected static RestApiServer instance = new RestApiServer();
}
public static RestApiServer getInstance() {
return SingletonHolder.instance;
}
}
I am following many examples of serenity bdd testing API. Mainly (https://github.com/serenity-bdd/serenity-core/blob/master/serenity-screenplay-rest/src/test/java/net/serenitybdd/screenplay/rest/examples/WhenRetrievingUserDetails.java)
But when I run my code I am always getting:
TEST FAILED WITH ERROR: User attempts to recover the password
---------------------------------------------------------------------
[main] ERROR net.serenitybdd.core.Serenity - TEST FAILED AT STEP Rest
[main] ERROR net.serenitybdd.core.Serenity - SERENITY_DISABLE_REST_CALLS_AFTER_FAILURES
net.serenitybdd.core.exceptions.SerenityManagedException: SERENITY_DISABLE_REST_CALLS_AFTER_FAILURES
at net.serenitybdd.rest.utils.RestExecutionHelper.restCallsAreDisabled(RestExecutionHelper.java:28)
at net.serenitybdd.rest.utils.RestSpecificationFactory.getInstrumentedRequestSpecification(RestSpecificationFactory.java:105)
at net.serenitybdd.rest.utils.RestDecorationHelper.decorate(RestDecorationHelper.java:20)
at net.serenitybdd.rest.SerenityRest.given(SerenityRest.java:220)
at net.serenitybdd.screenplay.rest.interactions.RestInteraction.rest(RestInteraction.java:32)
at net.serenitybdd.screenplay.rest.interactions.Post$$EnhancerByCGLIB$$6437c6d9.CGLIB$rest$3(<generated>)
at net.serenitybdd.screenplay.rest.interactions.Post$$EnhancerByCGLIB$$6437c6d9$$FastClassByCGLIB$$49fc3d71.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
at net.thucydides.core.steps.BaseMethodRunner.invokeMethod(BaseMethodRunner.java:10)
at net.thucydides.core.steps.NormalMethodRunner.invokeMethodAndNotifyFailures(NormalMethodRunner.java:20)
at net.thucydides.core.steps.StepInterceptor.runNormalMethod(StepInterceptor.java:361)
at net.thucydides.core.steps.StepInterceptor.testStepResult(StepInterceptor.java:132)
at net.thucydides.core.steps.StepInterceptor.intercept(StepInterceptor.java:68)
at net.serenitybdd.screenplay.rest.interactions.Post$$EnhancerByCGLIB$$6437c6d9.rest(<generated>)
at net.serenitybdd.screenplay.rest.interactions.Post.performAs(Post.java:24)
at net.serenitybdd.screenplay.rest.interactions.Post$$EnhancerByCGLIB$$6437c6d9.CGLIB$performAs$0(<generated>)
at net.serenitybdd.screenplay.rest.interactions.Post$$EnhancerByCGLIB$$6437c6d9$$FastClassByCGLIB$$49fc3d71.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
at net.thucydides.core.steps.StepInterceptor.invokeMethod(StepInterceptor.java:449)
at net.thucydides.core.steps.StepInterceptor.executeTestStepMethod(StepInterceptor.java:434)
at net.thucydides.core.steps.StepInterceptor.runTestStep(StepInterceptor.java:409)
at net.thucydides.core.steps.StepInterceptor.runOrSkipMethod(StepInterceptor.java:150)
at net.thucydides.core.steps.StepInterceptor.testStepResult(StepInterceptor.java:137)
at net.thucydides.core.steps.StepInterceptor.intercept(StepInterceptor.java:68)
at net.serenitybdd.screenplay.rest.interactions.Post$$EnhancerByCGLIB$$6437c6d9.performAs(<generated>)
at net.serenitybdd.screenplay.Actor.perform(Actor.java:100)
at net.serenitybdd.screenplay.Actor.attemptsTo(Actor.java:84)
at jarv.serenity.carnival.features.steps.steps.RecoverUserNameSteps.he_attemps_to_recover_password(RecoverUserNameSteps.java:40)
at ✽.he sends the required information to recover his password(src/test/resources/features/ProfileManagement.feature:7)
I have the following test steps:
package jarv.serenity.carnival.features.steps.steps;
import net.serenitybdd.screenplay.rest.abilities.CallAnApi;
import net.serenitybdd.screenplay.rest.interactions.Post;
import cucumber.api.java.Before;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;
import net.serenitybdd.screenplay.Actor;
import static org.hamcrest.Matchers.equalTo;
import static net.serenitybdd.screenplay.rest.questions.ResponseConsequence.seeThatResponse;
public class RecoverUserNameSteps {
private String theRestApiBaseUrl = "https://www.carnival.com/ProfileManagement/api/v1.0";
private Actor jorge;
private String userName;
#Before
public void set_the_test(){
jorge = Actor.named("Jorge, learning serenity rest").whoCan(CallAnApi.at(theRestApiBaseUrl));
}
#Given("^Jorge is not a registered user$")
public void jorge_is_not_registered() throws Throwable {
//TODO: Investigate what should be done here???
}
#Given("^he wants to recover his password$")
public void jorge_wants_to_recover_the_password() throws Throwable {
//I am setting this as blank cause the service to retrieve the password will only failed if request parameter is blank!!!
//When sending any other kind of string response is 202 Accepted, only blanks generate 400 Bad request
//Test may need to be changed to recover username instead, which has more options to get the error code
userName = "";
}
#When("^he sends the required information to recover his password$")
public void he_attemps_to_recover_password() throws Throwable {
//TODO: Create Object Model for Request Body and send it. Anyway example is way too simple to do that and I am running out of time Julian.
jorge.attemptsTo(
Post.to("/Accounts/Password/Forgot")
.with(request -> request.header("Content-Type", "application/json")
.body("{\"username\": \"joe\"}")
)
);
}
#Then("^he should be informed that operation could not be done$")
public void he_must_see_a_failed_response() throws Throwable {
jorge.should(
seeThatResponse( "Recover for password was denied",
response -> response.statusCode(400)
)
);
}
}
This is my maven with rest related dependencies
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-rest-assured</artifactId>
<version>2.0.34</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-screenplay-rest</artifactId>
<version>2.0.34</version>
<scope>test</scope>
</dependency>
This is my fork git hub repo: https://github.com/jarvcol/CarnivalTest/tree/addApiTesting
Any idea on this???
Because you are not using the matching serenity-core dependency. Try adding following dependency in pom.xml and update project:
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-core</artifactId>
<version>2.0.34</version>
</dependency>
Per my understanding RXJava works within a single JVM. Is there a wrapper/lib/api to support clustered environments with combination of distributed cache, JMS or any other queue to provide subscribers scale across distributed environments? Would like to check here before reinvent the wheel.
You can deploy Vertx instances in a cluster and use RxJava over it. The idea is use EventBus as a transport layer and subscribe to the messages using RxJava. It's not a pure RxJava solution.
A very simple runnable example:
package com.example;
import java.util.concurrent.TimeUnit;
import io.reactivex.Flowable;
import io.vertx.core.DeploymentOptions;
import io.vertx.core.VertxOptions;
import io.vertx.core.json.JsonObject;
import io.vertx.core.spi.cluster.ClusterManager;
import io.vertx.reactivex.core.AbstractVerticle;
import io.vertx.reactivex.core.Vertx;
import io.vertx.reactivex.core.eventbus.EventBus;
import io.vertx.spi.cluster.hazelcast.HazelcastClusterManager;
public class MainVerticle extends AbstractVerticle {
String nodeId;
static final String CENTRAL = "CENTRAL";
#Override
public void start() throws Exception {
EventBus eventBus = vertx.eventBus();
JsonObject config = config();
String nodeID = config.getString("nodeID");
eventBus.consumer(CENTRAL).toFlowable()
.map(msg -> (JsonObject) msg.body())
.filter(msgBody -> !msgBody.getString("sender", "").equals(nodeID))
.subscribe(msgBody -> {
System.out.println(msgBody);
});
Flowable.interval(1, TimeUnit.SECONDS)
.subscribe(tick -> {
JsonObject msg = new JsonObject()
.put("sender", nodeID)
.put("msg", "Hello world");
eventBus.publish(CENTRAL, msg);
});
}
public static void main(String[] args) {
ClusterManager clusterManager = new HazelcastClusterManager();
VertxOptions options = new VertxOptions().setClusterManager(clusterManager);
Vertx.rxClusteredVertx(options)
.doOnError(throwable -> throwable.printStackTrace())
.subscribe(vertx -> {
if (vertx.isClustered()) {
System.out.println("Vertx is running clustered");
}
String nodeID = clusterManager.getNodeID();
System.out.println("Node ID : " + nodeID);
String mainVerticle = MainVerticle.class.getCanonicalName();
DeploymentOptions deploymentOptions = new DeploymentOptions();
deploymentOptions.setConfig(new JsonObject().put("nodeID", nodeID));
vertx.rxDeployVerticle(mainVerticle, deploymentOptions).subscribe();
});
}
}
Maven dependencies:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>rxjava2-clustered</artifactId>
<version>0.42</version>
<packaging>jar</packaging>
<name>rxjava2-clustered</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-rx-java2</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-hazelcast</artifactId>
<version>3.5.0</version>
</dependency>
</dependencies>
</project>
In this example, I am using Hazelcast ClusterManager. There exists implementations for Infinispan, Apache Ignite and Apache Zookeeper. Refer to documentation for full reference:
I need to create a report capability(function) in an existing spring boot web application. The suggestion was to use BIRT, which I could integrate with the spring boot web app.
I found the article below and was able to run the reports in a spring boot starter project (using http://start.spring.io/). This article, fairly old, did help me to get to a working example. https://spring.io/blog/2012/01/30/spring-framework-birt. This article is basically exactly what I want, but in a spring boot web app.
The challenge I have is to run the report using the BIRT viewer, which comes with nice additional features. (Print, Expoet data, PDF, pagination etc.)
I cannot find any spring boot example using BIRT the way this article describes.
My questions are:
Is there an alternative or other way to do reports in a spring boot web application? (obviously not want to re-inventing the wheel by creating BIRT like capability from scratch or run the report engine separate from the web application if at all possible)
Does anyone today have BIRT working (with the viewer) in a spring boot web application and be willing to share or educate me on the best way to do this?
(I tried to get JSP page working with spring boot, but unable to so successfully...more a lack of experience than anything else)
Can someone help me please.
Kind Regards,
Henk
Dependencies, an instance of the IReportEngine, and directories are the most challenging part of setting up BIRT in a spring boot web application.
This example has a little bit of error handling and depends on environment variables for specifying paths to important directories.
One nice things about this setup is that it is a standalone ReST API for building and generating reports. As a side benefit it doesn't depend on JSP at all.
I run it in a Docker container.
Originally there was a plan to use some of the interfaces to try other reporting frameworks like Jasper reports or something else.
This isn't exactly what you are asking for though in my opinion it is better in certain contexts. You have a lot of flexibility with using, configuring, and deploying the BIRT report runner as an application like this. This does not use the pre-packaged BIRT Viewer provided by Actian.
I would make a data container for reports called birt-report-runner-data and then put all of the directories for logging and reports on that container which you can then mount onto your BIRT container.
Main Components
Main File (BirtReportRunnerApplication.java)
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class BirtReportRunnerApplication {
public static void main(String[] args) {
SpringApplication.run(BirtReportRunnerApplication.class, args);
}
}
Controller to receive report Requests (ReportController.java)
package com.example.core.web.controller;
import com.example.core.model.BIRTReport;
import com.example.core.service.ReportRunner;
import com.example.core.web.dto.ReportRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#RestController("ReportController")
#RequestMapping("/reports")
public class ReportController {
private Logger logger = LoggerFactory.getLogger(ReportController.class);
#Autowired
#Qualifier("birt")
ReportRunner reportRunner;
#RequestMapping(value = "/birt", method = RequestMethod.POST)
public ResponseEntity<byte[]> getBIRTReport(#RequestBody ReportRequest reportRequest) {
byte[] reportBytes;
ResponseEntity<byte[]> responseEntity;
try {
logger.info("REPORT REQUEST NAME: " + reportRequest.getReportName());
reportBytes =
new BIRTReport(
reportRequest.getReportName(),
reportRequest.getReportParameters(),
reportRunner)
.runReport().getReportContent().toByteArray();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.parseMediaType("application/pdf"));
String fileName = reportRequest.getReportName() + ".pdf";
httpHeaders.setContentDispositionFormData(fileName, fileName);
httpHeaders.setCacheControl("must-revalidate, post-check=0, pre-check=0");
responseEntity = new ResponseEntity<byte[]>(reportBytes, httpHeaders, HttpStatus.OK);
} catch (Exception e) {
responseEntity = new ResponseEntity<byte[]>(HttpStatus.NOT_IMPLEMENTED);
return responseEntity;
}
return responseEntity;
}
}
Report Request DTO for specifying report to run (ReportRequest.java)
package com.example.core.web.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
public class ReportRequest {
private String reportName;
private String reportParameters;
public ReportRequest(#JsonProperty("reportName") String reportName,
#JsonProperty("reportParameters") String reportParameters) {
this.reportName = reportName;
this.reportParameters = reportParameters;
}
public String getReportName() {
return reportName;
}
public String getReportParameters() {
return reportParameters;
}
public void setReportParameters(String reportParameters) {
this.reportParameters = reportParameters;
}
}
Interface for report runner (ReportRunner.java)
package com.example.core.service;
import com.example.core.model.Report;
import java.io.ByteArrayOutputStream;
public interface ReportRunner {
ByteArrayOutputStream runReport(Report report);
}
Largest class that does most of the work (BIRTReportRunner.java)
package com.example.core.service;
import com.example.core.model.Report;
import org.eclipse.birt.core.exception.BirtException;
import org.eclipse.birt.core.framework.Platform;
import org.eclipse.birt.report.engine.api.*;
import org.eclipse.core.internal.registry.RegistryProviderFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
#Service
#Qualifier("birt")
public class BIRTReportRunner implements ReportRunner {
private static final String DEFAULT_LOGGING_DIRECTORY = "defaultBirtLoggingDirectory/";
private Logger logger = LoggerFactory.getLogger(BIRTReportRunner.class);
private static String reportOutputDirectory;
private IReportEngine birtReportEngine = null;
#Autowired
private Environment env;
/**
* Starts up and configures the BIRT Report Engine
*/
#PostConstruct
public void startUp() {
if(env.getProperty("birt_report_input_dir") == null)
throw new RuntimeException("Cannot start application since birt report input directory was not specified.");
try {
String birtLoggingDirectory = env.getProperty("birt_logging_directory") == null ? DEFAULT_LOGGING_DIRECTORY : env.getProperty("birt_logging_directory");
Level birtLoggingLevel = env.getProperty("birt_logging_level") == null ? Level.SEVERE : Level.parse(env.getProperty("birt_logging_level"));
EngineConfig engineConfig = new EngineConfig();
logger.info("BIRT LOG DIRECTORY SET TO : {}", birtLoggingDirectory);
logger.info("BIRT LOGGING LEVEL SET TO {}", birtLoggingLevel);
engineConfig.setLogConfig(birtLoggingDirectory, birtLoggingLevel);
// Required due to a bug in BIRT that occurs in calling Startup after the Platform has already been started up
RegistryProviderFactory.releaseDefault();
Platform.startup(engineConfig);
IReportEngineFactory reportEngineFactory = (IReportEngineFactory) Platform.createFactoryObject(IReportEngineFactory.EXTENSION_REPORT_ENGINE_FACTORY);
birtReportEngine = reportEngineFactory.createReportEngine(engineConfig);
} catch (BirtException e) {
// TODO add logging aspect and find out how to log a platform startup problem from this catch block, if possible, using the aspect.
// Possibly rethrow the exception here and catch it in the aspect.
logger.error("Birt Startup Error: {}", e.getMessage());
}
reportOutputDirectory = env.getProperty("birt_temp_file_output_dir");
}
/**
* Shuts down the BIRT Report Engine
*/
#PreDestroy
public void shutdown() {
birtReportEngine.destroy();
RegistryProviderFactory.releaseDefault();
Platform.shutdown();
}
public File getReportFromFilesystem(String reportName) throws RuntimeException {
String reportDirectory = env.getProperty("birt_report_input_dir");
Path birtReport = Paths.get(reportDirectory + File.separator + reportName + ".rptdesign");
if(!Files.isReadable(birtReport))
throw new RuntimeException("Report " + reportName + " either did not exist or was not writable.");
return birtReport.toFile();
}
/**
* This method creates and executes the report task, the main responsibility
* of the entire Report Service.
* This method is key to enabling pagination for the BIRT report. The IRunTask run task
* is created and then used to generate an ".rptdocument" binary file.
* This binary file is then read by the separately created IRenderTask render
* task. The render task renders the binary document as a binary PDF output
* stream which is then returned from the method.
* <p>
*
* #param birtReport the report object created at the controller to hold the data of the report request.
* #return Returns a ByteArrayOutputStream of the PDF bytes generated by the
*/
#Override
public ByteArrayOutputStream runReport(Report birtReport) {
ByteArrayOutputStream byteArrayOutputStream;
File rptDesignFile;
// get the path to the report design file
try {
rptDesignFile = getReportFromFilesystem(birtReport.getName());
} catch (Exception e) {
logger.error("Error while loading rptdesign: {}.", e.getMessage());
throw new RuntimeException("Could not find report");
}
// process any additional parameters
Map<String, String> parsedParameters = parseParametersAsMap(birtReport.getParameters());
byteArrayOutputStream = new ByteArrayOutputStream();
try {
IReportRunnable reportDesign = birtReportEngine.openReportDesign(rptDesignFile.getPath());
IRunTask runTask = birtReportEngine.createRunTask(reportDesign);
if (parsedParameters.size() > 0) {
for (Map.Entry<String, String> entry : parsedParameters.entrySet()) {
runTask.setParameterValue(entry.getKey(), entry.getValue());
}
}
runTask.validateParameters();
String rptdocument = reportOutputDirectory + File.separator
+ "generated" + File.separator
+ birtReport.getName() + ".rptdocument";
runTask.run(rptdocument);
IReportDocument reportDocument = birtReportEngine.openReportDocument(rptdocument);
IRenderTask renderTask = birtReportEngine.createRenderTask(reportDocument);
PDFRenderOption pdfRenderOption = new PDFRenderOption();
pdfRenderOption.setOption(IPDFRenderOption.REPAGINATE_FOR_PDF, new Boolean(true));
pdfRenderOption.setOutputFormat("pdf");
pdfRenderOption.setOutputStream(byteArrayOutputStream);
renderTask.setRenderOption(pdfRenderOption);
renderTask.render();
renderTask.close();
} catch (EngineException e) {
logger.error("Error while running report task: {}.", e.getMessage());
// TODO add custom message to thrown exception
throw new RuntimeException();
}
return byteArrayOutputStream;
}
/**
* Takes a String of parameters started by '?', delimited by '&', and with
* keys and values split by '=' and returnes a Map of the keys and values
* in the String.
*
* #param reportParameters a String from a HTTP request URL
* #return a map of parameters with Key,Value entries as strings
*/
public Map<String, String> parseParametersAsMap(String reportParameters) {
Map<String, String> parsedParameters = new HashMap<String, String>();
String[] paramArray;
if (reportParameters.isEmpty()) {
throw new IllegalArgumentException("Report parameters cannot be empty");
} else if (!reportParameters.startsWith("?") && !reportParameters.contains("?")) {
throw new IllegalArgumentException("Report parameters must start with a question mark '?'!");
} else {
String noQuestionMark = reportParameters.substring(1, reportParameters.length());
paramArray = noQuestionMark.split("&");
for (String param : paramArray) {
String[] paramGroup = param.split("=");
if (paramGroup.length == 2) {
parsedParameters.put(paramGroup[0], paramGroup[1]);
} else {
parsedParameters.put(paramGroup[0], "");
}
}
}
return parsedParameters;
}
}
Report object class (Report.java)
package com.example.core.model;
import com.example.core.service.ReportRunner;
import java.io.ByteArrayOutputStream;
import java.util.List;
/**
* A Report object has a byte representation of the report output that can be
* used to write to any output stream. This class is designed around the concept
* of using ByteArrayOutputStreams to write PDFs to an output stream.
*
*
*/
public abstract class Report {
protected String name;
protected String parameters;
protected ByteArrayOutputStream reportContent;
protected ReportRunner reportRunner;
public Report(String name, String parameters, ReportRunner reportRunner) {
this.name = name;
this.parameters = parameters;
this.reportRunner = reportRunner;
}
/**
* This is the processing method for a Report. Once the report is ran it
* populates an internal field with a ByteArrayOutputStream of the
* report content generated during the run process.
* #return Returns itself with the report content output stream created.
*/
public abstract Report runReport();
public ByteArrayOutputStream getReportContent() {
return this.reportContent;
}
public String getName() {
return name;
}
public String getParameters() {
return parameters;
}
}
BIRTReport object class (BIRTReport.java)
package com.example.core.model;
import com.example.core.service.ReportRunner;
public class BIRTReport extends Report {
public BIRTReport(String name, String reportParameters, ReportRunner reportRunner) {
super(name, reportParameters, reportRunner);
}
#Override
public Report runReport() {
this.reportContent = reportRunner.runReport(this);
return this;
}
}
Build file (build.gradle)
buildscript {
ext {
springBootVersion = '1.3.2.RELEASE'
}
repositories {
jcenter()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
classpath("io.spring.gradle:dependency-management-plugin:0.5.2.RELEASE")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'spring-boot'
jar {
baseName = 'com.example.simple-birt-runner'
version = '1.0.0'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
ext {
springBootVersion = '1.3.0.M5'
}
repositories {
jcenter()
mavenCentral()
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-actuator:${springBootVersion}")
compile("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")
// BIRT Runtime and ReportEngine dependencies
// These were pulled from the Actuate download site at http://download.eclipse.org/birt/downloads/ for version 4.5.0
compile fileTree(dir: 'lib', include: '*.jar')
/*compile("org.eclipse.birt.runtime:org.eclipse.birt.runtime:4.4.2") {
exclude group: "org.eclipse.birt.runtime", module: "org.apache.poi"
exclude group: "org.eclipse.birt.runtime", module: "org.eclipse.orbit.mongodb"
}*/
compile("org.springframework.boot:spring-boot-starter-tomcat")
// include any runtime JDBC driver dependencies here
testCompile("org.springframework.boot:spring-boot-starter-test")
}
Dependencies
Get all of the dependencies in the Birt Viewer lib directory and put them on your classpath. Here is a full list of external dependencies:
com.ibm.icu_54.1.1.v201501272100.jar
com.lowagie.text_2.1.7.v201004222200.jar
derby.jar
flute.jar
javax.wsdl_1.5.1.v201012040544.jar
javax.xml.stream_1.0.1.v201004272200.jar
javax.xml_1.3.4.v201005080400.jar
net.sourceforge.lpg.lpgjavaruntime_1.1.0.v201004271650.jar
org.apache.batik.bridge_1.6.0.v201011041432.jar
org.apache.batik.css_1.6.0.v201011041432.jar
org.apache.batik.dom.svg_1.6.0.v201011041432.jar
org.apache.batik.dom_1.6.1.v201505192100.jar
org.apache.batik.ext.awt_1.6.0.v201011041432.jar
org.apache.batik.parser_1.6.0.v201011041432.jar
org.apache.batik.pdf_1.6.0.v201105071520.jar
org.apache.batik.svggen_1.6.0.v201011041432.jar
org.apache.batik.transcoder_1.6.0.v201011041432.jar
org.apache.batik.util.gui_1.6.0.v201011041432.jar
org.apache.batik.util_1.6.0.v201011041432.jar
org.apache.batik.xml_1.6.0.v201011041432.jar
org.apache.commons.codec_1.6.0.v201305230611.jar
org.apache.commons.logging_1.1.1.v201101211721.jar
org.apache.lucene.core_3.5.0.v20120725-1805.jar
org.apache.poi_3.9.0.v201405241750.jar
org.apache.xerces_2.9.0.v201101211617.jar
org.apache.xml.resolver_1.2.0.v201005080400.jar
org.apache.xml.serializer_2.7.1.v201005080400.jar
org.eclipse.birt.runtime_4.5.0.jar
org.eclipse.core.contenttype_3.5.0.v20150421-2214.jar
org.eclipse.core.expressions_3.5.0.v20150421-2214.jar
org.eclipse.core.filesystem_1.5.0.v20150421-0713.jar
org.eclipse.core.jobs_3.7.0.v20150330-2103.jar
org.eclipse.core.resources_3.10.0.v20150423-0755.jar
org.eclipse.core.runtime.compatibility_3.2.300.v20150423-0821.jar
org.eclipse.core.runtime_3.11.0.v20150405-1723.jar
org.eclipse.datatools.connectivity.apache.derby.dbdefinition_1.0.2.v201107221459.jar
org.eclipse.datatools.connectivity.apache.derby_1.0.103.v201212070447.jar
org.eclipse.datatools.connectivity.console.profile_1.0.10.v201109250955.jar
org.eclipse.datatools.connectivity.db.generic_1.0.1.v201107221459.jar
org.eclipse.datatools.connectivity.dbdefinition.genericJDBC_1.0.2.v201310181001.jar
org.eclipse.datatools.connectivity.oda.consumer_3.2.6.v201403131814.jar
org.eclipse.datatools.connectivity.oda.design_3.3.6.v201403131814.jar
org.eclipse.datatools.connectivity.oda.flatfile_3.1.8.v201403010906.jar
org.eclipse.datatools.connectivity.oda.profile_3.2.9.v201403131814.jar
org.eclipse.datatools.connectivity.oda_3.4.3.v201405301249.jar
org.eclipse.datatools.connectivity.sqm.core_1.2.8.v201401230755.jar
org.eclipse.datatools.connectivity_1.2.11.v201401230755.jar
org.eclipse.datatools.enablement.hsqldb.dbdefinition_1.0.0.v201107221502.jar
org.eclipse.datatools.enablement.hsqldb_1.0.0.v201107221502.jar
org.eclipse.datatools.enablement.ibm.db2.iseries.dbdefinition_1.0.3.v201107221502.jar
org.eclipse.datatools.enablement.ibm.db2.iseries_1.0.2.v201107221502.jar
org.eclipse.datatools.enablement.ibm.db2.luw.dbdefinition_1.0.7.v201405302027.jar
org.eclipse.datatools.enablement.ibm.db2.luw_1.0.3.v201401170830.jar
org.eclipse.datatools.enablement.ibm.db2.zseries.dbdefinition_1.0.4.v201107221502.jar
org.eclipse.datatools.enablement.ibm.db2.zseries_1.0.2.v201107221502.jar
org.eclipse.datatools.enablement.ibm.db2_1.0.0.v201401170830.jar
org.eclipse.datatools.enablement.ibm.informix.dbdefinition_1.0.4.v201107221502.jar
org.eclipse.datatools.enablement.ibm.informix_1.0.1.v201107221502.jar
org.eclipse.datatools.enablement.ibm_1.0.0.v201401170830.jar
org.eclipse.datatools.enablement.msft.sqlserver.dbdefinition_1.0.1.v201201240505.jar
org.eclipse.datatools.enablement.msft.sqlserver_1.0.3.v201308161009.jar
org.eclipse.datatools.enablement.mysql.dbdefinition_1.0.4.v201109022331.jar
org.eclipse.datatools.enablement.mysql_1.0.4.v201212120617.jar
org.eclipse.datatools.enablement.oda.ws_1.2.6.v201403131825.jar
org.eclipse.datatools.enablement.oda.xml_1.2.5.v201403131825.jar
org.eclipse.datatools.enablement.oracle.dbdefinition_1.0.103.v201206010214.jar
org.eclipse.datatools.enablement.oracle_1.0.0.v201107221506.jar
org.eclipse.datatools.enablement.postgresql.dbdefinition_1.0.2.v201110070445.jar
org.eclipse.datatools.enablement.postgresql_1.1.1.v201205252207.jar
org.eclipse.datatools.enablement.sap.maxdb.dbdefinition_1.0.0.v201107221507.jar
org.eclipse.datatools.enablement.sap.maxdb_1.0.0.v201107221507.jar
org.eclipse.datatools.modelbase.dbdefinition_1.0.2.v201107221519.jar
org.eclipse.datatools.modelbase.derby_1.0.0.v201107221519.jar
org.eclipse.datatools.modelbase.sql.query_1.1.4.v201212120619.jar
org.eclipse.datatools.modelbase.sql_1.0.6.v201208230744.jar
org.eclipse.datatools.sqltools.data.core_1.2.3.v201212120623.jar
org.eclipse.datatools.sqltools.parsers.sql.lexer_1.0.1.v201107221520.jar
org.eclipse.datatools.sqltools.parsers.sql.query_1.2.1.v201201250511.jar
org.eclipse.datatools.sqltools.parsers.sql_1.0.2.v201107221520.jar
org.eclipse.datatools.sqltools.result_1.1.6.v201402080246.jar
org.eclipse.emf.common_2.11.0.v20150512-0501.jar
org.eclipse.emf.ecore.change_2.11.0.v20150512-0501.jar
org.eclipse.emf.ecore.xmi_2.11.0.v20150512-0501.jar
org.eclipse.emf.ecore_2.11.0.v20150512-0501.jar
org.eclipse.equinox.app_1.3.300.v20150423-1356.jar
org.eclipse.equinox.common_3.7.0.v20150402-1709.jar
org.eclipse.equinox.preferences_3.5.300.v20150408-1437.jar
org.eclipse.equinox.registry_3.6.0.v20150318-1503.jar
org.eclipse.help_3.6.0.v20130326-1254.jar
org.eclipse.osgi.services_3.5.0.v20150519-2006.jar
org.eclipse.osgi_3.10.100.v20150529-1857.jar
org.eclipse.update.configurator_3.3.300.v20140518-1928.jar
org.mozilla.javascript_1.7.5.v201504281450.jar
org.w3c.css.sac_1.3.1.v200903091627.jar
org.w3c.dom.events_3.0.0.draft20060413_v201105210656.jar
org.w3c.dom.smil_1.0.1.v200903091627.jar
org.w3c.dom.svg_1.1.0.v201011041433.jar
Application properties (application.yml)
birt:
report:
output:
dir: ${birt_temp_file_output_dir}
input:
dir: ${birt_report_input_dir}
logging:
level: ${birt_logging_level}
server:
port: 8080
context-path: /simple-birt-runner
birt:
logging:
level: SEVERE
logging:
level:
org:
springframework:
web: ERROR
Dockerfile for running (Dockerfile)
FROM java:openjdk-8u66-jre
MAINTAINER Kent O. Johnson <kentoj#gmail.com>
COPY com.example.simple-birt-runner-*.jar /opt/soft/simple-birt-runner.jar
RUN mkdir -p /reports/input \
&& mkdir /reports/output \
&& mkdir -p /reports/log/engine
WORKDIR /opt/soft
CMD java -jar -Xms128M -Xmx4G simple-birt-runner.jar
EXPOSE 8080
Docker compose file for running image (docker-compose.yml)
simple-birt-runner:
image: soft/simple-birt-runner-release
ports:
- "8090:8080"
environment:
- birt_temp_file_output_dir=/reports/output
- birt_report_input_dir=/reports/input
- birt_logging_directory=/reports/log/engine
- birt_logging_level=SEVERE
volumes_from:
- birt-report-runner-data
Regarding #Kent Johnson 's answer. I didn't manage to configure the project with gradle, but I managed to build it with maven.
Below is the pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>birt-runner</groupId>
<artifactId>com.example.simple-birt-runner</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<!--<scope>provided</scope>-->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.eclipse.birt.runtime/org.eclipse.birt.runtime -->
<dependency>
<groupId>org.eclipse.birt.runtime</groupId>
<artifactId>org.eclipse.birt.runtime</artifactId>
<version>4.2.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>