Posts Tagged: ‘Java’

Migrating OSGi Plugins to standalone applications

13. Januar 2020 Posted by Sven Hasselbach

I have created an example project for migrating an OSGI-based Spring Boot application into an standalone application:

https://github.com/hasselbach/domino-springboot/blob/maven/domino-springboot.plugin

The application runs in the JVM of the Notes Client, only a user id is required, not a complete server installation (and ID). The benefit is that you can run as much JVM instances you want (which allows a better resource management and deployment) and it is easier to integrate the applications into existing CD environments. Also, Non-Domino developers can use their preferred IDE and are able to use existing / standard knowledge when developing Java applications.

When using the Java NAPI, you need to remove the spring-boot-devtools dependency because of problems with the class loader and DLLs.

Schulungsangebot – Spring Boot & Domino: REST APIs

14. Februar 2019 Posted by Sven Hasselbach

Kurs „Spring Boot & Domino: REST APIs“

Für große Anwendungen ist eine REST Schnittstelle heutzutage unverzichtbar. Doch mit den Bordmitteln sind schnell Grenzen erreicht und auch die Einbeziehung von Entwicklern ohne Domino-Hintergrundwissen gestaltet sich schwierig

Spring Boot ist der Defacto-Standard für Unternehmensanwendungen im Java-Umfeld und eine robuste wie auch flexible Alternative zu existierenden Frameworks des Domino-Umfelds.

Der Kurs vermittelt die nötigen Kenntnisse, um eine Spring Boot-basierte REST API zu entwickeln und diese als OSGi Plugin zu deployen. Ergänzt wird der Kurs durch langjährige Praxiserfahrung im täglichen Einsatz. Themen wie Token-Authentifizierung, Caching-Strategien für Hochperformante Anwendungen, Multi-Threading-Support, automatisierte Dokumentation dank Swagger uvw. werden im Kurs ausführlich behandelt.

Als Startpunkt für eigene Entwicklungen ist der verwendete Source Code Lizenzfrei enthalten.

Dauer: 3 Tage / Preis 3.500 € zzgl. MwSt. (Unabhängig von der Teilnehmerzahl)

Zielgruppe: Anwendungsentwickler Lotus Notes / Domino. Webentwicklungs- und Java- kenntnisse vorausgesetzt.

Bei Interesse oder Fragen bitte eine Mail an mich (freelancer AT hasselba.ch) oder das Kontaktformular nutzen.

Exchange API for Java: Allow *all* type of certificates

29. Januar 2019 Posted by Sven Hasselbach

I had troubles accessing internal Exchange servers using the EWS Java API because of self-signed SSL certificates (and not matching host names), that’s why I created a patch which overrides the existing certificate check and allows all type of SSL certificates.

The code can be found here: https://github.com/hasselbach/ews-java-api

Be aware that this is lowers the security, so you should know what you are doing before using it in productive environments!

Spring Boot Admin: Einfach genial und genial einfach

14. Oktober 2018 Posted by Stephan Kopp

Spring Boot Applikationen eignen sich optimal, um eine Microservice Applikation bestehend aus vielen kleinen Applikationen zu erstellen. Doch wie überwacht und verwaltet man diese Sammlung aus einzelnen Programmen?

Das Spring Boot Admin Projekt ist hierfür eine geniale und vor allem kostenlose Ergänzung.

Spring_Boot_Admin-3.png

Spring Boot Admin Server

Als Admin Server erstellt man sich eine eigene Spring Boot Applikation, fügt die Maven Dependency und die Annotation @EnableAdminServer hinzu und das wars schon.

pom.xml

<dependency>
   <groupId>de.codecentric</groupId>
   <artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>

Spring Boot Applikation

@EnableAdminServer
@SpringBootApplication
public class AdminServerApplication {

   public static void main(String[] args) {
      SpringApplication.run(AdminServerApplication.class, args);
   }
}

An diesem Server können sich jetzt alle Spring Boot Applikationen anmelden. Ein Beispiel findet man hier.


Nun zur Client Applikation, die sich am Server anmeldet

Dazu muss man lediglich das Client Paket in die Dependencies aufnehmen und zwei Parameter setzen. Das wäre die minimale Variante, aber natürlich noch längst nicht alles. Um wirklich alle Features nutzen zu können, müssen doch noch ein paar Dinge gemacht werden. Zunächst aber zu den Basics.

pom.xml

<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-client</artifactId>
    <version>2.0.3</version>
</dependency>

application.properties

