On software improvement

As you may know, I’m running the daily photo project Daily Fratze for more then 10 years now. What started as a simple PHP script in the late summer of 2005 grow into a Ruby On Rails application during 2006 and in 2010 i started writing the current version in Java, based on the Spring Framework. During those all those years, my database model stayed practically the same and has mostly seen additions so far.

I may have written it before, but Ruby On Rails was, despite all its hype, an eye opener for me. I was so deeply into desktop software, i really had no clue how to write web applications at that time and visiting some courses explaining J2EE (which Java EE was called backed then) didn’t help a lot, quite the contrary.

My love with Ruby On Rails last for over 3 years but our relationship broke eventually as I faced the several migrations from Rails 1.1 to 1.2, several 2.x and finally to 2.3 that really hurt though I did things as “in the book”.

The codebase I started in December 2010 is pretty much the same and something I’m only proud of as it was my way to come up with something like this and other stuff I do for a living.

Otherwise I wouldn’t really code anymore like this.

The last 1,5 years I didn’t do much on the site. I regularly updated some projects that have been extracted from the application but that was pretty much it. I put time into my Java User Group, the biking site and especially and certainly most rewarding, my family.

If you leave a software alone it ages and often not in good shape. If you skip a version of the libraries you’re using and then some you’ll often find yourself in a trap where one upgrade lead to dozens of others and minor upgrades that would bring security fixes and the likes leads to all kind of problems.

It didn’t come this far with Daily Fratze, i was only one version behind until last weekend, but this is how it starts.

Through my work with Spring Boot the last 2 years my first intuition was something like “Throw all but the entities away and start from scratch with boot” and I actually have 2 version of a new application floating around. One I started in December 2014 / January 2015 but abandoned very soon. The turn of the year 2014 to 2015 wasn’t a good time for me. I was highly depressed and for the first time in my life took an antidepressant and not only self-educated myself with beer. The question why programmers so often tend to run into this kind of problems is worth at least one another blogpost.

After somewhat finishing my biking application by even co-writing a book about it, i needed something new todo and startet a second try rewriting Daily Fratze from scratch and actually had some nice results this March.

But then I listened to Gernots Talk about the aim42 software architecture improvement method which needed some weeks to sink…

…and lead to asking myself the following questions

  • What are the main problems?
  • I’m I still happy with the UI or more specific (my) UX?
  • What is the cost on my family life creating a really complex and feature rich thing like the one I have again?
  • Can a brownfield project teach something I cannot learn on greenfield?

Outdated dependencies can become Daily Fratzes main problems. The UI, which pretty obviously has 2010/2011 style just works fine for me. It isn’t responsive in todays sense, but works better than half the Bootstrap based themes I tried and has less bloat. Yeah, it’s mostly still server side rendered HTML but guess what, I like it that way.

That leads to second: Yes, it does all the things I want and most of the stuff my faithful users want and are accustomed to.

The impact to my personal life was immense in March and April. Before going on a 9 days bike trip I was again at the edge of burnout. Working 9 to 10 hours a day, being a father and then coding until late after midnight takes its toll. Been there, done that. Don’t want to be there again.

And last and most important: As much as I dislike the way I programmed 5 years ago, I was the one writing down most of the requirements, listend to users and implemented it. So I know what is down there and I can change it for the better without the dead weight of not knowing why stuff is there.

Some weeks ago, on my bike in Denmark, I made the decision to stop working on a new version and made the old one a better one.

I started by upgrading all the dependencies (apart from the ORM, where I have to touch to many queries) and reduce most obvious coding style flaws.

That version went live on the weekend and already is faster:

First and immanent problem solved and fixed several bugs that neither me or a user has noticed before on the way.

The next thing on my list is: Writing real tests. I had no clue how to really do this 6 years ago. The tests I have crap and much of the code isn’t actually testable right now. So writing tests and changing stuff to actually be able to will have a significant impact.

So far, I’m really happy with my decision. My problem isn’t on the surface, I’m happy with the UI. And I’m happy with the algorithms I wrote. I’m not happy the way I wrapped them, packaged them and so on.

Gernots hint on stepping back and having a look at the bigger picture is of great value and even if its a pretty obvious hint, we rarely do this if we have the “start from scratch” option at our hands.

