mapjfx 1.18.0 has MAP_POINTER_MOVED event and uses OfflineCache singleton

I just released mapjfx version 1.18.0 it should be found shortly at maven central, the artifact coordinates are:

  <dependency>
    <groupId>com.sothawo</groupId>
    <artifactId>mapjfx</artifactId>
    <version>1.18.0</version>
  </dependency>

The source is available at GitHub.

The first change in this verison is that the OfflineCache instance now is a Singleton and shared between all instances of MapView objects in an application. This allows for more than one MapView to use the offline cache mechanisms.

The other change is that now for a pointer (mouse) move detetction when the pointer is over the map, an appropriate event is generated.

Java regular expression matching with some parts being case insensitive

I just came upon the possibility that in Java it is possible within regular expressions to turn case sensitivity on and off. This is no new feature, it has been there since Java 1.4, but I never saw it before and thought it might be worth a post.

So let’s assume I want to have a regular expression which matches the three words “goodbye bLuE SKIES” – the first in lowercase, the second mixed and the last in uppercase. This can be done with the following regular expression:

goodbye (?i)blue(?-i) SKIES

The “(?i)” switches the pattern to case insensitive, and “(?-i)” switches it back to case sensitive.

The java API doc https://docs.oracle.com/javase/8/docs/api/ lists these as “Special constructs”. And although they use the parenthesis, thesi constructs do not capture anything in a group.

mapjfx version 1.17.0 reporting the map’s extent and using OpenLayers 4.6.5

I just released mapjfx version 1.17.0 it should be found shortly at maven central, the artifact coordinates are:

  <dependency>
    <groupId>com.sothawo</groupId>
    <artifactId>mapjfx</artifactId>
    <version>1.17.0</version>
  </dependency>

The source is available at GitHub.

mapjfx now ow uses OpenLayers 4.6.5.

As a new feature, the MapView object now emits events whenever the extent of the map changes. This happens when the center of the map is changed, the zoom value is changed or when the map’s window size is changed. The event can be processed like this:

// add an event handler for extent changes and display them in the status label
mapView.addEventHandler(MapViewEvent.MAP_BOUNDING_EXTENT, event -> {
  event.consume();
  labelExtent.setText(event.getExtent().toString());
});

mapjfx problems

Since some time now users have reported that mapjfx does not properly display the maps. I wrote a small JavaFX application which is just a WebView and a textfield to edit an URL. This application as well shows buggy behaviour in not loading the map tiles properly. This is the plain JavaFX WebView, with no adddition, custom Javascript code or things that mapjfx does:

Other browsers have no problem with these pages. I do not know if this is some bug in WebView – or more probably in WebKit that’s used internally, but there is nothing I have found up to now that I could do to work around.

This map-tile loading problem only seems to happen with OpenStreetMap servers, I have up to now found no problems when using Bing Maps.

Edit 29.03.2018: At the moment I do not see these issues when running with Oracle JDK 10, feedback about this is very welcome!

processing meta annotations with Java

Everybody who is using Spring these days knows that Spring has many annotations that are just used to group together a couple of other annotations and so to keep the number of annotations small. For example the @RestController annotation combines basically the @Controller and the @ResponseBody annotation so that the user only needs to annotate his class with one annotation instead of two annotations.

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
    String value() default "";
}

The JEE @Stereotype annotation works basically the same way.

In this post I show how to define and evaluate such meta annotations in plain Java without using Spring or JEE.

The annotations

I define the annotations for the example within one class I called WorkingDays. I define one annotation for each day of the week and two annotations that group working days and weekend days by referencing the other annotations:

public class WorkingDays {

    @Retention(RUNTIME)
    @interface OnMonday { }

    @Retention(RUNTIME)
    @interface OnTuesday { }

    @Retention(RUNTIME)
    @interface OnWednesday { }

    @Retention(RUNTIME)
    @interface OnThursday { }

    @Retention(RUNTIME)
    @interface OnFriday { }