spring.boot.admin.client.url=http://localhost:9090

Actuator

Dieses Paket integriert eine Menge nützlicher APIs in die eigene Applikation, welche vom Spring Boot Admin Server verwendet werden um die eigene Applikation zu überwachen und auch zu steuern.

pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

application.properties

management.endpoints.web.exposure.include=*
logging.file=log/logfile.txt
logging.pattern.file=%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(${PID}){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wEx

Der Actuator funktioniert auch ohne Konfiguration, aber mit diesen Einstellungen passiert zusätzlich zu den Standards noch folgendes:

  • Es werden alle Endpoints aktiviert, nicht nur health und up
  • Es wird ein Logfile geschrieben und farblich formatiert, dadurch wird auch automatisch der Endpoint aktiviert um auf dieses Logfile zuzugreifen

Security

Security sollte von Beginn an immer aktiviert sein. Damit benötigt natürlich auch der Admin Server Zugang zu unserer Applikation, bzw. nur zur /actuator API. Das kann man z.B. durch einen eigenen zentralen User machen, oder auch mit selbst generierten Accounts pro Applikation. Diese Zugangsdaten müssen dem Admin Server bei der Anmeldung mitgeteilt werden.

pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

application.properties

spring.security.user.name=user
spring.security.user.password=password

spring.boot.admin.client.instance.metadata.user.name=${spring.security.user.name}
spring.boot.admin.client.instance.metadata.user.password=${spring.security.user.password}

Das wars schon

Dieses Basis Setup genügt, um ein zentrales (wirklich schönes) Webinterface zu haben, über das ich alle notwendigen Informationen meiner Applikationen abrufen kann. Es geht dabei nicht nur um den reinen Status up/down. Ich kann direkt auf die Logs der Services zugreifen oder mir die eingehende http Requests anschauen und noch sehr viel mehr.

Spring_Boot_Admin-7.png

Spring_Boot_Admin-4

Spring_Boot_Admin-5

Ein nettes Feature ist z.B. auch die Veränderung meiner Log Einstellungen ohne die Services neu starten zu müssen.

Spring_Boot_Admin-6

Jeder, der sich mit Spring Boot Applikationen beschäftigt, sollte sich das Spring Boot Admin Projekt mal genauer anschauen. Unter anderem deshalb liebe ich OpenSource.

Meinen Beispiel Code kann man sich hier anschauen.

Dropping Domino’s HTTP task (3): WebSSO Integration (Part 1)

30. Juli 2018 Posted by Sven Hasselbach

To integrate the new HTTP stack into the existing environment, we can use LTPA tokens. These tokens are cookies which store the authentication information and allow to share them betweeen different participating Domino servers. A users must log on only once, and existing applications and data/views can be accessed without a relogin.

Validating an existing LTPA token with Spring can be done with our own PreAuthentificationFilter which checks for an existing LTPA token and extracts the authentication details from the cookie and creates a new Principal instance.


import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;

public class LtpaPreAuthenticatedFilter extends AbstractPreAuthenticatedProcessingFilter {

   @Value("${ltpa.secret}")
   private String ltpaSecret;

   @Value("${ltpa.cookieName}")
   private String ltpaCookieName;

   @Override
   protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {

      Cookie[] cookies = request.getCookies();
      if( cookies == null ) {
         return null;
      }

      for( int i= 0; i<cookies.length ; i++ ){
         String name = cookies[i].getName();
         String value = cookies[i].getValue();

         if( ltpaCookieName.equalsIgnoreCase(name) ){
            DominoLtpaToken ltpaToken = new DominoLtpaToken( value, ltpaSecret );

            if( ltpaToken.isValid() ){
               return ltpaToken.getDistinguishedName();
            }
         }
      }

      return null;
   }

   @Override
   protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
      // is required to return an empty string
      return "";
   }

}

The filter implements two methods, one for extraction of the principal, and the other for the credentials (which we don’t have with LTPA tokens). In the getPreAuthenticatedPrincipal method, existinig LTPA tokens are searched, then the user extracted and the token validated.

The secret of the LTPA token and the name are stored in application.properties:

The second part is implementing a AuthenticationUserDetailsService. This service is for getting additional details for the authenticated user, for example the ACL roles or groups the user belongs to.

import java.util.Collection;
import java.util.HashSet;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;

public class LtpaUserDetailsService implements AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> {

