ScroogeXHTML RTF converter 10.3 Release Candidate 1

Version 10.3.0-RC1 of the RTF to HTML5 and XHTML converter library ScroogeXHTML for the Java SE 8+ platform is available now.

About

ScroogeXHTML is a library which converts RTF to HTML5 as markup fragments (for embedding in other documents), or to stand-alone documents.

Key features:

  • Minimizes documents using CSS and default font definitions
  • Includes an API for post-processing of the intermediate DOM tree
  • Supports hyperlinks, bookmarks, multi-language and LTR/RTL text, field results and simple tables
  • Picture data extraction (hex and binary)
  • Complimentary code for converting and embedding images as Data URIs
  • Output formats: XHTML and HTML5
  • Small: less than 150 KB
  • Fast: up to several MB/second
  • Tested with Java SE 8, 11 and 17
  • Full source code available

New in 10.3.0-RC1:

  • Add workaround for missing cellXs
  • Major refactorings for table conversion support

Home page: https://www.scroogexhtml.com
Resources: https://www.scroogexhtml.com/#resources
Live conversions: https://www.scroogexhtml.com/#examples
Online demo: https://www.scroogexhtml.com/sxd
The online demo converts a RTF document to HTML5, and allows to set many conversion options.
Feel free to submit example RTF documents.

It is also available for Object Pascal (different feature set).

Trademarks

Oracle® and Java are registered trademarks of Oracle and/or its affiliates.

Advertisement

ScroogeXHTML RTF converter 10.2

Version 10.2 of the RTF to HTML5 and XHTML converter library ScroogeXHTML for the Java SE 8+ platform is available now.

About

ScroogeXHTML is a library which converts RTF to HTML5 as markup fragments (for embedding in other documents), or to stand-alone documents.

Key features:
* minimizes documents using CSS and default font definitions
* includes an API for post-processing of the intermediate DOM tree
* supports hyperlinks, bookmarks, multi-language and LTR/RTL text, field results and simple tables
* picture data extraction (hex and binary)
* complimentary code for converting and embedding images as Data URIs
* output formats: XHTML and HTML5
* small: less than 150 KB
* fast: up to several MB/second
* tested with Java SE 8, 11 and 17
* full source code available

New in version 10.2:
* new property TablePropConfiguration#MaxTableWidthPercent
* target Java SE 8 and newer
* bump slf4j from 2.0.6 to 2.0.7

Home page: https://www.scroogexhtml.com
Resources: https://www.scroogexhtml.com/#resources
Live conversions: https://www.scroogexhtml.com/#examples
Online demo: https://www.scroogexhtml.com/sxd
The online demo converts a RTF document to HTML5, and allows to set many conversion options.
Feel free to submit example RTF documents.

It is also available for Object Pascal (different feature set).

Trademarks

Oracle® and Java are registered trademarks of Oracle and/or its affiliates.

Discover ActiveMQ brokers with Indy 10.6 and IP multicast

This article shows how IP multicast can be used to discover Apache ActiveMQ message broker instances in the local network using Indy. Example output:

Screenshot 2021-08-14 164137

With the code below, an application can list all ActiveMQ brokers, see their status, and get the IP addresses, protocols, and port numbers of discoverable transport connectors. Transport connectors are discoverable, if their broker.xml configuration entry includes a discoveryUri attribute, for example discoveryUri=”multicast://default”.

Continue reading “Discover ActiveMQ brokers with Indy 10.6 and IP multicast”

Habari Client libraries release 2020.04

Habarisoft released the 2020.04 version of its Object Pascal STOMP client libraries for application integration with open source message brokers Apache ActiveMQ, Artemis, OpenMQ and RabbitMQ. The release includes

About Habari Client libraries

habari_logo_2016Habari Client libraries enable Object Pascal applications to take advantage of message broker / message queue technology – which is distributed, loosely coupled, reliable and asynchronous – to build integrated systems, using peer-to-peer and publish-subscribe communication models.

Message exchange example for Delphi and WildFly 16 application server

Red Hat yesterday released the WildFly 16 Final Java EE 8 application server. WildFly uses Apache ActiveMQ Artemis as the the default messaging provider. Artemis supports STOMP, which allows to connect from clients written in many programming languages.