    @Retention(RUNTIME)
    @interface OnSaturday { }

    @Retention(RUNTIME)
    @interface OnSunday { }

    @Retention(RUNTIME)
    @OnMonday @OnTuesday @OnWednesday @OnThursday @OnFriday
    @interface OnWorkday { }

    @Retention(RUNTIME)
    @OnSaturday @OnSunday
    @interface OnWeekend { }

    public static List&lt;Class&lt;? extends Annotation&gt;&gt; dayOfWeekAnnotations() {
        return Arrays.asList(WorkingDays.OnMonday.class, WorkingDays.OnTuesday.class, WorkingDays.OnWednesday.class,
                WorkingDays.OnThursday.class, WorkingDays.OnFriday.class, WorkingDays.OnSaturday.class,
                WorkingDays.OnSunday.class);
    }
}

In addition to that there is a method dayOfWeekAnnotations() which returns the annotations for the single days, but not the grouping ones.

The class using the annotations

I define a class Employee with three nested subclasses, one is using the @OnWorkday annotation, another one the @OnWeekend annotation and the third one both of them. Please notice, that the annotations for the separate days are not used here:

public class Employee {

    @OnWorkday
    public static class OnlyOnWorkdayEmployee extends Employee {
    }

    @OnWeekend
    public static class OnlyOnWeekendEmployee extends Employee {
    }

    @OnWorkday @OnWeekend
    public static class EveryDayEmployee extends Employee {
    }

    public String simpleClassName() {
        return getClass().getSimpleName();
    }
}

The class evaluating the annotations

To evaluate this, I create a class named Office in which I first create three different employees, each of a different type.

Then I iterate over all the annotations for the single days – remember, the employees use the meta annotations, not the ones for the days –  and print out which of the employees works on which day. For this I use a utility class MetaAnnotationUtil which I will show after the output.

public class Office {
    private static final Logger LOGGER = LoggerFactory.getLogger(Office.class);

    public static void main(String[] args) {
        List<Employee> employees = getEmployees();

        WorkingDays.dayOfWeekAnnotations()
                .forEach(dayAnnotation -> {
                    LOGGER.info("\n{}", dayAnnotation.getSimpleName());
                    employees.forEach(employee ->
                            LOGGER.info(" {} is working: {}", employee.simpleClassName(),
                                    employeeWorksOn(employee, dayAnnotation)));
                });
    }

    private static boolean employeeWorksOn(Employee employee, Class<? extends Annotation> dayAnnotation) {
        return MetaAnnotationUtil.hasMetaAnnotation(employee.getClass(), dayAnnotation);
    }

    @NotNull
    private static List<Employee> getEmployees() {
        List<Employee> employees = new ArrayList<>();
        employees.add(new Employee.EveryDayEmployee());
        employees.add(new Employee.OnlyOnWorkdayEmployee());
        employees.add(new Employee.OnlyOnWeekendEmployee());
        return employees;
    }
}

This is the output when running the program:

13:04:18.302 [main] INFO  com.sothawo.annotations.Office - 
OnMonday
13:04:18.342 [main] INFO  com.sothawo.annotations.Office -  EveryDayEmployee is working: true
13:04:18.342 [main] INFO  com.sothawo.annotations.Office -  OnlyOnWorkdayEmployee is working: true
13:04:18.343 [main] INFO  com.sothawo.annotations.Office -  OnlyOnWeekendEmployee is working: false
13:04:18.343 [main] INFO  com.sothawo.annotations.Office - 
OnTuesday
13:04:18.343 [main] INFO  com.sothawo.annotations.Office -  EveryDayEmployee is working: true
13:04:18.343 [main] INFO  com.sothawo.annotations.Office -  OnlyOnWorkdayEmployee is working: true
13:04:18.343 [main] INFO  com.sothawo.annotations.Office -  OnlyOnWeekendEmployee is working: false
13:04:18.343 [main] INFO  com.sothawo.annotations.Office - 
OnWednesday
13:04:18.344 [main] INFO  com.sothawo.annotations.Office -  EveryDayEmployee is working: true
13:04:18.344 [main] INFO  com.sothawo.annotations.Office -  OnlyOnWorkdayEmployee is working: true
13:04:18.344 [main] INFO  com.sothawo.annotations.Office -  OnlyOnWeekendEmployee is working: false
13:04:18.344 [main] INFO  com.sothawo.annotations.Office - 
OnThursday
13:04:18.344 [main] INFO  com.sothawo.annotations.Office -  EveryDayEmployee is working: true
13:04:18.344 [main] INFO  com.sothawo.annotations.Office -  OnlyOnWorkdayEmployee is working: true
13:04:18.345 [main] INFO  com.sothawo.annotations.Office -  OnlyOnWeekendEmployee is working: false
13:04:18.345 [main] INFO  com.sothawo.annotations.Office - 
OnFriday
13:04:18.346 [main] INFO  com.sothawo.annotations.Office -  EveryDayEmployee is working: true
13:04:18.346 [main] INFO  com.sothawo.annotations.Office -  OnlyOnWorkdayEmployee is working: true
13:04:18.346 [main] INFO  com.sothawo.annotations.Office -  OnlyOnWeekendEmployee is working: false
13:04:18.347 [main] INFO  com.sothawo.annotations.Office - 
OnSaturday
13:04:18.347 [main] INFO  com.sothawo.annotations.Office -  EveryDayEmployee is working: true
13:04:18.347 [main] INFO  com.sothawo.annotations.Office -  OnlyOnWorkdayEmployee is working: false
13:04:18.347 [main] INFO  com.sothawo.annotations.Office -  OnlyOnWeekendEmployee is working: true
13:04:18.348 [main] INFO  com.sothawo.annotations.Office - 
OnSunday
13:04:18.349 [main] INFO  com.sothawo.annotations.Office -  EveryDayEmployee is working: true
13:04:18.349 [main] INFO  com.sothawo.annotations.Office -  OnlyOnWorkdayEmployee is working: false
13:04:18.349 [main] INFO  com.sothawo.annotations.Office -  OnlyOnWeekendEmployee is working: true

The class doing the magic

In the MetaAnnotationUtil class I setup a Set to collect the annotations and then add the found annotations if I have them not in the set already and recursively process the annotations of the current annotation:

public abstract class MetaAnnotationUtil {

    /**
     * checks wether an annotated element has an annotaion directly or as a meta annotation (annotation on annotation)
     *
     * @param annotatedElement
     *         the annotated element
     * @param annotationClass
     *         the annotation class to search
     * @return true if the annotaion class is found
     */
    public static boolean hasMetaAnnotation(@NotNull final AnnotatedElement annotatedElement,
                                            @NotNull Class<? extends Annotation> annotationClass) {
        final Set<Class<? extends Annotation>> foundAnnotations = new HashSet<>();
        findAllAnnotations(annotatedElement, foundAnnotations);
        return foundAnnotations.contains(annotationClass);
    }

    /**
     * do a meta (recursive) search on the annotated element and ignore annotations that have already been found
     *
     * @param annotatedElement
     *         the annotated element
     * @param foundAnnotations
     *         the already found annotations
     */
    private static void findAllAnnotations(@NotNull final AnnotatedElement annotatedElement,
                                           @NotNull final Set<Class<? extends Annotation>> foundAnnotations) {
        Arrays.stream(annotatedElement.getDeclaredAnnotations())
                .map(Annotation::annotationType)
                .filter(annotation -> !foundAnnotations.contains(annotation))
                .forEach(aClass -> {
                    foundAnnotations.add(aClass);
                    findAllAnnotations(aClass, foundAnnotations);
                });
    }
}

 

Conclusion

This post has shown that with a little recursive implementation, meta annotations can easily be processed. In a real life scenario I would add caching mechanisms to make sure that the annotations for classes and methods are not repeatedly searched. They can be cached as they are created on compile time and do not change when running.