   @Override
   public UserDetails loadUserDetails(PreAuthenticatedAuthenticationToken token)
      throws UsernameNotFoundException {

      String userName=(String)token.getPrincipal();

      Collection<GrantedAuthority> authorities = new HashSet<GrantedAuthority>() ;
      authorities.add(new LtpaUserAuthority());

      User user = new User(userName,"",authorities);

      return user;
    }

}

In our case, we are just adding an LtpaUserAuthority to the user information. Don’t worry about the usage of the LtpaUserAuthority. We come back to this in another post.

import org.springframework.security.core.GrantedAuthority;

public class LtpaUserAuthority implements GrantedAuthority {

   private static final long serialVersionUID = 1L;

   @Override
   public String getAuthority() {
      return "ROLE_USER_LTPA";
   }

}

In the last step we have to update the SecurityConfig.java to activate the filter:

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

   @Configuration
   @Order(1)
   static class DominoLtpaSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

      @Bean
      public AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService() {
         return new LtpaUserDetailsService();
      }

      @Bean
      public PreAuthenticatedAuthenticationProvider preAuthenticatedAuthenticationProvider() {
         PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();

         provider.setPreAuthenticatedUserDetailsService(authenticationUserDetailsService());
         provider.setUserDetailsChecker(new AccountStatusUserDetailsChecker());

         return provider;
      }

      @Override
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
         auth.authenticationProvider(preAuthenticatedAuthenticationProvider());
      }

      @Bean
      public AbstractPreAuthenticatedProcessingFilter preAuthenticatedProcessingFilter() throws Exception {
         LtpaPreAuthenticatedFilter filter = new LtpaPreAuthenticatedFilter();
         filter.setAuthenticationManager(authenticationManager());
         return filter;
      }

      @Override
      protected void configure(HttpSecurity http) throws Exception {
         http.addFilter(preAuthenticatedProcessingFilter())
         .authorizeRequests()
         .antMatchers("/**").permitAll() ;
      }
   }
  ...
}

This includes the filter in any request. Now, the Principal contains the user name stored in the LTPA token.

Dropping Domino’s HTTP task (2): Running in User Context

17. Juli 2018 Posted by Sven Hasselbach

To use the approach as an alternative to Domino’s HTTP task, we need support for the different user contexts, because using NotesFactory.createSession() just creates a session for the current Notes ID used.

This goal can be achived by using the Java NAPI and the following method:

import com.ibm.domino.napi.NException;
import com.ibm.domino.napi.c.NotesUtil;
import com.ibm.domino.napi.c.xsp.XSPNative; 

/**
* create a new Domino session for the give username
* @param userName
* String containing the canonical username
* @return
* lotus.domino.Session for the given username
* @throws NException
* @throws ServletException
*/
public static Session createUserSession(final String userName)
{
   Session session = null;
   try {
      long hList = NotesUtil.createUserNameList(userName);
      session = XSPNative.createXPageSession(userName, hList,
         false, false);

      return session;
   } catch (Exception e) {
      e.printStackTrace();
   }

   return session;
}

It is required to load the required njnotes.dll before this method can be used, otherwise the C-API references won’t work. This must be done after initiation of the NotesThread and (only once) per thread:

System.loadLibrary("njnotes");

It is also required to use the full hierarchical name for the session creation, otherwise readers / authors won’t work correctly (you can create a session for every user you want, even for not existing ones).

To get this work correctly, I have created a little helper class which memorizes if the Notes-relevant part was already initialized for the thread used:

public class Utils {

   private static final ThreadLocal<Boolean> isNotesInitialized = new ThreadLocal<Boolean>();

   public static void initNotes(){

      // check if the Thread is already initialized
      if( Boolean.TRUE.equals( isNotesInitialized.get() ) )
         return;

      // init Notes
      NotesThread.sinitThread();
      System.loadLibrary("njnotes");

      // mark as initialized
      isNotesInitialized.set( Boolean.TRUE );

   }

}

Now let’s activate Spring Boot Security on the new server by adding the required dependencies to the pom.xml:

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-security</artifactId>
</dependency>

To use the authentication with the existing Domino environment, we can use our own AuthenticationProvider:

import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component;

import org.springframework.security.authentication.BadCredentialsException;

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {

   @Override
   public Authentication authenticate(Authentication authentication) throws AuthenticationException {
      String name = authentication.getName();
      String password = authentication.getCredentials().toString();

      String realUser = validatePassword( name, password );

      if( Utils.isEmptyString( realUser ) ){
         throw new BadCredentialsException("Authentication failed for user = " + name);
      }

      return auth;
   }

   @Override
   public boolean supports(Class<?> authentication) {
      return authentication.equals(UsernamePasswordAuthenticationToken.class);
   }

   public String validatePassword( final String userName, final String password){
      // see https://github.com/hasselbach/domino-stateless-token-servlet/blob/master/src/ch/hasselba/servlet/DominoStatelessTokenServlet.java
      // and convert the username to the hierarchical one
   }
}