| Comments (0) »

17-May-16


Spring Boot Starter: Comfortable modularization and abstraction

This is the english version of an article about writing custom Spring Boot starters I’ve written for upcoming german JavaAktuell magazine. iJUG and DOAG members will get it for free.

Abstract

Even in the light of microservices good architecture and well thought modularization is vital for all kinds of application. A microservice without inner structure is as bad maintainable as an old-fashioned monolith, maybe even worse. Candidates for external modules that are intrinsic parts of many microservices without being one of their own could be:

  • Datastructures (entities) and corresponding data access objects or repositories for recurrent, structured information
  • Logging, status (healthchecks) and similar, cross-cutting or other non-functional requirements, which are paramount to manage the growing number of services
  • Integrations (for databases, caches and many others)

Those modules must not only be provided but also be configured, but often this configuration can be an opinionated configuration using sensible defaults which reduces manual configuration in many cases by magnitudes.

In this article i will show you how to create custom "Starters" for Spring Boot based applications that provide opinionated configuration, custom functionality and more.

Spring Boot

Spring Boot can be seen as an umbrella project for the Spring eco-system that has been created with the following goals in mind:

  • To provide a radically faster and widely accessible ‘getting started’ experience for all Spring development
  • To be opinionated out of the box, but get out of the way quickly as requirements start to diverge from the defaults
  • To provide a range of non-functional features that are common to large classes of projects (e.g. embedded servers, security, metrics, health checks, externalized configuration)

Spring Boot does not generate code during bootstrapping or during runtime and there is absolutely no requirement for XML configuration. All tools that are used by Spring Boot to detect available libraries, environment and configuration are available outside Spring Boot for the creation of custom configuration classes or whole "Starters".

The use case: Manage your JavaScript dependencies with Maven, Webjars und wro4j

Rumor has it, that there's still a need for server-side generated HTML code, JavaScript as progressive enhancement und simple management of JavaScript dependencies without the the need to use the "Universal install script", even in 2016:

The universal install script

We all have been cursed about Maven, but fact is: It works most of the time unobtrusive and it is also pretty good in managing dependencies, resolve them and find conflicting versions (for example using the enforcer-plugin).

WebJars is a project by James Ward and facilitates the explicit and easy management of the client-side dependencies in JVM-based web applications through Maven, SBT, Gradle, ivy or other build tools. Spring Boot supports WebJars out of the box and dependencies can be used like this:

<link rel='stylesheet' href='/webjars/bootstrap/3.1.0/css/bootstrap.min.css'>

wro4j is a tool for analyzing and optimizing web resources that combines many modern tools from frontend development.

The goal is to express frontend dependencies inside the pom.xml:

<properties>
	<jquery.version>1.11.3</jquery.version>
	<angularjs.version>1.2.29</angularjs.version>
</properties>
<!-- ... -->
<dependencies>
	<dependency>
	    <groupId>org.webjars</groupId>
	    <artifactId>jquery</artifactId>
	    <version>${jquery.version}</version>
	</dependency>	
	<dependency>
	    <groupId>org.webjars</groupId>
	    <artifactId>angularjs</artifactId>
	    <version>${angularjs.version}</version>
	</dependency>
</dependencies>

and to combine those dependencies into logical modules. The following example shows a library group that is combined with custom JavaScript code into one module:

<groups xmlns="http://www.isdc.ro/wro">
    <group name="lib">
        <js minimize="false">/webjars/jquery/@jquery.version@/jquery.min.js</js>
        <js minimize="false">/webjars/angularjs/@angularjs.version@/angular.min.js</js>
        <js minimize="false">/webjars/angularjs/@angularjs.version@/angular-route.min.js</js>
    </group>
 
    <group name="biking2">
    	<group-ref>lib</group-ref>
 
        <css>/css/stylesheet.css</css>
 
        <js>/js/app.js</js>
        <js>/js/controllers.js</js>
        <js>/js/directives.js</js>
    </group>
</group>

We're using the Xml configuration of Wro4j but there's also a nice Groovy DSL available.

This necessary configuration of what is to be used combined with the starter should be enough to include jQuery, AngularJS and custom functionality through one single HTML element.