If you need to exchange data between Delphi or Free Pascal programs and applications running on WildFly, you can follow these simple configuration steps and run a demonstration of bidirectional message exchange using the Habari Client for Artemis library from Habarisoft.

Read the full article here: https://habarisoft.wordpress.com/2019/02/28/connect-delphi-and-free-pascal-applications-with-wildfly-16/

Consuming Server-Sent Events (SSE) with Indy TIdHTTP and TIdEventStream

A new Indy HTTP client / JAX-RS server example is now available on GitHub. The server side generates Server-sent events. Server-sent events (SSE) is a technology enabling a browser to receive automatic updates from a server via HTTP connection.

The example code uses TIdHTTP and TIdEventStream to connect to the server, and writes the incoming events to the console window.

indy-sse-jaxrs

Requirements

  • Delphi or Lazarus IDE
  • Indy 10.6.2
  • Java IDE
  • Java EE 8 application server

Client

The SSE client setup in the TIndySSEClient.Create creates a TIdHTTP instance and a TIdEventStream instance.
As recommended, the Accept header is set to 'text/event-stream'. It also sets the Cache-Control header to 'no-store' to prevent proxy servers from caching the result.
Our event handler TIndySSEClient.MyOnWrite is assigned to the TIdEventStreamOnWrite property.

constructor TIndySSEClient.Create;
begin
  inherited Create;

  SSE_URL := URL;

  EventStream := TIdEventStream.Create;
  EventStream.OnWrite := MyOnWrite;

  IdHTTP := TIdHTTP.Create;
  IdHTTP.Request.Accept := 'text/event-stream';
  IdHTTP.Request.CacheControl := 'no-store';
end;

The OnWrite handler decodes the UTF-8 encoded event data and writes it to the console:

procedure TIndySSEClient.MyOnWrite;
begin
  WriteLn('Received ' + IntToStr(Length(ABuffer)) + ' bytes');
  WriteLn;
  WriteLn(IndyTextEncoding_UTF8.GetString(ABuffer));
  ...
end;

Server (main REST method)

The server generates the server-sent event and places a Stock instance in its data part:

  @GET
  @Path("prices")
  @Produces(MediaType.SERVER_SENT_EVENTS)
  public void getStockPrices(@Context SseEventSink sseEventSink) {
    int lastEventId = 1;

    while (running) {
      Stock stock = getNextTransaction();

      System.out.println("Send event ...");
      OutboundSseEvent sseEvent = this.eventBuilder
              .name("stock")
              .id(String.valueOf(lastEventId))
              .mediaType(MediaType.APPLICATION_JSON_TYPE)
              .data(Stock.class, stock)
              .reconnectDelay(3000)
              .comment("price change")
              .build();

      sseEventSink.send(sseEvent);
      lastEventId++;
    }
    sseEventSink.close();
  }

Additional notes

  • this is the first draft of a SSE client, which does not support features such as reconnect
  • the example assumes that every call of MyOnWrite contains exactly one event, teminated by a sequence of two line separators (cr lf / cr / lf) so that no extra code is required to do proper event stream parsing

Full source code

https://github.com/michaelJustin/indy-sse-jaxrs

References

Single file upload example using Indy TidHTTP and multipart/form-data

A new cross-language example for HTTP and REST is now available on GitHub.

Description

Single file upload example using Delphi client and Java server code and HTTP multipart/form-data

Requirements

  • Delphi 2009 or newer
  • Indy 10.6.2
  • Java JDK 8 or newer
  • Apache Maven
  • WildFly application server

Client

program IndyPostFormData;

{$APPTYPE CONSOLE}

uses
  IdHTTP, IdMultipartFormData, SysUtils;

const
  URL = 'http://localhost:8080/indy-post-formdata-1.0-SNAPSHOT/webresources/generic/pdf';

var
  Indy: TIdHTTP;
  Params: TIdMultiPartFormDataStream;
  Response: string;

begin
  Indy := TIdHTTP.Create;
  Params :=  TIdMultiPartFormDataStream.Create;
  try
    try
      Params.AddFile('file', 'client.pdf');
      Response := Indy.Post(URL, Params);
      WriteLn(Response);
    except
      on E:Exception do
        Writeln(E.Classname, ': ', E.Message);
    end;
  finally
    Params.Free;
    Indy.Free;
  end;
  ReadLn;
end.

 

