Tuesday, March 24, 2015

Spring boot and CORS (Cross Origin Resource Sharing)

Another example of use - spring boot

Sometimes a web application needs access to local resources (filesystem, printers, devices, etc).
The access is possible through Java Applets, ActiveX, browser plugins or ... a client application exposing REST API and the web application called by the client application through the Cross Domain AJAX call. This post describes how  you can create a local microserver called by browser in (about) 5 lines of code with spring boot.

The creation of a microserver with spring boot is very easy. Simply annotate your class with @RestController and your methods with @RequestMapper.  That's it.

But when the browser calls the local mircoserver as it follows:

$.ajax({
url: 'http://localhost:8080/rest/printers',
type: 'GET',
dataType: 'json',
async: true,
cache: false,
crossDomain: true,
beforeSend: function (xhr, opts) {
var auth = make_base_auth(user, password);
console.log('auth:' + auth);
xhr.setRequestHeader('Authorization', auth);
},
success: function (resp) {
$('#result').text(resp);
},
error: function(xhr) {
alert('Error!  Status = ' + xhr.status + " Message = " + xhr.statusText);
}
});

The browser raises this exception:

XMLHttpRequest cannot load http://localhost:8080/rest/printers?_=1424781952941. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8081' is therefore not allowed access.

In fact,  when you call the external domain the browser checks your call and it runs the CORS protocol http://www.w3.org/wiki/CORS for more details.

how can you work it out?

You can use a cors-filter  adds this dependence and registers the filter

@Bean
public FilterRegistrationBean corsFilter() {
FilterRegistrationBean registrationBean =
new FilterRegistrationBean();
CORSFilter corsFilter = new CORSFilter();
Map<String, String> params = new HashMap<>();
params.put("cors.allowed.methods",CORSFilter.DEFAULT_ALLOWED_HTTP_METHODS + ",DELETE,PUT");
params.put("cors.allowed.headers",CORSFilter.DEFAULT_ALLOWED_HTTP_HEADERS + ",authorization");
params.put("cors.logging.enabled", "true");
registrationBean.setInitParameters(params);
registrationBean.setFilter(corsFilter);
registrationBean.setUrlPatterns(Arrays.asList("/rest/*"));
return registrationBean;
}

at this point when the browser calls the external domain, the local microserver allows this call and it works.


Consideration for IE8-9

In this type of browser CORS is supported by XDomainRequest, not by XMLHttpRequest. 
This means that you need a jquery plugin jQuery-ajaxTransport-XDomainRequest to make a Cross Domain AJAX call.

IE 8-9 has more limitations. See xdomainrequest-restrictions-limitations-and-workarounds for more details and remember you must enable cross domain in security options!

To by-pass IE8-9 limitations you can add a workaround filter to microserver

@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public FilterRegistrationBean createIE8_9XDomainRequestFilter() {
    FilterRegistrationBean registrationBean =
            new FilterRegistrationBean();
    registrationBean.setFilter(new IE8_9XDomainRequestFilter());
    registrationBean.setUrlPatterns(Arrays.asList("/rest/*"));
    return registrationBean;
}


source code

Monday, March 2, 2015

WADL Generator in Spring Framework


Remarkable example of  WADL Generator in Spring Framework
http://javattitude.com/2014/05/26/wadl-generator-for-spring-rest/

Monday, February 23, 2015

Spring boot 1.4 run as windows service Procrun


The challenge was starting spring boot application as windows service. I spent a long time searching on the Internet the solution but I didn't find it.

My solution was the following.

First I created an application using start.spring.io



Then, I created a project to which I added spring-boot-loader dependency

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-loader</artifactId>
            <scope>provided</scope>
        </dependency>

because of the need to extend the JarLauncher class.
Spring boot provides a special launcher that changes the java behaviour class loader. The class org.springframework.boot.loader.JarLauncher creates a special class loader and boostraps the application.

Since I wanted to launch the application as a window service, I chose Procrun as service manager. 
Procrun needs two start and stop methods or one method with string array parameter (see procrun project for more details). Therefore I created a Bootsrap class that extended JarLauncher and implemented the methods that Procrun needs.

public class Bootstrap extends JarLauncher {

    private static ClassLoader classLoader = null;
    private static Bootstrap bootstrap = null;

    protected void launch(String[] args, String mainClass, ClassLoader classLoader, boolean wait)
            throws Exception {
        // spring boot 1.2 Runnable runner = createMainMethodRunner(mainClass, args, classLoader);
        // spring boot 1.2 Thread runnerThread = new Thread(runner);
        Thread runnerThread = new Thread(() -> {
            try {
                createMainMethodRunner(mainClass, args, classLoader).run();
            }
            catch(Exception ex) {}
        });        
        runnerThread.setContextClassLoader(classLoader);
        runnerThread.setName(Thread.currentThread().getName());
        runnerThread.start();
        if (wait == true) {
            runnerThread.join();
        }
    }

    public static void start (String []args) {
        bootstrap = new Bootstrap ();
        try {
            JarFile.registerUrlProtocolHandler();
            classLoader = bootstrap.createClassLoader(bootstrap.getClassPathArchives());
            bootstrap.launch(args, bootstrap.getMainClass(), classLoader, true);
        }
        catch (Exception ex) {
            ex.printStackTrace();
            System.exit(1);
        }
    }

    public static void stop (String []args) {
        try {
            if (bootstrap != null) {
                bootstrap.launch(args, bootstrap.getMainClass(), classLoader, true);
                bootstrap = null;
                classLoader = null;
            }
        }
        catch (Exception ex) {
            ex.printStackTrace();
            System.exit(1);
        }
    }

    public static void main(String[] args) {
        String mode = args != null && args.length > 0 ? args[0] : null;
        if ("start".equals(mode)) {
            Bootstrap.start(args);
        }
        else if ("stop".equals(mode)) {
            Bootstrap.stop(args);
        }
    }
}

In the spring boot application class I changed the main method with:

    private static ApplicationContext applicationContext = null;

    public static void main(String[] args) {
        String mode = args != null && args.length > 0 ? args[0] : null;

        if (logger.isDebugEnabled()) {
            logger.debug("PID:" + ManagementFactory.getRuntimeMXBean().getName() + 
                         " Application mode:" + mode + " context:" + applicationContext);
        }
        if (applicationContext != null && mode != null && "stop".equals(mode)) {
            System.exit(SpringApplication.exit(applicationContext, new ExitCodeGenerator() {
                @Override
                public int getExitCode() {
                    return 0;
                }
            }));
        }
        else {
            SpringApplication app = new SpringApplication(TestProcrunApplication.class);
            applicationContext = app.run(args);
            if (logger.isDebugEnabled()) {
                logger.debug("PID:" + ManagementFactory.getRuntimeMXBean().getName() + 
                             " Application started context:" + applicationContext);
            }
        }
    }

Then, I installed the service with prunsrv.exe

prunsrv.exe //IS//test-procrun --DisplayName="test-procrun" --Description="test-procrun" --Startup=auto --Install=%CD%\prunsrv.exe --Jvm=auto --Classpath=%CD%..\target\test-procrun-0.0.1-SNAPSHOT.jar --StartMode=jvm --StartClass=it.test.procrun.Bootstrap --StartMethod=start --StartParams=start --StopMode=jvm --StopClass=it.test.procrun.Bootstrap --StopMethod=stop --StopParams=stop --StdOutput=auto --StdError=auto --LogPath=%CD% --LogLevel=Debug





source code spring boot 1.2,1.3
source code spring boot 1.4