If the authentication was successfull, we can access the correct username from the Principal object. Just add it as a parameter to the controller method, and you get the hierarchical name.

@MessageMapping("/hello")
@SendTo("/topic/greetings")
public Greeting greeting(HelloMessage message, Principal principal) throws Exception {

   Thread.sleep(1000); // simulated delay

   // init Notes
   Utils.initNotes();

   // create the session for the user
   Session session = (Session) Utils.createUserSession(principal.getName());

   return new Greeting("Hello, " + HtmlUtils.htmlEscape( session.getEffectiveUserName() ) + "!" );
}

Now let’s see this in action:

P.S.
I have ignored recycling and termination of the Notes threads for an easier understanding.

Dropping Domino’s HTTP task

15. Juli 2018 Posted by Sven Hasselbach

Instead of waiting for updates of the Domino HTTP task any longer I was thinking about how to use modern HTTP technologies on top of Domino. But instead of implementing it in the Domino stack, I think I found a new way for developing and running my Spring Boot applications: Why not using the existing JVM, and run my application directly on it? This means full access to the Domino objects, and allows access to the latest available technologies: No more limitations because of the provided tech stack, Websockets, Async HTTP Request Processing, full JEE support, modern and better development tools, …

I am not talking about DIIOP or RPC, that’s something different, and more a crutch as a solution. I need full access, especially to NAPI for C-API calls for running the code in the user context I want.

First thing to do is downloading and installing Maven and Git. I am using older versions on my Winows 7 VM, because I am to lazy to upgrade them. Then I have cloned the Websockets example from Spring as a starting point for a quick testing scenario.

I have choosen Jetty as the webserver to use by adding the dependencies to the pom.xml:

<dependency>
   <groupId>org.eclipse.jetty.websocket</groupId>
   <artifactId>websocket-client</artifactId>
   <version>9.4.11.v20180605</version>
</dependency>

<dependency>
   <groupId>org.eclipse.jetty.websocket</groupId>
   <artifactId>websocket-server</artifactId>
   <version>9.4.11.v20180605</version>
</dependency>

<dependency>
   <groupId>org.eclipse.jetty</groupId>
   <artifactId>jetty-client</artifactId>
   <version>9.4.11.v20180605</version>
</dependency>

Jetty provides support for WebSockets, http/2, the latest servlet container and many more features which on the wishlist of Domino developers for years.

For an easier maintainment, I am using properties in the pom.xml for referencing the Domino environment:

<properties>
   <java.version>1.8</java.version>
   <domino.directory>T:/IBM/Domino901</domino.directory>
   <domino.osgi.shared.directory>${domino.directory}/osgi/shared/eclipse/plugins</domino.osgi.shared.directory>
   <domino.osgi.rcp.directory>${domino.directory}/osgi/rcp/eclipse/plugins</domino.osgi.rcp.directory>
   <domino.version>9.0.1</domino.version>
   <domino.jarversion>9.0.1.20180115-1058</domino.jarversion>
</properties>

My installation of Domino server is on drive T, and the JAR version is the part of the file or folder name which depends on the current installation / feature pack.

Now, we can add the dependencies to the required Domino JARs:


<dependency>
   <groupId>com.ibm</groupId>
   <artifactId>domino-api-binaries</artifactId>
   <version>${domino.version}</version>
   <scope>system</scope>
   <systemPath>${domino.directory}/jvm/lib/ext/Notes.jar</systemPath>
</dependency>

<dependency>
   <groupId>com.ibm</groupId>
   <artifactId>commons</artifactId>
   <version>${domino.version}</version>
   <scope>system</scope>
   <systemPath>${domino.osgi.shared.directory}/com.ibm.commons_${domino.jarversion}/lwpd.commons.jar</systemPath>
</dependency>

<dependency>
   <groupId>com.ibm.domino</groupId>
   <artifactId>napi</artifactId>
   <version>${domino.version}</version>
   <scope>system</scope>
   <systemPath>${domino.osgi.shared.directory}/com.ibm.domino.napi_${domino.jarversion}/lwpd.domino.napi.jar</systemPath>
</dependency>