Spring Boot Starter

A Spring Boot starter consists of two components:

  • One autoconfigure module containing the automatic configuration including it's default values.
  • The starter module itself, providing the dependency to the autoconfigure module and all needed additional dependencies

Including the starter as a dependency for a Spring Boot application should be enough to start using that library. If a separation of those two isn't needed, combine everything into the starter module.

Not a kind of magic: Conditions

Pivotals Spring Developer Advocate Josh Long explicitly opposes calling auto configuration inside Spring Boot applications "magic" and he is right saying so.

The environments automatic configuration is based on two core components:

  • Profiles (@Profile, available since Spring 3.1) and
  • Conditions (@Conditional together with org.springframework.context.annotation.Condition, available since Spring 4)

Profiles allow provisioning of Beans based on active profiles (spring.profiles.active), whereas conditions can be used to model simple to complex conditions under which Beans are instantiated and configured:

  • @Conditional from the core framework allows with a custom implementation of a Condition arbitrary statements

Spring Boot contains several useful implementations

  • @ConditionalOnBean / @ConditionalOnMissingBean: Configuration and beans are only instantiated and used when certain other Beans are available or missing
  • @ConditionalOnClass / @ConditionalOnMissingClass: Configuration and beans are only instantiated and used when certain classes are available to the classloader
  • @ConditionalOnResource is triggered only when specific resources are available
  • @ConditionalOnProperty: A really powerful condition that matches concrete properties inside configurational files

As much the automatic configuration of Spring Boot applications and their adaption to environment seems magical, it doesn't fit Clarke's third law. It's all there to see and use. Spring Boot even provides a ConditionEvaluationReport, that allows exactly to understand, why a configuration in a given order took place.

And by the way: Spring uses it's own features: The @Profile annotation has been rewritten for Spring 4 and is based on Conditions itself.

Example: Providing the Wro4j Servlet filter

Wro4js core is basically the Servlet filter mapped to an URL pattern which processes the requests for CSS and JavaScript files configured in wro.xml or wro.groovy. This filter should be added the the servlet registration without manual interaction by just adding the wro4j-spring-boot-starter dependency:

@Configuration
@ConditionalOnClass(WroFilter.class)
@ConditionalOnMissingBean(WroFilter.class)
@EnableConfigurationProperties(Wro4jProperties.class)
@AutoConfigureAfter(CacheAutoConfiguration.class)
public class Wro4jAutoConfiguration {
}
 
@ConfigurationProperties("wro4j")
public class Wro4jProperties {
}

Wro4jAutoConfiguration is a common Spring @Configuration class (classes which use programmatically instantiate, configure and provide Beans). The configuration – all methods annotated with @Bean among others – should only be done if the WroFilter class is available and there isn't a bean of that type yet. Configuration should be done after the automatic cache configuration as this will be used later. This class also provides properties starting with "wro4j.*" inside an object, in an instance of Wro4jProperties.

There's a caveat here: Often you create configuration for a possible starter inside the project that's using the starter first. That works fine (as by default all @Configuration classes under the base package are evaluated) until the classes building that starter are extracted from the project and you forget to add a META-INF/spring.factories. This file is very similar to the Java "Service Provider Interfaces" and contains the list of classes that should take part in autoconfiguration:

org.springframework.boot.autoconfigure.EnableAutoConfiguration = ac.simons.spring.boot.wro4j.Wro4jAutoConfiguration

With this file Spring Boot neither doesn't have to scan the whole classpath for starter nor the user must take additional steps besides adding the starter itself.

Back to the example. The Servlet filter is a regular Spring Bean and is created like this inside Wro4jAutoConfiguration:

@Bean
ConfigurableWroFilter wroFilter(WroManagerFactory wroManagerFactory, Wro4jProperties wro4jProperties) {
	ConfigurableWroFilter wroFilter = new ConfigurableWroFilter();
	wroFilter.setProperties(wroFilterProperties(wro4jProperties));
	wroFilter.setWroManagerFactory(wroManagerFactory);
	return wroFilter;
}

As you can see: The filter depends on a WroManagerFactory and other properties which are injected as usual into this method.