Server (main REST method)

  @POST
  @Path("/pdf")
  @Consumes({MediaType.MULTIPART_FORM_DATA})
  public Response upload(MultipartFormDataInput input) {
    String UPLOAD_PATH = "c:/tmp/";
    try {
      InputStream fileInputStream = input.getFormDataPart("file", InputStream.class, null);
      String fileName = "test.pdf";

      int read;
      byte[] bytes = new byte[1024];

      try (OutputStream out = new FileOutputStream(new File(UPLOAD_PATH + fileName))) {
        while ((read = fileInputStream.read(bytes)) != -1) {
          out.write(bytes, 0, read);
        }
      }
    } catch (IOException e) {
      throw new WebApplicationException("Error while uploading file. Please try again");
    }
    return Response.ok("Data uploaded successfully").build();
  }

 

Full source code

https://github.com/michaelJustin/indy-post-formdata

JAX-WS SOAP server testing with Delphi and Thorntail

Update: this project is now available on Github

In the previous blog post, I have shown Delphi code examples using Indy TIdHTTP as a client for a basic REST server written using the JAX-RS API, running in Thorntail.

This article shows how a JAX-WS SOAP WebService can be exposed using Thorntail and consumed with Delphi.

For brevity, we will adopt a code-first approach, and use JAX-WS annotations to expose our service under SOAP format.

JAX-WS WebService – the Java EE server

package com.example;

import javax.jws.WebService;
import javax.jws.WebMethod;

@WebService(
   portName="helloPort",
   serviceName="helloService",
   targetNamespace = "http://hello.example.com/")
public class Hello {
    private final String message = "Hello, ";

    @WebMethod
    public String sayHello(String name) {
        return message + name + ".";
    }
}

That’s the standard JAX-WS web service code for a simple SOAP endpoint which has one method, sayHello, that takes a String argument and returns a String response.

After building the server with Maven, we launch it from the command line:

>java -jar target/soap-endpoint-thorntail.jar

The server will log the startup and deployment of our web service application:

2019-01-28 09:24:42,517 INFO  [org.jboss.as.server] (main) WFLYSRV0010: Deployed
 "soap-endpoint.war" (runtime-name : "soap-endpoint.war")

To check that the web service is working, we can invoke its WSDL at the endpoint address:

http://localhost:8080/helloService?WSDL

Alternatively, we can use SoapUI to run a first test request:

soapui

JAX-WS WebService – the Delphi client

First we create a new command line application.

Then we use the WSDL importer to generate a Delphi binding. This will create a unit ”helloService”, and add it to the project.

importer

Now wee need only to insert a call to the web service in the Delphi code:

program helloWorldClient;

{$APPTYPE CONSOLE}

uses
  helloService in 'helloService.pas',
  SysUtils, ActiveX;

var
  HelloServer: Hello;
  Response: string;

begin
  ActiveX.CoInitialize(nil);
  try
     HelloServer := GetHello;
     Response := HelloServer.sayHello('Delphi');
     WriteLn(Response);
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;

  ReadLn;
  CoUninitialize;
end.

Note for older Delphi versions: if we run the Delphi code unmodified, our client will show an exception:

ERemotableException: Unmarshalling Error: unerwartetes Element (URI:"http://hell
o.example.com/", lokal:"arg0"). Erwartete Elemente sind

The fix on the Delphi side is shown below. The last parameter of RegisterInvokeOptions must be changed from ioDocument to ioHasNamespace.

initialization
  InvRegistry.RegisterInterface(TypeInfo(Hello), 'http://hello.example.com/', '');
  InvRegistry.RegisterDefaultSOAPAction(TypeInfo(Hello), '');
  InvRegistry.RegisterInvokeOptions(TypeInfo(Hello), ioHasNamespace);

And with this change, the client receives the expected response:

Hello, Delphi.

 

Conclusion

Thorntail, which is designed for usage in microservice architectures, saves much of the time which is usually needed for creating, deploying, and testing Java EE components (such as JAX-RS and JAX-WS web services). Building a Java EE compliant web service server with Thorntail requires only a basic Java IDE. The resulting server is packaged as a single jar file and can be run instantly from the command line, or can be packaged as a war file ready for deployment in any Java EE compatible application server.

References

Unmarshalling Error

Example code based on Thorntail docs https://docs.thorntail.io/2.3.0.Final/#_basics

JAX-WS Java API for XML Web Services

JAX-RS REST service testing with Indy TidHTTP and Thorntail

Update: this project is now available on Github

Thorntail (https://thorntail.io/) is an open source project which creates stand-alone Java EE application servers that can be launched with a simple java -jar command.

Thorntail can be a very handy tool for quick prototyping and integration testing of Delphi web service client code, as it requires no installation of a heavy-weight Java EE application server enviroment.

Example 1: HTTP GET Test

JAX-RS web service code / TIdHTTP client code


package com.example.rest;

import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import javax.ws.rs.GET;
import javax.ws.rs.Produces;

@Path("/hello")
public class HelloWorldEndpoint {

  @GET
  @Produces("text/plain")
  public Response doGet() {
    return Response.ok("Hello from Thorntail!").build();
  }
}

That’s the standard JAX-RS web service code for a simple endpoint which processes GET requests. The boilerplate code which defines the root context is:

package com.example.rest;

import javax.ws.rs.core.Application;
import javax.ws.rs.ApplicationPath;

@ApplicationPath("/rest")
public class RestApplication extends Application {
}

The web service client code, written with Delphi and the Indy TIdHTTP client class:

program IndyClient;

{$APPTYPE CONSOLE}

uses
  IdHTTP,
  SysUtils;

procedure Main;
var
  IdHTTP: TIdHTTP;
  Response: string;
begin
  IdHTTP := TIdHTTP.Create;
  try
    Response := IdHTTP.Get('http://localhost:8080/rest/hello');
    WriteLn(Response);
  finally
    IdHTTP.Free;
  end;
end;

begin
  try
    Main;
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
  ReadLn;
end.

After building the server with Maven, we launch it from the command line:

>java -jar target/restful-endpoint-thorntail.jar

The server will log the startup and deployment of our web service application:

2019-01-23 15:45:58,037 INFO [org.jboss.as.server] (main) WFLYSRV0010: Deployed
"restful-endpoint.war" (runtime-name : "restful-endpoint.war")

And we are ready to test our Delphi client.
Typically for GET requests, we can also launch a web browser and navigate to https://localhost:8080/rest/hello to test it.
In the next example, we move one step further and add a POST handler to the web service, and change the Delphi code to send a HTTP POST request with an JSON payload.

Example 2: HTTP POST Test

JAX-RS web service code / TIdHTTP client code


package com.example.rest;

import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Produces;

@Path("/hello")
public class HelloWorldEndpoint {

  @GET
  @Produces("text/plain")
  public Response doGet() {
    return Response.ok("Hello from Thorntail!").build();
  }

  @POST
  @Produces("text/plain")
  public Response doPost() {
    return Response.ok("Hello from Thorntail POST!").build();
  }
}

And the corresponding Delphi test code is:

program IndyClient;

{$APPTYPE CONSOLE}

uses
  IdHTTP,
  SysUtils, Classes;

procedure Main;
var
  IdHTTP: TIdHTTP;
  RequestBody: TStream;
  Response: string;
begin
  IdHTTP := TIdHTTP.Create;
  RequestBody := TStringStream.Create('{"answer":42}',
    TEncoding.UTF8);
  try
    Response := IdHTTP.Post('http://localhost:8080/rest/hello',
      RequestBody);
    WriteLn(Response);
  finally
    RequestBody.Free;
    IdHTTP.Free;
  end;
end;

begin
  try
    Main;
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
  ReadLn;
end.

Example 3: HTTP POST with media type application/json

In this example, we remove the GET handler to the server code (so that only HTTP POST requests are allowed now), and add a restriction that the POST request must use the media type “application/json”:

JAX-RS web service code / TIdHTTP client code

@Path("/hello")
public class HelloWorldEndpoint {

  @POST
  @Produces("text/plain")
  @Consumes("application/json")
  public Response doPost(String body) {
    return Response.ok("Hello from Thorntail POST!\n" + body).build();
  }
}

The server code now also prints the body of the POST request so we can verify it has been received.

If we run the Delphi code unmodified, the server will refuse the POST request (because it is lacking the mediatype) and our client will show an exception:

EIdHTTPProtocolException: HTTP/1.1 415 Unsupported Media Type

The fix on the Delphi side is shown below, in line 6, the content type header is added to the request.

  IdHTTP := TIdHTTP.Create;
  try
    RequestBody := TStringStream.Create('{"answer":42}',
      TEncoding.UTF8);
    try
      IdHTTP.Request.ContentType := 'application/json';
      Response := IdHTTP.Post('http://localhost:8080/rest/hello',
        RequestBody);
      WriteLn(Response);
    finally
      RequestBody.Free;
    end;
  finally
    IdHTTP.Free;
  end;

And with this change, the client receives the expected response:

Hello from Thorntail POST!
{"answer":42}

Example 4: HTTP POST with media type and response type application/json

In this example, the Delphi client sends another request header. Now the media type “application/json” is not only specified for the request content, but also required for the server response, by setting the HTTP Accept header (in line 7).

TIdHTTP client code

  IdHTTP := TIdHTTP.Create;
  try
    RequestBody := TStringStream.Create('{"answer":42}',
      TEncoding.UTF8);
    try
      IdHTTP.Request.ContentType := 'application/json';
      IdHTTP.Request.Accept := 'application/json';
      Response := IdHTTP.Post('http://localhost:8080/rest/hello',
        RequestBody);
      WriteLn(Response);
    finally
      RequestBody.Free;
    end;
  finally
    IdHTTP.Free;
  end;

If we run this client code with the same server as in the previous example, there will be a new exception with HTTP code 406:

EIdHTTPProtocolException: HTTP/1.1 406 Not Acceptable

Since our Delphi client used the Accept header to ask for a response with media type “application/json”, the server rejects the request because the POST handler responds with media type “text/plain”, which does not match the client request.

So we update the server to match the change on the client side:

JAX-RS web service code / TIdHTTP client code

@Path("/hello")
public class HelloWorldEndpoint {

  @POST
  @Produces("application/json")
  @Consumes("application/json")
  public Response doPost(String body) {
    return Response.ok(body).build();
  }

}

Note that the server code also only returns the request body (which must be JSON).

Now the client and server code use matching media types both for request and for the accepted response, and the client shows the expected server response:

{"answer":42}

Conclusion

Thorntail, which is designed for usage in microservice architectures, saves much of the time which is usually needed for creating, deploying, and testing Java EE components (such as JAX-RS web services). Building a Java EE compliant web service server with Thorntail requires only a basic Java IDE. The resulting server is packaged as a single jar file and can be run instantly from the command line, or can be packaged as a war file ready for deployment in any Java EE compatible application server.

References

Example code based on Thorntail docs https://docs.thorntail.io/2.3.0.Final/#_basics

JAX-RS https://en.wikipedia.org/wiki/Java_API_for_RESTful_Web_Services

Related articles

Indy 10.6 HTTPS POST example with JSON body

First milestone of ScroogeXHTML for the Java™ platform 8.0

Version 8.0.MS1 of the RTF (Rich Text) to HTML5 and XHTML converter library ScroogeXHTML for the Java™ platform is now available for registered users.

New features in 8.0.MS1

  • compiles with Oracle JDK 8 and Oracle OpenJDK 11
  • JUnit tests pass with Oracle JDK 8 and Oracle OpenJDK 11

Other changes in 8.0.MS1:

  • moved package to com.scroogexhtml
  • fixed Spotbugs warnings and JavaDoc errors
  • removed deprecated methods getISO8601DateTime, getStyleSheetLink, setCompatibleDefaults, useListTable, metaDate

Download link

https://www.habarisoft.com/scroogexhtml_j/download/snapshot/ScroogeXHTML-jar-8.0.MS1.install.jar (only for registered users)

API documentation

https://www.habarisoft.com/scroogexhtml_j/8.0.MS1/docs/api/index.html (note: JavaDoc generated with JDK 11 includes a search feature and no HTML frames anymore)

About

ScroogeXHTML for the Java platform is a library which can convert a subset of the Rich Text Format (RTF) standard to HTML5 and XHTML, as a standalone document, or as a fragment which can be used in other documents. It supports RTF features such as hyperlinks, field results, simple tables, picture data extraction, and embeds PNG and JPG pictures as data URI. It is easy to use, and requires no external runtime libraries except the SLF4J logging facade.

Other platforms

ScroogeXHTML is also available for Delphi 2009+ and Free Pascal.

scrooge_portrait_logo_2016