And, when you are already using Spring and just need to add some custom meta annotations to your project: Use Spring Core’s AnnotationUtil class and do not write something of your own!

mapjfx 1.15.0 using OpenLayers 4.6.4

I just released mapjfx version 1.15.0 it should be found shortly at maven central, the artifact coordinates are:

  <dependency>
    <groupId>com.sothawo</groupId>
    <artifactId>mapjfx</artifactId>
    <version>1.15.0</version>
  </dependency>

The source is available at GitHub.

Now uses OpenLayers 4.6.4.

Comments and contributions welcome.

mapjfx 1.14.0 released

I just released mapjfx version 1.14.0 it should be found shortly at maven central, the artifact coordinates are:

  <dependency>
    <groupId>com.sothawo</groupId>
    <artifactId>mapjfx</artifactId>
    <version>1.14.0</version>
  </dependency>

The source is available at GitHub.

  • There are new events that are triggered on mouse entry and exit on markers and labels. Thanks to skinkie for that.
  • The CSS style property of a Label is now an observed property and changes will be reflected on the map immediately.
  • Now uses OpenLayers 4.4.0

Comments and contributions welcome.

mapjfx 1.13.2 using OpenLayers 4.3.2

I just released mapjfx version 1.13.2 it should be found shortly at maven central, the artifact coordinates are:

  <dependency>
    <groupId>com.sothawo</groupId>
    <artifactId>mapjfx</artifactId>
    <version>1.13.2</version>
  </dependency>

The source is available at GitHub.

Now uses OpenLayers 4.3.2.

Comments and contributions welcome.

annotation based Logger injection in Spring with Kotlin

what’s it all about

In this post I show how to implement an annotation-based Logger injection in Spring when writing the application with Kotlin.

The used technique is quite far from new, basically what I do is implement a BeanPostProcessor which scans the properties of the beans for fields annotated with a custom annotation and sets these fields to a Logger instance. Examples how to implement this in Java can be found on the web, here I show the Kotlin version.

In this example I have an innovative REST controller wich has the ability to say hello. This controller has an injected service to do the processing of the incoming name. The reason for implementing this in two classes is that I want to show 2 different ways to inject a Logger.

The whole project for this example is available on GitHub.

The setup

I use Spring-Boot to setup the project. There is nothing special in relation to boot here, but it gets me fast on the track. I created my project from within IntelliJ IDEA as a Spring-Boot project, but you can use https://start.spring.io/ as well. Language is Kotlin, and the only dependency needed is web.

The logging configuration

I added a simple logback.xml to my project:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/base.xml"/>
    <logger name="com.sothawo" level="DEBUG"/>
    <root level="WARN"/>
</configuration>

this sets the global level to WARN, and everything from my code to DEBUG.

The classes that get a Logger injected

The first class I show is HelloService, this is a Spring component which has one method that takes a string, and returns it concatenated to “hello “, and of course this is logged. In this class the Logger is injected in a normal property of the class by using the @Slf4jLogger annotation which I will show later:

package com.sothawo

import org.slf4j.Logger
import org.springframework.stereotype.Service

/**
 * @author P.J. Meisch (pj.meisch@sothawo.com)
 */
@Service
class HelloService {

    @Slf4jLogger
    lateinit var log: Logger

    fun sayHello(name: String): String {
        log.info("saying hello to: $name")
        return "hello $name"
    }
}

Using a normal property would create multiple instances of the same Logger-name if multiple HelloService instances were created (which they aren’t, but if you would change the scope this might happen). Therefore a better solution is to put the Logger in the companion object . This can be seen in the HelloController class:

package com.sothawo

import org.slf4j.Logger
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

/**
 * @author P.J. Meisch (pj.meisch@sothawo.com)
 */
@RestController
@RequestMapping("/hello")
class HelloController(val helloService: HelloService) {