This example shows how powerful the code based configuration of Spring Beans is: Complex conditions and configuration are right their in the source and can be understood, debugged and more, with or without tooling support by IDEs.

Example: Different environment

Wro4j supports caching of optimized resources through a simple, memory-based Least Recently Used strategy. The strategy is an interface and an implementation of a new CacheStrategy using the built-in org.springframework.cache.CacheManager is easily done. To make it work there must be an instance of Springs CacheManager. To get one, use @EnableCaching for example. As Spring supports several caches, ehcache, Redis and others, you get either get a manager backed by either one of those.

Otherwise, if there's no cache manager, the starter should use the basic Wro4j strategy that certainly doesn't make use of any Spring features:

@Bean
@ConditionalOnBean(CacheManager.class)
@ConditionalOnProperty("wro4j.cacheName")
@ConditionalOnMissingBean(CacheStrategy.class)
@Order(-100)
<K, V> CacheStrategy<K, V> springCacheStrategy(CacheManager cacheManager, Wro4jProperties wro4jProperties) {
	return new SpringCacheStrategy<K, V>(cacheManager, wro4jProperties.getCacheName());
}
 
@Bean
@ConditionalOnMissingBean(CacheStrategy.class)
@Order(-90)
<K, V> CacheStrategy<K, V> defaultCacheStrategy() {
	return new LruMemoryCacheStrategy<K, V>();
}

The springCacheStrategy method is only executed if a CacheManager is active, the name for the actual cache is configured and there isn't a cache strategy already defined. By using an additional @Order annotation its ensured that this method is evaluated before defaultCacheStrategy, which instantiates the default Wro4j strategy.

There are certainly people who would add this exampel to @Annotatiomania™ in no time, but I think using this combination of annotations and code for conditions and configuration apart from business logic and objects is an appropriate and actually readable way.

Deployment

The example project is a standard Maven project, combining a shared autoconfigure and starter module. As such, it bundles the implementation of autoconfiguration as well as the dependencies to Wroj4:

<dependencies>
	<dependency>
	    <groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-starter</artifactId>
	</dependency>
 
	<dependency>
	    <groupId>ro.isdc.wro4j</groupId>
	    <artifactId>wro4j-core</artifactId>
	    <version>${wro4j.version}</version>
	</dependency>
 
	<dependency>
	    <groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-starter-test</artifactId>
	    <scope>test</scope>
	</dependency>       
</dependencies>

The dependencies of the autoconfigure module are usually marked as optional and only finalized in the starter module when creating two separate projects.

There's one peculiarity of the starter presented here: It's a child artifact of the official spring-boot-starters parent project. I thought it would be a nice training for clean code and good coding guidelines learning from the settings the Spring team uses. But, as i heard, they only want to inflict them on themselves. What made my life easier was my preferred IDE, which supports Checkstyle, JaCoCo and others out of the box.

The project is available on GitHub.

Conclusion

The starter described here is in production in several projects in our company as well as in public, for example at the Euregio JUG. It saved us from manually managing dependencies and copy and pasting recurring configuration.

There are few alternatives to Spring Boot Starters when developing shared modules for Spring Boot based applications. You could use annotation based configuration for them (@Service and the like) but than you'd have to trigger classpath scanning on a broader level. Manually writing additional XML or Java based configuration seems tedious and may lead to errors.

There are many useful starters listed in the spring-boot-starters-repository: Be it database integration, support for various template engines or even remote shells.

The goal of providing a starter for Wro4j was reached. You have to add the starter and a wro.xml like above containing Maven placeholders like @jquery.version@, which are also used for the WebJar dependencies and processed by the Maven resource plugin:

<resources>
   <resource>
		<directory>src/main/resources</directory>
		<filtering>true</filtering>
		<includes>
		    <include>wro.xml</include>
		</includes>
    </resource>
</resources>

You are free to configure the Wro4j processors and url inside application.properties:

wro4j.filterUrl = /owr
wro4j.managerFactory.preProcessors = removeSourceMaps, cssUrlRewriting, cssImport, cssMinJawr, semicolonAppender, jsMin
wro4j.cacheName = wro4j

and as a result you'll get minimized CSS and JavaScript resources that can be included with a single statement in your HTML pages.

Outlook