At this point it is time to start the project for the first time. To do this, it is required to use the Domino JVM instead of the installed one. And here comes a small drawback: When using the Server JVM you cannot run the Domino server in parallel. If so, the server will crash immediatly with a „PANIC!“ message. You could start every task manually – but not the database server itself (this means you can use replication and mail and everything, but you cannot connect to the server from another client).

So here is a workaround: Install the Notes client and use his JVM! Think as it of a massive Database driver to connect to the server (there is no need to start the client, we just need the JVM and the DLLs).

Start a console, and change the environment (dependent of your installation of Maven & the Notes client):

SET JAVA_HOME=T:\IBM\Notes901\jvm
SET Path=T:\IBM\Notes901;C:\apache-maven-3.3.9\bin;T:\IBM\Notes901\jvm\bin

Go to your project folder and start the server:

mvn spring-boot:run

After opening http://localhost:8080 in your browser and clicking the „Connect“ button, you can see the WebSocket connection in the Dev Console:

At this point, nothing really spectacular. But now let’s modify the GreetingController.java and add some Domino code:

@MessageMapping("/hello")
@SendTo("/topic/greetings")
public Greeting greeting(HelloMessage message) throws Exception {

   Thread.sleep(1000); // simulated delay

   // make to Domino Thread
   NotesThread.sinitThread();
   Session session = NotesFactory.createSession();

   return new Greeting("Hello, " + HtmlUtils.htmlEscape( session.getEffectiveUserName() ) + "!" );
}

Restart the Jetty server, and enter a name. Et voilà…

In the next post, let’s use the Java NAPI to create a „real“ Webserver.

Domino & Spring Boot: How does it work

1. Juni 2018 Posted by Sven Hasselbach

The example Spring Boot Plugin I have published two days ago is a full working example to run a Spring Boot applications directly in the Domino HTTP task. It is designed as an OSGi plugin and runs inside the servlet container, which means that the „normal“ features of the Domino HTTP task are available at runtime, for example the existing the user session is accessible via the ContextInfo object.

To complile and deploy the plugin, you first have to install the XPages SDK and create the missing feature project and update site project as described in on of my older posts.
After importing the plugin in the UpdateSite and restarting the HTTP task, the Spring Boot application is ready and will flood the console with a lot of debugging informations when the first request hits the application:

While the example is a really basic one I am using the same approach for years in production environment. The reason is that it was fundamental for the customer (which has abandoned Domino years ago) to have a modern and industry-wide standard framework: The application is now a Spring Boot application which has a NoSQL backend – the decision makers had some buzzwords, the term „Domino“ is no longer used and all participants are happy now.

How does it work?

First it is required that the main configuration is still in the web.xml file. This is required because of the old servlet container which is still only available in version 2.5; I was not successfull to implement the latest Spring Boot version 5, because it seems that a servlet container version 3.0 is required.

In the web.xml file, the servlet mapping is defined:

<servlet>
   <servlet-name>dominoSpringBootServlet</servlet-name>
   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
   <load-on-startup>1</load-on-startup>
</servlet>

An additional configuration file dominoSpringBootServlet.xml contains the servlet specific configuration; both (the servlet name and the XML configuration) must have the same name, otherwise Spring Boot will not find the configuration file.

In this configuration file, the base package is defined, which tells the framework where to scan for Java classes and annotations:

<context:component-scan base-package="domino.springboot.plugin" />

The following line enables Spring Boot’s annotations:

<mvc:annotation-driven />

Now, the package domino.springboot is scanned for controllers and configuration classes, which I will describe in the next post.

 

Domino & Spring Boot: An example project

30. Mai 2018 Posted by Sven Hasselbach

I have uploaded an example for running Spring Boot applications on top of Domino. You can find it here:
https://github.com/hasselbach/domino-springboot

This solution is running for years in productive environments.

Hopefully I will find some time to explain how it works.

Domino & Spring Boot: ScheduledTasks

2. Mai 2018 Posted by Sven Hasselbach

When developing Spring Boot applications running on Domino, there is a feature which runs out of the box and makes developers happy: ScheduledTasks.

These are the equivalent for agents, but they are running directly in the HTTP task (which allows to access the complete Spring Application at runtime) and can be scheduled exactly to the milisecond.

To enable a scheduled task, you first have to add the @EnableScheduling annotation in your Application class:

package domino_spring.plugin;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class Application extends org.springframework.boot.web.support.SpringBootServletInitializer {

   public static void main(String[] args) {
      SpringApplication.run(Application.class, args);
   }

}