    @GetMapping("/{name}")
    fun sayHello(@PathVariable name: String): ResponseEntity {
        log.info("sayHello called with arg $name")
        return ResponseEntity.ok(helloService.sayHello(name))
    }

    companion object {
        @Slf4jLogger
        lateinit var log: Logger
    }
}

The annotation and injection code

package com.sothawo

import org.slf4j.LoggerFactory
import org.springframework.beans.factory.config.BeanPostProcessor
import org.springframework.stereotype.Component
import kotlin.reflect.KMutableProperty
import kotlin.reflect.full.companionObjectInstance
import kotlin.reflect.full.declaredMemberProperties

/**
 * @author P.J. Meisch (pj.meisch@sothawo.com)
 */

@Target(AnnotationTarget.PROPERTY)
annotation class Slf4jLogger

@Component
class LoggingInjector : BeanPostProcessor {

    override fun postProcessBeforeInitialization(bean: Any?, beanName: String?): Any? {
        bean?.let {
            val loggerName = it::class.java.canonicalName
            processObject(it, loggerName)
            it::class.companionObjectInstance?.let {
                processObject(it, loggerName)
            }
        }

        return bean
    }

    override fun postProcessAfterInitialization(bean: Any?, beanName: String?) = bean

    private fun processObject(target: Any, loggerName: String) {
        target::class.declaredMemberProperties.forEach {
            property ->
            property.annotations
                    .filter { it is Slf4jLogger }
                    .forEach {
                        if (property is KMutableProperty<*>) {
                            property.setter.call(target, LoggerFactory.getLogger(loggerName))
                        }
                    }
        }
    }
}

First I create the annotation with the AnnotationTarget.PROPERTY  (lines 14, 15). Then I define a Spring component that implements the BeanPostProcessor interface. The postProcessAfterInitialization method does nothing, it just returns the bean that is passed in, the magic happens in postProcessBeforeInitialization. Here, if the bean that is passed in is not null, it is passed into my processObject method, and then I check if there is a companion object and analyze this in the same way. The Logger name is built from the bean’s class as I do not want to have the companion object’s name in there.

In the processObject method I iterate over the properties of the target object that was passed in. For each property I check if it has the @SLF4JLogger annotation and if so I set it’s value to a newly created Logger instance with the requested name.

The output

When running the program and issuing a call like

curl localhost:8080/hello/world

the log output shows:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.5.4.RELEASE)

2017-07-12 15:58:32.567  INFO 2797 --- [           main] KotlinSpringLoggerInjectionApplicationKt : Starting KotlinSpringLoggerInjectionApplicationKt on pjm.local with PID 2797 (/Users/peter/Entwicklung/sothawo/kotlin-spring-logger-injection/target/classes started by peter in /Users/peter/Entwicklung/sothawo/kotlin-spring-logger-injection)
2017-07-12 15:58:32.571 DEBUG 2797 --- [           main] KotlinSpringLoggerInjectionApplicationKt : Running with Spring Boot v1.5.4.RELEASE, Spring v4.3.9.RELEASE
2017-07-12 15:58:32.571  INFO 2797 --- [           main] KotlinSpringLoggerInjectionApplicationKt : No active profile set, falling back to default profiles: default
2017-07-12 15:58:35.423  INFO 2797 --- [           main] KotlinSpringLoggerInjectionApplicationKt : Started KotlinSpringLoggerInjectionApplicationKt in 3.355 seconds (JVM running for 3.776)
2017-07-12 15:58:51.745  INFO 2797 --- [nio-8080-exec-1] com.sothawo.HelloController              : sayHello called with arg world
2017-07-12 15:58:51.745  INFO 2797 --- [nio-8080-exec-1] com.sothawo.HelloService                 : saying hello to: world

Conclusion

As shown here it is pretty simple to process annotation in Kotlin and use this for custom injection in Spring. Especially statments like annotations.filter { it is Slf4jLogger } are a pretty cool feature.