The starter here can be extended by creating custom Thymeleaf elements that either render one single HTML element or links to the original resources so that they can be easily debugged. The approach would be the same: Check if Thymeleaf is on the path, register additional tags and so on.

Sources

| Comments (1) »

02-May-16


Book: arc42 by example

title_pageHere’s a shameless self plug for the first book i’ve contributed too:

arc42 by example by Gernot Starke, Stefan Zörner and myself.

This book contains several real-world software architectures, documented with the practical arc42 template.

You will learn about the inner workings of a chess engine, a huge CRM system, a cool web system to track biking activities and an extremely small menu-bar application.

This is a direct result of a Gernots course for the iSAQB I attended at the end of last year. I’ve started to use arc42 and it really helped me a lot: For my pet project as well as “professional” software development and I’m more than happy that Gernot decided to include my example.

You can get our book at Leanpub.

Also, check out Gernots visit at the EuregioJUG: Rückblick zum aim42 Vortrag mit Gernot Starke (de).

| Comments (0) »

25-Apr-16


NetBeans settings for smooth debugging Spring Boot Applications

Here’s a short tip for a smoother debugging experience using NetBeans for developing Spring Boot Applications while using the Spring Boot Devtools:

Make sure you include the Devtools inside your dependency management:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

or

dependencies {
    compile("org.springframework.boot:spring-boot-devtools")
}

Devtools will automatically recognize that you’re running in an IDE respectively debugger and apart from a LiveReload server it’ll monitor your files and restart the context if necessary. To make this work you’ll to enable “Compile on save” in your NetBeans project (that is a project property) like this:


projectproperties

NetBeans however tries to apply all code changes in “Compile on save”-Mode. That works pretty well (not as good as JRebel, but then, NetBeans is free), but for example not for method deletions etc and you’ll end up with that error:


error-dialog

As you’re already opted to use Spring Boot Devtools, you won’t need that feature. The checkbox to turn it of is not a project specific setting but a global which you’ll find under the Java Debugger Tab inside Java options:


remove-apply-code

Turn “Apply code changes after save” of to enjoy a really smooth debugging and working on Spring Boot applications with NetBeans and Devtools.

| Comments (0) »

22-Apr-16


8 new features you’ll get with Spring Boot 1.4

Phil as a nice post about the improvements on testing in Spring Boot 1.4, check it out here: Testing improvements in Spring Boot 1.4.

I’d like to add a concrete example for those and some more, please have a look at the comments inside one of the most important controllers I’ve ever written 😉

Those are the dependencies you’ll need for the demo:

<dependencies>
    <dependency>
        <groupid>org.springframework.boot</groupid>
        <artifactid>spring-boot-starter-web</artifactid>
    </dependency>		
    <dependency>
        <groupid>org.springframework.boot</groupid>
        <artifactid>spring-boot-starter-test</artifactid>
        <scope>test</scope>
    </dependency>
    <dependency> 
        <groupid>org.springframework.restdocs</groupid>
        <artifactid>spring-restdocs-mockmvc</artifactid>	    	    
        <scope>test</scope>
    </dependency>
</dependencies>

Here’s the complete demo application:

import java.io.IOException;
import java.io.PrintStream;
import javax.servlet.http.HttpServletResponse;
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.env.Environment;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
 
@SpringBootApplication
public class Demo14Application {
 
    @Controller
    @RequestMapping("/api/banner")
    static class BannerController {
 
	// 1) If you didn't chose to disable banner in your application (and why
	//    should you? ;) ), the banner will be availabe as a bean in your 
	//    application
	// 2) banner.jpg :) You can replace banner.txt with a banner.jpg or banner.png
	//    to generate some nice ascii art during startup
	private final Banner banner;
 
	private final Environment environment;
 
	// 3) no more @Autowired on constructors necessary 
	//    when there's on a unique non-default constructor
	//    This also works on @Configuration classes, which didn't support 
	//    constructor injection at all so far.
	//    This comes actually from Spring Framework 4.3.RC1   
	public BannerController(final Banner banner, final Environment environment) {
	    this.banner = banner;
	    this.environment = environment;
	}
 