Next step is to declare the TaskScheduler used. Just add a bean to your Application class:

@Bean
 public TaskScheduler taskScheduler() {
      return new ConcurrentTaskScheduler();
 }

After completing the setup you can define your scheduled task:

package domino_spring.plugin;

import java.text.SimpleDateFormat;
import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class ScheduledTasks {

   private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);

   private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");

   @Scheduled(fixedRate = 5000)
   public void reportCurrentTime() {
      log.info("The time is now {}", dateFormat.format(new Date()));
   }
 
}

When the Sprint Boot application is now started, the current time is  printed to the logs every 5 seconds.

java.security.AccessControlException kills productivity

9. April 2018 Posted by Sven Hasselbach

Dear IBM, can you please remove the totally useless java policy restrictions? Especially for agents running on the server?

I can’t imagine how much life time and customers money was spent during the last decades just to find a workaround for these limitations. The Q&A sites are full of questions about problems with this topic, and I never met someone who found the restriction usefull.

It’s 2018, and writing something in Lotus Script just to „solve“ this issue makes absolutly no sense. The whole „Let’s restrict Java as much as we can“ is pita – everyone knows how to ship around these restrictions. The „bad guys“ are not stopped, only the „good developers“ will be limited.

By the way: Are these limitations planned for notes.js integration? If so, please drop it immediatly.

2018 kann losgehen

2. Februar 2018 Posted by Stephan Kopp

Ich weiß es ist schon Februar, aber ich hatte die letzten 3 Wochen Urlaub und starte sozusagen erst jetzt so richtig ins neue Jahr. Ich bin kein Freund von guten Vorsätzen, primär weil ich mich eh selten daran halte. Trotzdem hat natürlich jeder so seine Vorstellungen und Wünsche an ein neues Jahr und blickt auf das Vergangene zurück.

Beruflicher Wandel

Mein Berufsleben habe ich als Lotus Notes Entwickler gestartet, das ist mittlerweile schon ein paar Jährchen her. Vor einigen Jahren, als das Ende dieser Ära abzusehen war, habe ich angefangen mich auch mit anderen Dingen zu beschäftigen. Nach einigen Experimenten bin ich dann bei Java, dem Vaadin und Spring Boot Framework gelandet. Rückblickend kann ich sagen, dass ich letztes Jahr nun erstmals mehr Zeit mit diesen neuen Themen verbracht habe und weniger mit Lotus Notes. Das Thema Notes/Domino wird mich natürlich auch weiterhin begleiten, aber ich denke der Fokus wird sich weiter darauf konzentrieren, die Kunden bestmöglich bei der Ablösung zu beraten.

Mein persönlicher Wunsch für dieses Jahr ist es, diese Projekte, die sich komplett jenseits der altbekannten Lotus Notes Welt bewegen weiter auszubauen. Die neuen Themen, Technologien und Konzepte wie Microservices, APIs, etc. machen mir viel mehr Spaß. Das sind herausfordernde Projekte und man hat die Gelegenheit sich in neue Themen und Ideen einzuarbeiten.

IBM Notes/Domino 10

Der Verkauf der Produkt Sparte hat einige Leute in der Community in helle Aufregung versetzt und positive Erwartungen und Hoffnungen geweckt. Persönlich hat mich das alles allerdings nicht überzeugt. Es ist eine naheliegende Entscheidung von IBM, aber die Hoffnung, dass mit Notes 10 jetzt eine echte Weiterentwicklung auf uns zukommt kann ich nicht teilen. Meiner Meinung nach ist das allerhöchstens eine Produktpflege, um den Kunden ein wenig den Druck von den Schultern zu nehmen, Notes möglichst schnell abzulösen. Das kommt allerdings einige Jahre zu spät und die meisten haben das Thema „End of Lotus Notes“ schon fest auf der Agenda.

Soll jetzt nicht heißen, dass ich Kunden jede Investition in die Plattform versuche auszureden. Wer weiter darauf setzen will, hat jetzt einen validen Fahrplan für die nächsten Jahre und kann bestehende Applikationen mit gutem Gewissen weiterentwickeln. Ich persönlich setze meinen Fokus aber nicht mehr auf Lotus Notes, sondern will mich weiterentwickeln und mich um interessantere Themen kümmern.

Work Life Balance