	// 4) More annotations ;) 
	//    Precomposed @GetMapping, @PostMapping, @RequestScope, @SessionScope etc
	@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
	public void get(final HttpServletResponse response) throws IOException {
	    try (PrintStream printStream = new PrintStream(response.getOutputStream())) {
		banner.printBanner(environment, BannerController.class, printStream);
	    }
	}
    }
 
    public static void main(String[] args) {
	SpringApplication.run(Demo14Application.class, args);
    }
}

And here’s the test. I really like the simplifications there:

import BannerController;
import java.io.PrintStream;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.Banner;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
 
import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.doAnswer;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
 
// 5) Easier named JUnit Runner ;)
@RunWith(SpringRunner.class)
// 6) Test slices of your application, in this case the Spring MVC slice
//    similiar options are available for @DataJpaTest and @JsonTest (incuding
//    JacksonTester).
@WebMvcTest(controllers = BannerController.class)	
// 7) Together with the above auto configuration comes autoconfiguration of
//    Spring REST docs, which removes the need for the JUnit rule and additional
//    configuration of the mock mvc instance
//    BTW: It seams that you can use addtitional formats like markdown for the
//    snippets
@AutoConfigureRestDocs(
	outputDir = "target/generated-snippets",
	uriHost = "banner-as-a-service.io",
	uriPort = 80
)
public class Demo14ApplicationTests {
 
    // 8) Spring Boot includes a @MockBean annotation that can be used to define
    //    a Mockito mock for a bean inside your ApplicationContext. You can use 
    //    the annotation to add new beans, or replace a single existing bean 
    //    definition. 
    @MockBean
    private Banner banner;
 
    @Autowired
    private MockMvc mockMvc;
 
    @Test
    public void testSomeMethod() throws Exception {
	doAnswer(invocation -> {
	    final PrintStream out = invocation.getArgumentAt(2, PrintStream.class);
	    out.write(bannerText.getBytes());
	    return null;
	}).when(banner).printBanner(anyObject(), anyObject(), anyObject());
 
	mockMvc
		.perform(
			get("/api/banner").accept(APPLICATION_JSON)
		)
		.andExpect(status().isOk())
		.andExpect(content().string(bannerText))
		.andDo(document("api/banner",
			preprocessRequest(prettyPrint()),
			preprocessResponse(prettyPrint())
		));
    }
 
    private final String bannerText
	    = " _____            _              ______ _____ _____ _____       _                \n"
	    + "/  ___|          (_)             | ___ \\  ___/  ___|_   _|     | |               \n"
	    + "\\ `--. _ __  _ __ _ _ __   __ _  | |_/ / |__ \\ `--.  | |     __| | ___   ___ ___ \n"
	    + " `--. \\ '_ \\| '__| | '_ \\ / _` | |    /|  __| `--. \\ | |    / _` |/ _ \\ / __/ __|\n"
	    + "/\\__/ / |_) | |  | | | | | (_| | | |\\ \\| |___/\\__/ / | |   | (_| | (_) | (__\\__ \\\n"
	    + "\\____/| .__/|_|  |_|_| |_|\\__, | \\_| \\_\\____/\\____/  \\_/    \\__,_|\\___/ \\___|___/\n"
	    + "      | |                  __/ |                                                 \n"
	    + "      |_|                 |___/                                                  \n"
	    + "          _ _   _        ___   _____ _____ _____ _____              _            \n"
	    + "         (_) | | |      / _ \\ /  ___/  __ \\_   _|_   _|            | |           \n"
	    + "__      ___| |_| |__   / /_\\ \\\\ `--.| /  \\/ | |   | |     __ _ _ __| |_          \n"
	    + "\\ \\ /\\ / / | __| '_ \\  |  _  | `--. \\ |     | |   | |    / _` | '__| __|         \n"
	    + " \\ V  V /| | |_| | | | | | | |/\\__/ / \\__/\\_| |_ _| |_  | (_| | |  | |_          \n"
	    + "  \\_/\\_/ |_|\\__|_| |_| \\_| |_/\\____/ \\____/\\___/ \\___/   \\__,_|_|   \\__|         \n"
	    + "                                                                                 \n"
	    + "                                                                                 ";
}

Have a look at the release notes or the updated reference documentation for more.

Read the complete article »

| Comments (1) »

17-Apr-16