Das ist ein sehr überstrapazierter Begriff, aber für mich hat das Thema einen hohen Stellenwert. Ich arbeite um zu leben, aber ich lebe nicht nur um zu arbeiten. Natürlich stecke ich viel Zeit und Mühe in meinen Job, aber kann meine Freizeit und Urlaub auch sehr genießen. Der Start in das Jahr 2018 mit einer ausgedehnten Asien Reise war hierfür ein guter Anfang und sicher nicht meine letzte Auszeit dieses Jahr.

Das neue Jahr kann also kommen und ich bin gespannt, was mich erwartet.

2018 kann losgehen

2. Februar 2018 Posted by Stephan Kopp

Ich weiß es ist schon Februar, aber ich hatte die letzten 3 Wochen Urlaub und starte sozusagen erst jetzt so richtig ins neue Jahr. Ich bin kein Freund von guten Vorsätzen, primär weil ich mich eh selten daran halte. Trotzdem hat natürlich jeder so seine Vorstellungen und Wünsche an ein neues Jahr und blickt auf das Vergangene zurück.

Beruflicher Wandel

Mein Berufsleben habe ich als Lotus Notes Entwickler gestartet, das ist mittlerweile schon ein paar Jährchen her. Vor einigen Jahren, als das Ende dieser Ära abzusehen war, habe ich angefangen mich auch mit anderen Dingen zu beschäftigen. Nach einigen Experimenten bin ich dann bei Java, dem Vaadin und Spring Boot Framework gelandet. Rückblickend kann ich sagen, dass ich letztes Jahr nun erstmals mehr Zeit mit diesen neuen Themen verbracht habe und weniger mit Lotus Notes. Das Thema Notes/Domino wird mich natürlich auch weiterhin begleiten, aber ich denke der Fokus wird sich weiter darauf konzentrieren, die Kunden bestmöglich bei der Ablösung zu beraten.

Mein persönlicher Wunsch für dieses Jahr ist es, diese Projekte, die sich komplett jenseits der altbekannten Lotus Notes Welt bewegen weiter auszubauen. Die neuen Themen, Technologien und Konzepte wie Microservices, APIs, etc. machen mir viel mehr Spaß. Das sind herausfordernde Projekte und man hat die Gelegenheit sich in neue Themen und Ideen einzuarbeiten.

IBM Notes/Domino 10

Der Verkauf der Produkt Sparte hat einige Leute in der Community in helle Aufregung versetzt und positive Erwartungen und Hoffnungen geweckt. Persönlich hat mich das alles allerdings nicht überzeugt. Es ist eine naheliegende Entscheidung von IBM, aber die Hoffnung, dass mit Notes 10 jetzt eine echte Weiterentwicklung auf uns zukommt kann ich nicht teilen. Meiner Meinung nach ist das allerhöchstens eine Produktpflege, um den Kunden ein wenig den Druck von den Schultern zu nehmen, Notes möglichst schnell abzulösen. Das kommt allerdings einige Jahre zu spät und die meisten haben das Thema „End of Lotus Notes“ schon fest auf der Agenda.

Soll jetzt nicht heißen, dass ich Kunden jede Investition in die Plattform versuche auszureden. Wer weiter darauf setzen will, hat jetzt einen validen Fahrplan für die nächsten Jahre und kann bestehende Applikationen mit gutem Gewissen weiterentwickeln. Ich persönlich setze meinen Fokus aber nicht mehr auf Lotus Notes, sondern will mich weiterentwickeln und mich um interessantere Themen kümmern.

Work Life Balance

Das ist ein sehr überstrapazierter Begriff, aber für mich hat das Thema einen hohen Stellenwert. Ich arbeite um zu leben, aber ich lebe nicht nur um zu arbeiten. Natürlich stecke ich viel Zeit und Mühe in meinen Job, aber kann meine Freizeit und Urlaub auch sehr genießen. Der Start in das Jahr 2018 mit einer ausgedehnten Asien Reise war hierfür ein guter Anfang und sicher nicht meine letzte Auszeit dieses Jahr.

Das neue Jahr kann also kommen und ich bin gespannt, was mich erwartet.

Quick-n-Dirty: Hotfix for DateTimeHelper

12. Juni 2017 Posted by Sven Hasselbach

This weekend I stumbled over a bug of the DateTimeHelper: If the value of the field is empty, no actions and/or action listeners connected with a managed bean will be executed anymore.

Here is an example of a small XPage to illustrate the problem:

<?xml version="1.0" encoding="UTF-8"?><?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
 
    <xp:label 
        value="#{javascript:java.lang.System.currentTimeMillis()}" id="labelNow" />

     <xp:inputText id="inputTextDT" value="#{myBean.valueDT}">
         <xp:this.converter>
             <xp:convertDateTime type="date" />
         </xp:this.converter>
         <xp:dateTimeHelper />
     </xp:inputText>

    <xp:button id="button" value="OK">
        <xp:eventHandler
            event="onclick"
            submit="true"
            refreshMode="partial"
            refreshId="labelNow"
            actionListener="#{myBean.action}" />
     </xp:button>

</xp:view>

It does not matter if you set the disableValidators property for the text field to true, even an immediate=true won’t help here. The reason for the problem is that the renderer of the dateTimeHelper always uses the attached converter and fails with a null pointer exception if the value is empty (this infringes the JSF specification, but IBM has implemented it this way).

The workaround for this problem is to overwrite the existing renderer class and handle the NPE by yourself:

package ch.hasselba.xpages.renderer;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.ConverterException;
public class DateTimeHelperRenderer
    extends com.ibm.xsp.renderkit.dojo.DateTimeHelperRenderer{

    public Object getConvertedValue(FacesContext fc, UIComponent uiComponent, Object obj)
        throws ConverterException  {

          Object result = super.getConvertedValue(fc, uiComponent, obj);

          if( result == null )
            return new Object();

          return result;
    }
}

The renderer must now be registered in faces-config.xml:

<?xml version="1.0" encoding="UTF-8"?><?xml version="1.0" encoding="UTF-8"?>
<faces-config>
  <render-kit>
    <renderer>
      <component-family>javax.faces.Input</component-family>
      <renderer-type>com.ibm.xsp.DateTimeHelper</renderer-type>
      <renderer-class>ch.hasselba.xpages.renderer.DateTimeHelperRenderer</renderer-class>
    </renderer>
  </render-kit>
</faces-config>

Now the problem is solved, the managed bean’s action get executed even if the value is empty.

High Performance REST Applications (4) – Looking into OSGi

4. Mai 2017 Posted by Sven Hasselbach

Before going any deeper into the the servlet project, let’s have a look at the imported projects and talk about some OSGi basics.

First you will notice that for every cloned repository three Eclipse projects have been imported:

  1. A plugin project
  2. A feature project
  3. An updatesite project

The plugin project contains the code and all the relevant resources of the servlet. It defines extension points provided or describes which extension points are used by the plugin.
A feature project is basically a list of plugins and other features which can be understood as a logical separate unit. And an updatesite contains all features you need.

Using an UpdateSite is the preferred way to deploy plugins to Domino. It has a „Build All„-Button which builds all plugins from all features in your updatesite project, and creates the JARs to import into an UpdateSite in Domino.

Update Site „Build All“-Button

A plugin is mainly described by two files: the MANIFEST.MF (stored in the /META-INF folder) and a plugin.xml in the root.

plugin.xml & MANIFEST.MF

The MANIFEST.MF contains meta informations about the Plugin, like the name, imports and exports, other required bundles, the Activator class, the Execution environment and many more.

For example the Domino REST servlet plugin requires the concurrent plugin:

Required Bundles: „ch.hasselba.concurrent.plugin“

If the bundle is loaded, but the requirements are not fullyfied, the bundle won’t start.

The plugin.xml is optional for OSGI bundles, but we need this file, because it describes the extension point used by our plugin:

plugin.xml – Extension Point

Our servlet plugin uses the com.ibm.pvc.webcontainer.application extension point to run a JEE application. The parameter contextRoot is the path our servlet listens (which means it will be reachable at http://example.com/ec2017), and the contentLocation parameter is the path to the static files of the plugin and our web.xml.

WebContent folder

An Activator class is the class executed when a bundle is started or stopped (or the other possibilities a lifecyle of a bundle can have). It is also a good idea to store the BundleContext in the Activator to communicate with the OSGi environment.

Last but not least, there is a build.properties file: This file is for Eclipse and contains all relevant information to build the bundle. This means that if you add a JAR file the plugin requires, you have to add the JAR to both – the MANIFEST.MF and the build.properties (if the classes of the JAR file are used in the project).

In the Domino REST Servlet project, the spymemcached.jar is used, that’s why it is added to the build path and the manifest:

build.properties – spymemcached JAR

MANTIFEST.MF – spymemcached JAR

You have to keep this in your mind, because if you write some code, it is not enough just to add the required JAR to the build path, it also has to be exported in your bundle / plugin.

In the next post, let’s have a look into web.xml, and then go into the real „High Performance“ solution.