Quantcast
Channel: Baeldung
Viewing all 3871 articles
Browse latest View live

Baeldung Weekly Review 18

$
0
0

I usually post about Dev stuff on Twitter - you can follow me there:

At the very beginning of 2014 I decided to track my reading habits and share the best stuff here, on Baeldung.

2014 has been quite the year, covering each week with a review. I’ve been doing a lot more reading to make sure I cover and curate stuff that has value and is actually worth reading.

Let me know in the comments if you’re finding my reviews interesting and useful.

Here we go…

1. Spring and Java

>> How does Hibernate READ_ONLY CacheConcurrencyStrategy work

Caching read-only entities is definitely a good idea – here’s a detailed breakdown of how to do it in Hibernate.

>> When JVMs Crash: How to Investigate the Root Cause of Your Toughest Errors

Identifying errors quickly is the hallmark of a mature, well thought-out system – and these are some useful techniques you can make use of to get there.

>> How Java 9 And Project Jigsaw May Break Your Code

A detailed and quite interesting look into what the Java 9 modularization work may mean for us in terms of compatibility.

>> Performance improvements in Eclipse Mars

The next version of Eclipse is not far away, and it’s cool to see that the IDE hasn’t been standing still.

>> Extracting Real Task from FutureTask

Some advanced concurrency goodness in this installment of the JavaSpecialists newsletter.

>> Java 8 Concurrency Tutorial: Synchronization and Locks

The second part of a solid intro to Java (8) Concurrency.

Also worth reading:

Webinars and presentations:

Time to upgrade:

2. Technical and Musings

>> The Precious Feature Design Meetings

Some meetings are actually a good way to use your time – and I definitely agree that technically focused design meetings can catch things early, stop developers from moving in the wrong direction and generally improve the health of your system.

>> The days are long but the decades are short

Practical, actionable advice? Not really – but well worth a read.

Also worth reading:

3. Comics

And my favorite Dilberts of the week:

>> A nail in a piece of wood

>> Our strategic plan

>> Cognitive disonance

4. Pick of the Week

Earlier this year I introduced the “Pick of the Week” section here in my “Weekly Review”. If you’re already on my email list – you got the pick already – hope you enjoyed it.

If not – you can share the review and unlock it right here:

I usually post about Dev stuff on Twitter - you can follow me there:


Baeldung Weekly Review 19

$
0
0

I usually post about Dev stuff on Twitter - you can follow me there:

At the very beginning of 2014 I decided to track my reading habits and share the best stuff here, on Baeldung.

2014 has been quite the year, covering each week with a review. I’ve been doing a lot more reading to make sure I cover and curate stuff that has value and is actually worth reading.

Let me know in the comments if you’re finding my reviews interesting and useful.

Here we go…

1. Spring and Java

>> Proposed schedule for JDK 9

The timeline of Java 9 has finally been set.

>> Call me maybe: Elasticsearch 1.5.0

This very detailed piece and its predecessor are both mandatory reading if you’re using Elasticsearch. Or working with distributed systems in general.

>> Getting Started With Gradle: Integration Testing

When you build your application, handling the integration tests well is oh-so important. If you’re using Gradle – have a read of this one.

Also worth reading:

Webinars and presentations:

Time to upgrade:

2. Technical

>> Evaluating persistent, replicated message queues

Good readin’ on message queues.

Also worth reading:

3. Musings

>> Talking About Money

If you’re just starting out or have been in the field for a decade or more – this piece is going to inform the way you think about being compensated for your work.

>> 8 Career Tips That Don’t Require Competence

Pragmatic advice on moving forward in your career faster than the “default”.

I’d definitely recommend reading this even if you can’t see identify with the “up or out” way of thinking, for some much needed and sometimes sobering context.

Also worth reading:

4. Comics

And my favorite Dilberts of the week:

>> We had to injure nine employees to meet the goal

>> Mordac, the preventer of information services

>> The magic cylinder

5. Pick of the Week

Earlier this year I introduced the “Pick of the Week” section here in my “Weekly Review”. If you’re already on my email list – you got the pick already – hope you enjoyed it.

If not – you can share the review and unlock it right here:

I usually post about Dev stuff on Twitter - you can follow me there:

A Reddit Application with Spring

$
0
0

I usually post about Spring stuff on Twitter - you can follow me there:

This Case Study follows the creation of a small web application with Spring from the ground up.

The app helps users manage their posting to Reddit by scheduling Posts, determining the best times to post, re-trying posts when they’re not immediately performing well, etc.

>> Authenticating with Reddit OAuth2 and Spring Security

A quick introduction to how to authenticate your application with Spring Security and the Reddit OAuth2 API.

>> Post a Link to the Reddit API

A basic interaction with the Reddit API – posting a new Link, using Spring Security OAuth2.

>> Schedule Post to Reddit with Spring

How to build a simple Spring app to schedule, edit and delete Posts using the Reddit API.

>> First Round of Improvements to the Reddit Application

The reddit app in this case study is coming along and slowly starting to become usable.

I usually post about Spring stuff on Twitter - you can follow me there:

First Round of Improvements to the Reddit Application

$
0
0

1. Overview

The Reddit web application Case Study is moving along nicely – and the small web application is shaping up and slowly becoming usable.

In this installment, we’re going to be making small improvements to the existing functionality – some externally facing, some not – and generally making the app better.

2. Setup Checks

Let’s start with some simple – but useful – checks that need to run when the application is bootstrapped:

@Autowired
private UserRepository repo;

@PostConstruct
public void startupCheck() {
    if (StringUtils.isBlank(accessTokenUri) || 
      StringUtils.isBlank(userAuthorizationUri) || 
      StringUtils.isBlank(clientID) || StringUtils.isBlank(clientSecret)) {
        throw new RuntimeException("Incomplete reddit properties");
    }
    repo.findAll();
}

Note how we’re using the @PostConstruct annotation here to hook into the lifecycle of the application, after the dependency injection process is over.

The simple goals are:

  • check if we have all the properties we need to access the Reddit API
  • check that the persistence layer is working (by issuing a simple findAll call)

If we fail – we do so early.

3. The “Too Many Requests” Reddit Problem

The Reddit API is aggressive in rate limiting requests that aren’t sending a unique “User-Agent“.

So – we need to add in this unique User-Agent header to our redditRestTemplate – using a custom Interceptor:

3.1. Create Custom Interceptor

Here is our custom interceptor – UserAgentInterceptor:

public class UserAgentInterceptor implements ClientHttpRequestInterceptor {

    @Override
    public ClientHttpResponse intercept(
      HttpRequest request, byte[] body, 
      ClientHttpRequestExecution execution) throws IOException {

        HttpHeaders headers = request.getHeaders();
        headers.add("User-Agent", "Schedule with Reddit");
        return execution.execute(request, body);
    }
}

3.2. Configure redditRestTemplate

We of course need to set this interceptor up with the redditRestTemplate we’re using:

@Bean
public OAuth2RestTemplate redditRestTemplate(OAuth2ClientContext clientContext) {
    OAuth2RestTemplate template = new OAuth2RestTemplate(reddit(), clientContext);
    List<ClientHttpRequestInterceptor> list = new ArrayList<ClientHttpRequestInterceptor>();
    list.add(new UserAgentInterceptor());
    template.setInterceptors(list);
    return template;
}

4. Configure H2 Database for Testing

Next – let’s go ahead and set up an in-memory DB – H2 – for testing. We need to add this dependency to our pom.xml:

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.187</version>
</dependency>

And define a persistence-test.properties:

## DataSource Configuration ###
jdbc.driverClassName=org.h2.Driver
jdbc.url=jdbc:h2:mem:oauth_reddit;DB_CLOSE_DELAY=-1
jdbc.user=sa
jdbc.pass=
## Hibernate Configuration ##
hibernate.dialect=org.hibernate.dialect.H2Dialect
hibernate.hbm2ddl.auto=update

5. Switch to Thymeleaf

JSP is out and Thymeleaf is in.

5.1. Modify pom.xml

First, we need to add these dependencies to our pom.xml:

<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf</artifactId>
    <version>2.1.4.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring4</artifactId>
    <version>2.1.4.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity3</artifactId>
    <version>2.1.2.RELEASE</version>
</dependency>

5.2. Create ThymeleafConfig

Next – a simple ThymeleafConfig:

@Configuration
public class ThymeleafConfig {
    @Bean
    public TemplateResolver templateResolver() {
        ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver();
        templateResolver.setPrefix("/WEB-INF/jsp/");
        templateResolver.setSuffix(".jsp");
        return templateResolver;
    }

    @Bean
    public SpringTemplateEngine templateEngine() {
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(templateResolver());
        templateEngine.addDialect(new SpringSecurityDialect());
        return templateEngine;
    }

    @Bean
    public ViewResolver viewResolver() {
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setTemplateEngine(templateEngine());
        viewResolver.setOrder(1);
        return viewResolver;
    }
}

And add it to our ServletInitializer:

@Override
protected WebApplicationContext createServletApplicationContext() {
    AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
    context.register(PersistenceJPAConfig.class, WebConfig.class, 
      SecurityConfig.class, ThymeleafConfig.class);
    return context;
}

5.3. Modify home.html

And a quick modification of the homepage:

<html>
<head>
<title>Schedule to Reddit</title>
</head>
<body>
<div class="container">
        <h1>Welcome, <small><span sec:authentication="principal.username">Bob</span></small></h1>
        <br/>
        <a href="posts" >My Scheduled Posts</a>
        <a href="post" >Post to Reddit</a>
        <a href="postSchedule" >Schedule Post to Reddit</a>
</div>
</body>
</html>

6. Logout

Now – let’s do some improvements that are actually visible to the end user of the application. We’ll start with logout.

We’re adding a simple logout option into the application by modifying our security config:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .....
        .and()
        .logout()
        .deleteCookies("JSESSIONID")
        .logoutUrl("/logout")
        .logoutSuccessUrl("/");
}

7. Subreddit Autocomplete

Next – let’s implement a simple autocomplete functionality for the filling it the subreddit; writing it manually is not a good way to go, since there’s a fair chance to get it wrong.

Let’s start with the client side:

<input id="sr" name="sr"/>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js"></script>
<script>
$(function() {
    $( "#sr" ).autocomplete({
        source: "/subredditAutoComplete"
    });
});
</script>

Simple enough. Now, the server side:

@RequestMapping(value = "/subredditAutoComplete")
@ResponseBody
public String subredditAutoComplete(@RequestParam("term") String term) {
    MultiValueMap<String, String> param = new LinkedMultiValueMap<String, String>();
    param.add("query", term);
    JsonNode node = redditRestTemplate.postForObject(
      "https://oauth.reddit.com//api/search_reddit_names", param, JsonNode.class);
    return node.get("names").toString();
}

8. Check If Link Is Already on Reddit

Next – let’s see how to check if a link is already submitted before to Reddit.

Here is our submissionForm.html:

<input name="url" />
<input name="sr">

<a href="#" onclick="checkIfAlreadySubmitted()">Check if already submitted</a>
<span id="checkResult" style="display:none"></span>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script>
$(function() {
    $("input[name='url'],input[name='sr']").focus(function (){
        $("#checkResult").hide();
    });
});
function checkIfAlreadySubmitted(){
    var url = $("input[name='url']").val();
    var sr = $("input[name='sr']").val();
    if(url.length >3 && sr.length > 3){
        $.post("checkIfAlreadySubmitted",{url: url, sr: sr}, function(data){
            var result = JSON.parse(data);
            if(result.length == 0){
                $("#checkResult").show().html("Not submitted before");
            }else{
                $("#checkResult").show().html(
               'Already submitted <b><a target="_blank" href="http://www.reddit.com'
               +result[0].data.permalink+'">here</a></b>');
            }
        });
    }
    else{
        $("#checkResult").show().html("Too short url and/or subreddit");
    }
}           
</script>

And here is our controller method:

@RequestMapping(value = "/checkIfAlreadySubmitted", method = RequestMethod.POST)
@ResponseBody
public String checkIfAlreadySubmitted(
  @RequestParam("url") String url, @RequestParam("sr") String sr) {
    JsonNode node = redditRestTemplate.getForObject(
      "https://oauth.reddit.com/r/" + sr + "/search?q=url:" + url + "&restrict_sr=on", JsonNode.class);
    return node.get("data").get("children").toString();
}

9. Deployment to Heroku

Finally – we’re going to set up deployment to Heroku – and use their free tier to power the sample app.

9.1. Modify pom.xml

First, we will need to add this Web Runner plugin to the pom.xml:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <version>2.3</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals><goal>copy</goal></goals>
            <configuration>
                <artifactItems>
                    <artifactItem>
                        <groupId>com.github.jsimone</groupId>
                        <artifactId>webapp-runner</artifactId>
                        <version>7.0.57.2</version>
                        <destFileName>webapp-runner.jar</destFileName>
                    </artifactItem>
                </artifactItems>
            </configuration>
        </execution>
    </executions>
</plugin>

Note – we will use Web Runner to launch our app on Heroku.

We’re going to be using Postgresql on Heroku – so we’ll need to have a dependency to the driver:

<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>9.4-1201-jdbc41</version>
</dependency>

9.2. The Procfile

We need to define the process that will run on the server in a Procfile – as follows:

web:    java $JAVA_OPTS -jar target/dependency/webapp-runner.jar --port $PORT target/*.war

9.3. Create Heroku app

To create a Heroku app from your project, we’ll simply:

cd path_to_your_project
heroku login
heroku create

9.4. Database Configuration

Next – we need to configure our database using our app’s Postgres database properties.

For example, here is persistence-prod.properties:

## DataSource Configuration ##
jdbc.driverClassName=org.postgresql.Driver
jdbc.url=jdbc:postgresql://hostname:5432/databasename
jdbc.user=xxxxxxxxxxxxxx
jdbc.pass=xxxxxxxxxxxxxxxxx

## Hibernate Configuration ##
hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
hibernate.hbm2ddl.auto=update

Note that we need to get the database details [host name, database name, user and password] form the Heroku dashborad.

Also – like in most cases, the keyword “user” is a reserved word in Postgres, so we need to change our “User” entity table name:

@Entity
@Table(name = "APP_USER")
public class User { .... }

9.5. Push code to Heoku

Now – let’s push code to Heroku:

git add .
git commit -m "init"
git push heroku master

10. Conclusion

In this forth part of our Case Study, the focus were small but important improvements. If you’ve been following along, you can see how this is shaping up to be an interesting and useful little app.

Baeldung Weekly Review 20

$
0
0

I usually post about Dev stuff on Twitter - you can follow me there:

At the very beginning of 2014 I decided to track my reading habits and share the best stuff here, on Baeldung.

2014 has been quite the year, covering each week with a review. I’ve been doing a lot more reading to make sure I cover and curate stuff that has value and is actually worth reading.

Let me know in the comments if you’re finding my reviews interesting and useful.

Here we go…

1. Spring and Java

>> To My Fellow Students:

A good intro to CS if there ever was one.

>> Modularizing the Client: Angular JS and Spring Security Part VII

The most recent installment of the series – looking at modularizing the app (client side) and implementing nicer, more user-friendly URLs than the Angular defaults.

>> First Release of JDeps Maven Plugin

A new cool plugin developed to address the “JDK 9 will break my code” problem – finding the APIs we might be using – that are going to no longer be available in Java 9.

>> Java Performance Impact by Dynamic Class Loading

An interesting performance problem in a production system – brought to the surface only with extensive stress testing and introspection tools.

Also worth reading:

Webinars and presentations:

Time to upgrade:

2. Technical

>> MicroservicePremium

The hype of microservices is reaching new heights – so knowing when to use this rather complex architectural style and when not to is of paramount importance. Here’s a good guide to help us navigate that decision process.

>> Log Collection With Graylog on AWS

A practical guide for setting up a log collection system on AWS, with Graylog.

I’ve traditionally used Logstash and the ELK stack for this very purpose, but Graylog looks like a solid option as well.

Regardless of the specific solution, having such a system in place is a must for a production application.

Also worth reading:

3. Musings

>> Appeasers, Crusaders, and Why Meetings Usually Suck

An introspective piece about the different types of personalities and natural inclinations when it comes to conflict.

I definitely nudged towards “crusader” tendencies when I was quite young and slowly started to understand how that’s not a sane and productive use of my time as I grew older. Now I naturally don’t engage in debate if I don’t have a personal stake in – but I do sometimes still have to check myself and be aware of a short burst of “I need to get in there” drive.

A fun read.

Also worth reading:

4. Comics

And my favorite Dilberts of the week:

>> What is an index fund?

>> Cycle of an ideea

>> Most problems go away

5. Pick of the Week

Earlier this year I introduced the “Pick of the Week” section here in my “Weekly Review”. If you’re already on my email list – you got the pick already – hope you enjoyed it.

If not – you can share the review and unlock it right here:

Rate Limiting Access to the Reddit API

$
0
0

1. Overview

In this quick article we’re going to continue improving our small Reddit app by rate limiting the way it has access to the live Reddit API.

The simple idea is that we want to make sure we don’t hit their API to much – otherwise Reddit will start blocking the requests. We’re going to make good use of the Guava RateLimiter to get there.

2. A Custom RedditTemplate

First, let’s create a Reddit template – a small client for the Reddit API that will put all the low level communication and API details in a single component:

@Component
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RedditTemplate {

    @Autowired
    @Qualifier("redditRestTemplate")
    private OAuth2RestTemplate redditRestTemplate;

    private RateLimiter rateLimiter;

    public RedditTemplate() {
        rateLimiter = RateLimiter.create(1);
    }
    
    public JsonNode getUserInfo() {
        rateLimiter.acquire();
        return redditRestTemplate.getForObject(
          "https://oauth.reddit.com/api/v1/me", JsonNode.class);
    }
    
    public JsonNode submitPost(MultiValueMap<String, String> params) {
        rateLimiter.acquire();
        return redditRestTemplate.postForObject(
          "https://oauth.reddit.com/api/submit", params, JsonNode.class);
    }
    
    public String needsCaptcha() {
        rateLimiter.acquire();
        return redditRestTemplate.getForObject(
          "https://oauth.reddit.com/api/needs_captcha.json", String.class);
    }
    
    public String getNewCaptcha() {
        rateLimiter.acquire();
        Map<String, String> param = new HashMap<String, String>();
        param.put("api_type", "json");
        return redditRestTemplate.postForObject(
          "https://oauth.reddit.com/api/new_captcha", param, String.class, param);
    }
    
    public OAuth2AccessToken getAccessToken() {
        rateLimiter.acquire();
        return redditRestTemplate.getAccessToken();
    }
}

A few interesting things are happening here.

First – we’re using the Session scope for this bean – simply so that each user/session in our app will get its own RedditTemplate instance.

Now – the OAuth2RestTemplate already has support for keeping credentials session scoped, but we’re going beyond that here and making the actual bean instance session scoped – so that we can also rate limit each user separately.

Which leads us to the actual rate limiting logic – simply put, we’re using the Guava RateLimiter to acquire a permit before letting the request through and hitting the live API.

3. The RedditController

Next – let’s start using this new RedditTemplate in the RedditContoller:

@Controller
public class RedditController {
    @Autowired
    private RedditTemplate redditTemplate;

    @Autowired
    private UserRepository userReopsitory;

    @RequestMapping("/login")
    public String redditLogin() {
        JsonNode node = redditTemplate.getUserInfo();
        
        loadAuthentication(node.get("name").asText(), 
          redditTemplate.getAccessToken());
        return "redirect:home.html";
    }

    @RequestMapping(value = "/submit", method = RequestMethod.POST)
    public String submit(Model model, @RequestParam Map<String, String> formParams) {
        MultiValueMap<String, String> param1 = constructParams(formParams);

        JsonNode node = redditTemplate.submitPost(param1);
        String responseMsg = parseResponse(node);
        model.addAttribute("msg", responseMsg);
        return "submissionResponse";
    }
}

4. Conclusion

In this part of the Case Study we added rate limiting to the Reddit application, to make sure we’re not blocked by the live API for to much activity.

This isn’t a theoretical problem either – but actually something that I ran into a couple of times using the app.

Caching in Spring

$
0
0

I usually post about Spring stuff on Twitter - you can follow me there:

1. The Cache Abstraction?

In this article we’re going to show how to use the Caching Abstraction in Spring – and generally improve the performance of your system.

We’ll enable simple caching for a some real-world method examples and we’ll discuss how we can practically improve the performance of these calls through smart cache management.

2. Enable Caching

To enable caching, Spring makes good use of annotations, much like enabling any other configuration level feature in the framework.

The caching feature can be declaratively enabled by simply adding the @EnableCaching annotation to any of the configuration classes:

@Configuration
@EnableCaching
public class MyAppConfig {

    // Your configuration code goes here.

}

You can of course enable cache management with XML configuration as well:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:cache="http://www.springframework.org/schema/cache"
    xsi:schemaLocation="
      http://www.springframework.org/schema/beans 
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/cache 
      http://www.springframework.org/schema/cache/spring-cache.xsd">

        <cache:annotation-driven />

</beans>

Using XML does enable more flexible options to configure caching – you can specify your own Cache-Manager, Cache-Resolver, Error-Handler and generally use more advanced customization options (Refer the javadoc for more details)

3. Use Caching With Annotations

Once you’ve enabled caching, the next step is to bind the caching behavior to the methods with declarative annotations.

3.1. @Cacheable

The simplest way to enable caching behavior for a method is to demarcate it with @Cacheable and parameterize it with the name of the cache where the results would be stored:

@Cacheable("addresses")
public String getAddress(Customer customer) {...}

The getCustomerName() call will first checks the cache addresses before actually invoking the method and then caching the result.

While in most cases, one cache is enough, the Spring framework also supports multiple caches to be passed as parameters:

@Cacheable("addresses", “directory”)
public String getAddress(Customer customer) {...}

In this case, if any of the cache contains the required result, the result is returned and the method is not invoked.

3.2. @CacheEvict

Now, what would be the problem with making all methods @Cacheable?

The problem is size – we don’t want to populate the cache with values that we don’t need often. Caches can grow quite large, quite fast, and we could be holding on to a lot of stale or unused data.

The @CacheEvict annotation is used to indicate the removal of one or more/all values – so that fresh values can be loaded into the cache again:

@CacheEvict(value="addresses", allEntries=true)
public String getAddress(Customer customer) {...}

Here, we’re using the additional parameter allEntries in conjunction with the cache to be emptied – to clear all the entries in the cache addresses and prepare it for new data.

3.3. @CachePut

While @CacheEvict reduces the overhead of looking up entries in a large cache by removing stale and unused entries, ideally, you want avoid evicting to much data out of the cache.

Instead, you’d want to selectively and intelligently update the entries whenever they’re altered.

With the @CachePut annotation, you can update the content of the cache without interfering the method execution. That is, the method would always be executed and the result cached.

@CachePut(value="addresses")
public String getAddress(Customer customer) {...}

The difference between @Cacheable and @CachePut is that @Cacheable will skip running the method, whereas @CachePut will actually run the method and then put its results in the cache.

3.4. @Caching

What if you want to use multiple annotations of the same type for caching a method. Look at the incorrect example below:

@CacheEvict("addresses")
@CacheEvict(value="directory", key=customer.name)
public String getAddress(Customer customer) {...}

The above code would fail to compile, since Java does not allow multiple annotation of the same type to be declared for a given method.

The workaround to the above issue would be:

@Caching(evict = { @CacheEvict("addresses"), @CacheEvict(value="directory", key="customer.name") })
public String getAddress(Customer customer) {...}

As shown in the code snippet above, you can group multiple caching annotations with @Caching, and use it to implement your own customized caching logic.

3.5 @CacheConfig

With the @CacheConfig annotation, you can streamline some of the cache configuration into a single place – at the class level – so that you don’t have to don’t have to declare things multiple times:

@CacheConfig("addresses")
public class CustomerDataService {

    @Cacheable
    public String getAddress(Customer customer) {...}

4. Conditional Caching

Sometimes, caching might not work well for a method in all situations.

For example – reusing our example from the @CachePut annotation – this will both execute the method as well as cache the results each and every time:

@CachePut(value="addresses")
public String getAddress(Customer customer) {...}

4.1 Condition Parameter

Now – if we want more control over when the annotation is active – @CachePut can be parametrized with a condition parameter that takes a SpEL expression to ensure that the results are cached based on evaluating that expression:

@CachePut(value="addresses", condition=”#customer.name=’Tom’”)
public String getAddress(Customer customer) {...}

4.2 Unless Parameter

We can also control the caching based on the output of the method rather than the input – via the unless parameter:

@CachePut(value="addresses", unless=”#result.length>64”)
public String getAddress(Customer customer) {...}

The above annotation would cache only those customers that have really long addresses (>64 characters).

It’s important to know that the condition and unless parameters can be used in conjunction with all the caching annotations.

This kind of conditional caching can prove quite useful for managing large results and customizing behavior based on input parameters instead of enforcing a generic behavior to all operations.

5. Declarative XML-based Caching

In case you don’t have access to your application’s source code or want to inject the caching behavior externally, you can also use declarative XML- based caching.

The above example can be be translated to:

<!-- the service that you wish to make cacheable -->
<bean id="customerDataService" 
  class="com.your.app.namespace.service.CustomerDataService"/>

<!-- define caching behavior -->
<cache:advice id="cachingBehavior" cache-manager="cacheManager">
    <cache:caching cache="addresses">
        <cache:cacheable method="getAddress" key="#customer.name"/>
    </cache:caching>
</cache:advice>

<!-- apply the behavior to all the implementations of CustomerDataService interface->
<aop:config>
    <aop:advisor advice-ref="cachingBehavior"
      pointcut="execution(*com.your.app.namespace.service.CustomerDataService.*(..))"/>
</aop:config>

6. Summary

In this article we discussed the basics of Caching in Spring and how to make good use of that abstraction with annotations.

I usually post about Spring stuff on Twitter - you can follow me there:


Baeldung Weekly Review 21

$
0
0

I usually post about Dev stuff on Twitter - you can follow me there:

At the very beginning of 2014 I decided to track my reading habits and share the best stuff here, on Baeldung.

2014 has been quite the year, covering each week with a review. I’ve been doing a lot more reading to make sure I cover and curate stuff that has value and is actually worth reading.

Let me know in the comments if you’re finding my reviews interesting and useful.

Here we go…

1. Spring and Java

>> How is Java 8 / Spring 4 Adoption Going?

I’m running a poll to see what Java 8 and Spring 4 are getting adopted across our industry. It only takes a couple of seconds to vote, so please do.

>> Testing an Angular Application: Angular JS and Spring Security Part VIII

This installment covers testing the single page Angular+Spring app with Jasmine, run these tests locally and then wire them up in a CI environment.

I’m sure I’ll give this one a try sooner rather than later.

>> How To Publish Software Artifacts To Maven Central

Nice practical overview of the steps you have to follow when releasing an artifact into Maven Central.

Also worth reading:

Webinars and presentations:

Time to upgrade:

2. Technical

>> Graphite vs. Grafana: Build the Best Monitoring Architecture for Your Application

The lay of the land for monitoring solutions – sending the data, receiving it and displaying it nicely.

Graphite is a nice tool and I’ve been making extensive use of it in the past, but the truth is – it’s damn ugly, especially after you play around with Kibana.

>> High Availability for Mere Mortals

This is a must-read if you haven’t spent a few years doing Ops work. Keep things simple, but does provide a good base to build on.

3. Musings

>> Just Don’t Hire 0x Engineers

I always like pragmatic thinking and practical advice, and this is both.

Also worth reading:

4. Comics

And my favorite Dilberts of the week:

>> Cure Me Of Optimism

>> Must Find Antidote

>> A Whiner

5. Pick of the Week

Earlier this year I introduced the “Pick of the Week” section here in my “Weekly Review”. If you’re already on my email list – you got the pick already – hope you enjoyed it.

If not – you can share the review and unlock it right here:


Baeldung Weekly Review 22

$
0
0

I usually post about Dev stuff on Twitter - you can follow me there:

At the very beginning of 2014 I decided to track my reading habits and share the best stuff here, on Baeldung.

2014 has been quite the year, covering each week with a review. I’ve been doing a lot more reading to make sure I cover and curate stuff that has value and is actually worth reading.

Let me know in the comments if you’re finding my reviews interesting and useful.

Here we go…

1. Spring and Java

>> Spring Framework 4.2 goes RC1

Yeah, Spring 4.2 has a first Release Candidate in the wild.

One thing I’m excited about is the better event support.

>> Cleaning ThreadLocals

When to use (and when to avoid) ThreadLocal, and of course how to clean it up and make sure it’s not holding on to anything it shouldn’t – when the thread returns to the pool.

>> Transforming Collections

Interesting tools for easily doing transformations of collections in Java.

>> The Ultimate JSON Library: JSON.simple vs GSON vs Jackson vs JSON

Benchmark data for some of the most widely used JSON Java libraries out there.

Also worth reading:

Webinars and presentations:

Time to upgrade:

2. Technical

>> the next level in Web APIs

A new, ongoing series on InfoQ on the state of APIs today, edited by Mike Amundsen. This is going to be a useful page to bookmark and come back to.

>> Yagni

A well structured, nuanced and practical deep-dive into Yagni, full of examples from Middle Earth. It’s most definitely worth a read (or two).

>> Demystified CQRS

A must read to better grasp CQRS – what it is and why it’s a very good idea to use it.

>> How can we Build Better Complex Systems? Containers, Microservices, and Continuous Delivery.

Some interesting notes on logically decoupling a complex system.

Also worth reading:

3. Musings

>> How to Make Your Open Plan Office Suck Less

Funny and also quite accurate drawings on how to intelligently lay out office space.

>> Seven stages of learning

This is a really interesting read that shines some light on the process of learning a new skill/language. I really how the learning stages are presented in a structured, logical cadence.

4. Comics

And my favorite Dilberts of the week:

>> Planning the pre-meeting

>> Designing a new logo

>> Over budget?

5. Pick of the Week

Earlier this year I introduced the “Pick of the Week” section here in my “Weekly Review”. If you’re already on my email list – you got the pick already – hope you enjoyed it.

If not – you can share the review and unlock it right here:

I usually post about Dev stuff on Twitter - you can follow me there:


Survey Results – Java 8, Spring 4 and Spring Boot Adoption

$
0
0

Java developers are a dynamic, ever changing bunch.

Over the past couple of weeks, I ran an industry survey here on Baeldung, to see how Java 8, Spring 4 and the newer Spring Boot are adopted and used in the industry.

We got over 1500 answers to the survey – leading to some very cool numbers:

>> CHECK OUT THE FULL SURVEY RESULTS

Java 8 Adoption

Let’s start with Java, where we numbers are as follows:

Java 8 adoption

  • 38% – Java 8

  • 48.5% – Java 7

  • 13.5% – Java 6

The community is clearly moving to Java 8 at a very fast pace – a Typesafe survey had Java 8 adoption hovering at around 26% back in October of 2014.

We are now at almost 40% adoption in May of 2015 – only 7 months later – which is quite impressive.

Spring 4 Adoption

Spring 4 has been released in December of 2013. Let’s see how much it has been adopted across the industry today:

Spring 4 adoption

  • 65% – Spring 4

  • 32.5% – Spring 3

  • 2.5% – Spring 2

The numbers are very clear – the ecosystem is moving to Spring 4.

Back in June of 2014 – almost 1 year ago – adoption looked quite different:

  • 37% – Spring 4
  • 57% – Spring 3
  • 6% – Spring 2

Spring Boot Adoption

Finally – Spring Boot was released back in April of 2014. Let’s see it has been adopted over this past year:

Spring Boot Adoption

  • 34% – Using Spring Boot Now

  • 66% – Not Yet

Conclusion

The earlier releases – Java 7 and Spring 3 – are clearly on a downward trend but do have a solid part of the market. What’s interesting though is that the older versions – Java 6 and Spring 2 are nearly gone or down to single digits.

Overall, it looks like the pace of adoption is strong for both Java 8 and the newer Spring technologies.

Spring Boot Actuator

$
0
0

1. Overview

In this article, we’re going to introduce Spring Boot Actuators and talk about some simple ways to work with them in production.

2. What Is An Actuator?

Actuators enable production-ready features to a Spring Boot application – without having to actually implement these things yourself.

They’re mainly used to expose different types of information about the running application – health, metrics, info, dump, env etc. And while these are no replacement for a production-grade monitoring solution – they’re a very good starting point.

2.1. How to Enable an Actuator

To start using the existing actuators in Boot – we’ll just need to add the spring-boot-actuator dependency to the pom:

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

3. Endpoints

Endpoints allow you to monitor the application and, in some cases – interact with it as well. Boot comes with many built-in endpoints and, like with pretty much anything in Spring – you can also roll your own.

Most endpoints are sensitive – meaning they’re not fully public – while a handful are not: /health and /info.

Here’s some of the most common endpoints Boot provides out of the box:

  • /health – Shows application health information (a simple ‘status’ when accessed over an unauthenticated connection or full message details when authenticated). It is sensitive by default.
  • /info – Displays arbitrary application info. Not sensitive by default.
  • /metrics – Shows ‘metrics’ information for the current application. It is also sensitive by default.
  • /trace – Displays trace information (by default the last few HTTP requests).

You can find the full list of existing endpoints over on the official docs.

4. Customizing Existing Endpoints

Each endpoint can be customized with properties using the following format: endpoints.[endpoint name].[property to customize]

Three properties are available:

  • id – by which this endpoint will be accessed over HTTP
  • enabled – if true then it can be accessed otherwise not
  • sensitive – if true then need authorization to show crucial information over HTTP

For example, add the following properties will customize the /beans endpoint:

endpoints.beans.id=springbeans
endpoints.beans.sensitive=false
endpoints.beans.enabled=true

4.1. /health Endpoint

The /health endpoint is used to check the health / status of the running application. It’s usually used by basic monitoring software to alert you if the production goes down.

By default only health information is shown to unauthorized access over HTTP:

{
    "status" : "UP"
}

This health information is collected from all the beans implementing HealthIndicator interface configured in your application context.

Some information returned by HealthIndicator is sensitive in nature – but you can configure endpoints.health.sensitive=false to expose the other information like diskspace, datasource etc.

4.2. A Custom HealthIndicator

You can also roll your own custom health indicator – which can collect any type of custom health data specific to the application and provide it to the /health endpoint:

@Component
public class HealthCheck implements HealthIndicator {
    @Override
    public Health health() {
        int errorCode = check(); // perform some specific health check
        if (errorCode != 0) {
            return Health.down().withDetail("Error Code", errorCode).build();
        }
        return Health.up().build();
    }
    
    public int check() {
    	// Your logic to check health
    	return 0;
    }
}

Here’s how the output would look like:

{
    "status" : "DOWN",
    "myHealthCheck" : {
        "status" : "DOWN",
        "Error Code" : 1,
        "Description" : "You custom MyHealthCheck endpoint is down"
     },
     "diskSpace" : {
         "status" : "UP",
         "free" : 209047318528,
         "threshold" : 10485760
     }
}

4.3. /info Endpoint

You can also customize the data shown by /info endpoint – for example:

info.app.name=Spring Sample Application
info.app.description=This is my first spring boot application
info.app.version=1.0.0

And the sample output:

{
    "app" : {
        "version" : "1.0.0",
        "description" : "This is my first spring boot application",
        "name" : "Spring Sample Application"
    }
}

4.4. /metrics Endpoint

The metrics endpoint is one of the more important endpoints as it gathers and publishes information about OS, JVM and Application level metrics; out of the box, we get things like memory, heap, processors, threads, classes loaded, classes unloaded, thread pools along with some HTTP metrics as well.

Here’s what the output of this endpoint looks like out of the box:

{
    "mem" : 193024,
    "mem.free" : 87693,
    "processors" : 4,
    "instance.uptime" : 305027,
    "uptime" : 307077,
    "systemload.average" : 0.11,
    "heap.committed" : 193024,
    "heap.init" : 124928,
    "heap.used" : 105330,
    "heap" : 1764352,
    "threads.peak" : 22,
    "threads.daemon" : 19,
    "threads" : 22,
    "classes" : 5819,
    "classes.loaded" : 5819,
    "classes.unloaded" : 0,
    "gc.ps_scavenge.count" : 7,
    "gc.ps_scavenge.time" : 54,
    "gc.ps_marksweep.count" : 1,
    "gc.ps_marksweep.time" : 44,
    "httpsessions.max" : -1,
    "httpsessions.active" : 0,
    "counter.status.200.root" : 1,
    "gauge.response.root" : 37.0
}

4.5. Custom Metric Data

There is actually support here for ‘gauge’ – single value data, and ‘counter’ – incrementing/decrementing data types of metrics. Let’s make use of some of this support to implement our own custom data into the /metrics endpoint.

For example – we’ll customize the login flow to record a successful and failed login attempt:

public interface LoginService {
    public boolean login(String userName, char[] password);
}

And the actual implementation, where we increase the relevant counters:

@Service
public class LoginServiceImpl implements LoginService {

    private CounterService counterService;
    
    @Autowired
    public LoginServiceImpl(CounterService counterService) {
        this.counterService = counterService;
    }
	
    public boolean login(String userName, char[] password) {
        boolean success;
        if (userName.equals("admin") && "secret".toCharArray().equals(password)) {
            counterService.increment("counter.login.success");
            success = true;
        }
        else {
            counterService.increment("counter.login.failure");
            success = false;
        }
        return success;
    }
}

Here’s what the output might look like:

{
    ...
    "counter.login.success" : 105,
    "counter.login.failure" : 12,
    ...
}

5. Create A New Endpoint

Besides using the existing endpoints provided by Spring Boot – you can also create an entirely new endpoint.

First – you’ll need to have the new endpoint implementation implement the Endpoint<T> interface:

@Component
public class CustomEndpoint implements Endpoint<List<String>> {
    
    public String getId() {
        return "customEndpoint";
    }

    public boolean isEnabled() {
        return true;
    }

    public boolean isSensitive() {
        return true;
    }

    public List<String> invoke() {
        // Custom logic to build the output
        List<String> messages = new ArrayList<String>();
        messages.add("This is message 1");
        messages.add("This is message 2");
        return messages;
    }
}

Output:

[ "This is message 1", "This is message 2" ]

6. A New Endpoint To List All Endpoints

Another interesting custom endpoint is one that exposes all available endpoints. To implement this one, you’ll need to extend the AbstractEndpoint<T> class:

@Component
public class ListEndpoints extends AbstractEndpoint<List<Endpoint>> {
    private List<Endpoint> endpoints;

    @Autowired
    public ListEndpoints(List<Endpoint> endpoints) {
        super("listEndpoints");
        this.endpoints = endpoints;
    }

    public List<Endpoint> invoke() {
        return this.endpoints;
    }
}

Here’s how the output will look like:

[ 
   {
       "id" : "customEndpoint",
       "enabled" : true,
       "sensitive" : true
    }, {
       "id" : "mappings",
       "sensitive" : true,
       "enabled" : true
    }, {
       "id" : "env",
       "sensitive" : true,
       "enabled" : true
    }, {
       "id" : "myhealth",
       "sensitive" : true,
       "enabled" : true,
       "timeToLive" : 1000
    }, {
       "id" : "beans",
       "sensitive" : true,
       "enabled" : true
    }, {
       "id" : "info",
       "sensitive" : false,
       "enabled" : true
    }, {
       "id" : "metrics",
       "sensitive" : true,
       "enabled" : true
    }, {
       "id" : "trace",
       "sensitive" : true,
       "enabled" : true
    }, {
       "id" : "dump",
       "sensitive" : true,
       "enabled" : true
    }, {
       "id" : "autoconfig",
       "sensitive" : true,
       "enabled" : true
    }, {
       "id" : "shutdown",
       "sensitive" : true,
       "enabled" : true
    }, {
       "id" : "configprops",
       "sensitive" : true,
       "enabled" : true
    } 
]

7. Further Customization

For security purposes, you might choose to expose the actuator endpoints over a non-standard port – the management.port property can easily be used to configure that.

You can also change the management.address property to restrict where the endpoints can be accessed from over the network:

management.port=8081
management.address=127.0.0.1
management.security.enabled=false

All the built-in endpoints except /info are sensitive by default. If the application is using Spring Security – you can secure these endpoints by defining the default security properties – user name, password and role – in application.properties file:

security.user.name=admin
security.user.password=secret
management.security.role=SUPERUSER

8. Conclusion

In this tutorial we had a first look at the interesting Actuator functionality provided by Spring Boot. We went over the existing endpoints available in the framework, augmented some of them with new, custom data and finally rolled our own to expose some entirely new kind of data.

Make It Easy to Schedule to Reddit

$
0
0

1. Overview

In this article, we’re going to continue the case study and add a new feature to the Reddit application, with the goal of making it much simpler to schedule articles.

Instead of slowly adding in every article by hand in the schedule UI, the user can now just have some favorite sites to post articles to Reddit from. We’re going to use RSS to do that.

2. The Site Entity

First – let’s create an entity to represent the site:

@Entity
public class Site {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false)
    private String url;

    @ManyToOne
    @JoinColumn(name = "user_id", nullable = false)
    private User user;
}

Note that the url field represents the URL of the RSS feed of the site.

3. The Repository and the Service

Next – lets create the repository to work with the new Site entity:

public interface SiteRepository extends JpaRepository<Site, Long> {
    List<Site> findByUser(User user);
}

And the service:

public interface ISiteService {

    List<Site> getSitesByUser(User user);

    void saveSite(Site site);

    Site findSiteById(Long siteId);

    void deleteSiteById(Long siteId);
}
@Service
public class SiteService implements ISiteService {

    @Autowired
    private SiteRepository repo;

    @Override
    public List<Site> getSitesByUser(User user) {
        return repo.findByUser(user);
    }

    @Override
    public void saveSite(Site site) {
        repo.save(site);
    }

    @Override
    public Site findSiteById(Long siteId) {
        return repo.findOne(siteId);
    }

    @Override
    public void deleteSiteById(Long siteId) {
        repo.delete(siteId);
    }
}

4. Load Data from the Feed

Now – let’s see how to load the articles details from website feed using the Rome Library.

We’ll first need to add Rome into our pom.xml:

<dependency>
    <groupId>com.rometools</groupId>
    <artifactId>rome</artifactId>
    <version>1.5.0</version>
</dependency>

And then use it to parse out the feeds of the sites:

public List<SiteArticle> getArticlesFromSite(Long siteId) {
    Site site = repo.findOne(siteId);
    return getArticlesFromSite(site);
}

List<SiteArticle> getArticlesFromSite(Site site) {
    List<SyndEntry> entries;
    try {
        entries = getFeedEntries(site.getUrl());
    } catch (Exception e) {
        throw new FeedServerException("Error Occurred while parsing feed", e);
    }
    return parseFeed(entries);
}

private List<SyndEntry> getFeedEntries(String feedUrl) 
  throws IllegalArgumentException, FeedException, IOException {
    URL url = new URL(feedUrl);
    SyndFeed feed = new SyndFeedInput().build(new XmlReader(url));
    return feed.getEntries();
}

private List<SiteArticle> parseFeed(List<SyndEntry> entries) {
    List<SiteArticle> articles = new ArrayList<SiteArticle>();
    for (SyndEntry entry : entries) {
        articles.add(new SiteArticle(
          entry.getTitle(), entry.getLink(), entry.getPublishedDate()));
    }
    return articles;
}

Finally – here’s the simple DTO that we’re going to use in the response:

public class SiteArticle {
    private String title;
    private String link;
    private Date publishDate;
}

5. Exception Handling

Notice how, when parsing the feed, we’re wrapping the entire parsing logic into a try-catch block and – in case of an exception (any exception) – we’re wrapping it and throwing it.

The reason for that is simple – we need to control the type of exception that gets thrown out of the parsing process – so that we can then handle that exception and provide a proper response to the client of the API:

@ExceptionHandler({ FeedServerException.class })
public ResponseEntity<Object> handleFeed(RuntimeException ex, WebRequest request) {
    logger.error("500 Status Code", ex);
    String bodyOfResponse = ex.getLocalizedMessage();
    return new ResponseEntity<Object>(bodyOfResponse, new HttpHeaders(), 
      HttpStatus.INTERNAL_SERVER_ERROR);
}

6. The Sites Page

6.1. Display the Sites

First, we will see how to show list of sites belonging to the logged in user:

@RequestMapping("/sites")
public String getUserSites(Model model) {
    List<Site> sites = service.getSitesByUser(getCurrentUser());
    model.addAttribute("sites", sites);
    return "siteListView";
}

And here is the very simple front end piece:

<table>
<thead><tr><th>Site Name</th><th>Feed URL</th><th>Actions</th></tr></thead>
    <tr th:each="site  : ${sites}">
    <td th:text="${site.getName()}"></td>
    <td th:text="${site.getUrl()}"></td>
    <td><a href="#"
      th:onclick="'javascript:deleteSite(\'' +${site.getId()}+ '\') '">Delete</a></td>
    </tr>
</table>
<script>
function deleteSite(id){
    $.ajax({ url: 'sites/'+id, type: 'DELETE', success: function(result) {
            window.location.href="sites"
        }
    });
}
</script>

6.2. Add A New Site

Next, let’s see how a user can create a new favorite site:

@RequestMapping(value = "/sites", method = RequestMethod.POST)
public String addSite(
  Model model, @RequestParam("url") String url, 
  @RequestParam("name") String name) throws ParseException {
    
    Site site = new Site();
    site.setName(name);
    site.setUrl(url);
    site.setUser(getCurrentUser());
    service.saveSite(site);
 
    List<Site> sites = service.getSitesByUser(getCurrentUser());
    model.addAttribute("sites", sites);
    
    return "siteListView";
}

And here is the – again very simple – client side:

<form th:action="@{/sites}" method="post">
    <input name="name"/>
    <input name="url" />
    <button type="submit" id="submitBut">Add Site</button>
</form>
<script>
$("#submitBut").click(function(event) {
    event.preventDefault();
    $.get("sites/isValidUrl?url="+$("#url").val(), function(data){
        if(data == true)
            $("form").submit();
        else
            alert("Invalid Feed Url");
    });
});
</script>

6.3. Validating a Feed

The validation of a new feed is a bit of an expensive operation – we need to actually retrieve the feed and parse it out to validate it fully.

So, in this first version – instead of having the validation as part of the create logic, it’s a separate operation in the API:

@RequestMapping(value = "/sites/isValidUrl")
@ResponseBody
public boolean isValidUrl(@RequestParam("url") final String url) {
    return service.isValidFeedUrl(url);
}

And the simple service method:

public boolean isValidFeedUrl(String feedUrl) {
    try {
        return getFeedEntries(feedUrl).size() > 0;
    } catch (Exception e) {
        return false;
    }
}

6.3. Delete a Site

Now, let’s see how the user can delete a site from their list of favorite sites:

@RequestMapping(value = "/sites/{id}", method = RequestMethod.DELETE)
@ResponseStatus(HttpStatus.OK)
public void deleteSite(@PathVariable("id") Long id) {
    service.deleteSiteById(id);
}

And here the – again very simple – service level method:

public void deleteSiteById(Long siteId) {
    repo.delete(siteId);
}

7. Schedule a Post from a Site

Now – let’s actually start using these sites and implement a basic way a user can schedule a new post to go out to Reddit not manually, but by loading in an article from an existing site.

7.1. Modify Scheduling Form

Let’s start with the client site and modify the existing schedulePostForm.html – we’re going to add:

<button data-target="#myModal">Load from My Sites</button>
<div id="myModal">
    <button id="dropdownMenu1">Choose Site</button><ul id="siteList"></ul>
    <button id="dropdownMenu2">Choose Article</button><ul id="articleList"></ul>
    <button onclick="load()">Load</button>
</div>

Note that we’ve added:

  • the button – “Load from my Sites” – to start the process
  • the pop-up – showing the list of sites and their articles

7.2. Load The Sites

Loading the sites in the popup is relatively easy with a bit of javascript:

$('#myModal').on('shown.bs.modal', function () {
    if($("#siteList").children().length > 0)
        return;
    $.get("sites/list", function(data){
        $.each(data, function( index, site ) {
            $("#siteList").append('<li><a href="#" onclick="loadArticles('+
              site.id+',\''+site.name+'\')">'+site.name+'</a></li>')
	});
    });
});

This of course hooks into a simple server side operation to load the sites:

@RequestMapping(value = "/sites/list")
@ResponseBody
public List<Site> getSitesList() {
    return service.getSitesByUser(getCurrentUser());
}

7.3. Load the Posts of a Site

When the user select a website from the list, we need to show the articles of that site – again with some basic js:

function loadArticles(siteID,siteName){
    $("#dropdownMenu1").html(siteName);
    $.get("sites/articles?id="+siteID, function(data){
        $("#articleList").html('');
        $("#dropdownMenu2").html('Choose Article');
    $.each(data, function( index, article ) {
        $("#articleList").append(
              '<li><a href="#" onclick="chooseArticle(\''+article.title+
              '\',\''+article.link+'\')"><b>'+article.title+'</b> <small>'+
              new Date(article.publishDate).toUTCString()+'</small></li>')
    });
    }).fail(function(error){ 
        alert(error.responseText); 
    });
}

And similarly to loading up the sites, we’re going to provide a simple server side operation to load up the articles of a site:

@RequestMapping(value = "/sites/articles")
@ResponseBody
public List<SiteArticle> getSiteArticles(@RequestParam("id") Long siteId) {
    return service.getArticlesFromSite(siteId);
}

Finally, we get the article data, fill in the form and schedule the article to go out to Reddit:

var title = "";
var link = "";
function chooseArticle(selectedTitle,selectedLink){
    $("#dropdownMenu2").html(selectedTitle);
    title=selectedTitle;
    link = selectedLink;
}
function load(){
    $("input[name='title']").val(title);
    $("input[name='url']").val(link);
}

8. Integration Tests

Finally – let’s test our SiteService on two different feed formats:

public class SiteIntegrationTest {

    private ISiteService service;

    @Before
    public void init() {
        service = new SiteService();
    }

    @Test
    public void whenUsingServiceToReadWordpressFeed_thenCorrect() {
        Site site = new Site("http://www.baeldung.com/feed/");
        List<SiteArticle> articles = service.getArticlesFromSite(site);

        assertNotNull(articles);
        for (SiteArticle article : articles) {
            assertNotNull(article.getTitle());
            assertNotNull(article.getLink());
        }
    }

    @Test
    public void whenUsingRomeToReadBloggerFeed_thenCorrect() {
        Site site = new Site("http://blogname.blogspot.com/feeds/posts/default");
        List<SiteArticle> articles = service.getArticlesFromSite(site);

        assertNotNull(articles);
        for (SiteArticle article : articles) {
            assertNotNull(article.getTitle());
            assertNotNull(article.getLink());
        }
    }
}

There’s clearly a bit of duplication here, but we can take care of that later.

9. Conclusion

In this installment we focused on a new, small feature – making the scheduling of the post to Reddit – simpler.

Retry to Submit to Reddit a Post Without Enough Traction

$
0
0

1. Overview

Posting to Reddit is a crap shoot. One post may do great and get a whole lot of attention while another, maybe better post will get no love at all. What about keeping an eye on these posts early on and – if they’re not getting enough traction – deleting them quickly and resubmitting them.

In this quick article we’re continuing the Reddit case study by implementing an interesting functionality – delete and resubmit a post if it’s not getting enough attention immediatly.

The simple goal is to allow the user to configure how many votes on Reddit are enough to consider the Post is getting enough traction to leave it up – inside a certain time interval.

2. More Reddit Permissions

First – we’ll need to ask for additional permissions from the Reddit API – specifically we need to edit posts.

So we’ll add “edit” scope to our Reddit Resource:

@Bean
public OAuth2ProtectedResourceDetails reddit() {
    AuthorizationCodeResourceDetails details = 
      new AuthorizationCodeResourceDetails();
    details.setScope(Arrays.asList("identity", "read", "submit", "edit"));
    ...
}

3. The Entity and The Repository

Now – let’s add the extra information in our Post entity:

@Entity
public class Post {
    ...
    private String redditID;
    private int noOfAttempts;
    private int timeInterval;
    private int minScoreRequired;
}

The fields:

  • redditID: Post ID on Reddit, to be used when checking the score and when deleting the post
  • noOfAttempts: Maximum number of resubmit tries (delete Post and resubmit it)
  • timeInterval: Time interval to check if the post is getting enough traction
  • minScoreRequired: Minimum score required to consider it successful enough to leave up

Next – let’s add some new operations into our PostRepository interface – in order to easily retrieve posts when we need to check check them:

public interface PostRepository extends JpaRepository<Post, Long> {

    List<Post> findBySubmissionDateBeforeAndIsSent(Date date, boolean sent);

    List<Post> findByUser(User user);

    List<Post> findByRedditIDNotNullAndNoOfAttemptsGreaterThan(int attempts);
}

4. A New Scheduled Task

Now – let’s define a new task – the re-submit task – into the scheduler:

@Scheduled(fixedRate = 3 * 60 * 1000)
public void checkAndReSubmitPosts() {
    List<Post> submitted = 
      postReopsitory.findByRedditIDNotNullAndNoOfAttemptsGreaterThan(0);
    for (Post post : submitted) {
        checkAndReSubmit(post);
    }
}

Each few minutes, this is simply iterating over the posts that are still in play – and, if they’re not getting enough traction, it deletes them and submits them again.

And here is checkAndReSubmit() method:

private void checkAndReSubmit(Post post) {
    try {
        checkAndReSubmitInternal(post);
    } catch (final Exception e) {
        logger.error("Error occurred while check post " + post.toString(), e);
    }
}
private void checkAndReSubmitInternal(Post post) {
    if (didIntervalPassed(post.getSubmissionDate(), post.getTimeInterval())) {
        int score = getPostScore(post.getRedditID());
        if (score < post.getMinScoreRequired()) {
            deletePost(post.getRedditID());
            resetPost(post);
        } else {
            post.setNoOfAttempts(0);
            postReopsitory.save(post);
        }
    }
}
private boolean didIntervalPassed(Date submissonDate, int postInterval) {
    long currentTime = new Date().getTime();
    long interval = currentTime - submissonDate.getTime();
    long intervalInMinutes = TimeUnit.MINUTES.convert(interval, TimeUnit.MILLISECONDS);
    return intervalInMinutes > postInterval;
}
private void resetPost(Post post) {
    long time = new Date().getTime();
    time += TimeUnit.MILLISECONDS.convert(post.getTimeInterval(), TimeUnit.MINUTES);
    post.setRedditID(null);
    post.setSubmissionDate(new Date(time));
    post.setSent(false);
    post.setSubmissionResponse("Not sent yet");
    postReopsitory.save(post);
}

We’ll also need to keep track of the redditID when first submitting it to Reddit:

private void submitPostInternal(Post post) {
    ...
    JsonNode node = redditRestTemplate.postForObject(
      "https://oauth.reddit.com/api/submit", param, JsonNode.class);
    JsonNode errorNode = node.get("json").get("errors").get(0);
    if (errorNode == null) {
        post.setRedditID(node.get("json").get("data").get("id").asText());
        post.setNoOfAttempts(post.getNoOfAttempts() - 1);
        ...
}

The logic here is also quite simple – we just save the id and decrease the number of attempts counter.

5. Get Reddit Post score

Now – let’s see how we can get the current score of the post from Reddit:

private int getPostScore(String redditId) {
    JsonNode node = redditRestTemplate.getForObject(
      "https://oauth.reddit.com/api/info?id=t3_" + redditId, JsonNode.class);
    int score = node.get("data").get("children").get(0).get("data").get("score").asInt();
    return score;
}

Note that:

  • We need the “read” scope when retrieving the post information from Reddit
  • We add “t3_” to the reddit id to obtain full name of the post

6. Delete The Reddit Post

Next – let’s see how delete Reddit post using its id:

private void deletePost(String redditId) {
    MultiValueMap<String, String> param = new LinkedMultiValueMap<String, String>();
    param.add("id", "t3_" + redditId);
    redditRestTemplate.postForObject(
      "https://oauth.reddit.com/api/del.json", param, JsonNode.class);
}

7. The RedditController

Now – let’s add the new info to the controller:

@RequestMapping(value = "/schedule", method = RequestMethod.POST)
public String schedule(Model model, 
  @RequestParam Map<String, String> formParams) throws ParseException {
    Post post = new Post();
    post.setTitle(formParams.get("title"));
    post.setSubreddit(formParams.get("sr"));
    post.setUrl(formParams.get("url"));
    post.setNoOfAttempts(Integer.parseInt(formParams.get("attempt")));
    post.setTimeInterval(Integer.parseInt(formParams.get("interval")));
    post.setMinScoreRequired(Integer.parseInt(formParams.get("score")));
    ....
}

8. The UI – Configure the Rules

Finally – let’s modify our very simple schedule form to add resubmit the new settings:

<label class="col-sm-3">Resubmit Settings</label>

<label>Number of Attempts</label> 
<select name="attempt">
    <option value="0" selected>None</option>
    <option value="2">2</option>
    <option value="3">3</option>
    <option value="4">4</option>
    <option value="5">5</option>
</select>

<label>Time interval</label>
<select name="interval">
    <option value="0" selected >None</option>
    <option value="45">45 minutes</option>
    <option value="60">1 hour</option>
    <option value="120">2 hours</option>
</select>

<label>Min score</label>
<input type="number"value="0" name="score" required/>

9. Conclusion

We’re continuing to improve what this simple app can do – we can now post to Reddit and – if the post isn’t getting enough traction quickly – we can have the system delete it and re-post to give it a better chance of performing.

Jackson – Working with Maps and nulls

$
0
0

I usually post about Jackson and JSON stuff on Twitter - you can follow me there:

1. Overview

In this quick article we’re going to look at a more advanced use-case of using Jackson – working with Maps that contain null values or null keys.

2. Ignore Nulls Values in a Map

Jackson has a simple but useful way of globally controlling what happens to null values when a Map gets serialized:

ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);

Now any null value in Map object serialized through this mapper is going to be ignored:

@Test
public void givenIgnoringNullValuesInMap_whenWritingMapObjectWithNullValue_thenIgnored() 
  throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();
    mapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);

    MyDto dtoObject1 = new MyDto();

    Map<String, MyDto> dtoMap = new HashMap<String, MyDto>();
    dtoMap.put("dtoObject1", dtoObject1);
    dtoMap.put("dtoObject2", null);

    String dtoMapAsString = mapper.writeValueAsString(dtoMap);

    assertThat(dtoMapAsString, containsString("dtoObject1"));
    assertThat(dtoMapAsString, not(containsString("dtoObject2")));
}

3. Serializing A Map With A Null Key

By default, Jackson doesn’t allow the serialization of a Map with a null key. If you do try to write out such a map, you’ll get the following exception:

c.f.j.c.JsonGenerationException: 
  Null key for a Map not allowed in JSON (use a converting NullKeySerializer?)
    at c.f.j.d.s.i.FailingSerializer.serialize(FailingSerializer.java:36)

The library is however flexible enough that you can define a custom, null key serializer and override the default behavior:

class MyDtoNullKeySerializer extends JsonSerializer<Object> {
    @Override
    public void serialize(Object nullKey, JsonGenerator jsonGenerator, SerializerProvider unused) 
      throws IOException, JsonProcessingException {
        jsonGenerator.writeFieldName("");
    }
}

Now the Map with the null key will work just fine – and the null key will be written as an empty String:

@Test
public void givenAllowingMapObjectWithNullKey_whenWriting_thenCorrect() 
throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();
    mapper.getSerializerProvider().setNullKeySerializer(new MyDtoNullKeySerializer());

    MyDto dtoObject = new MyDto();
    dtoObject.setStringValue("dtoObjectString");
 
    Map<String, MyDto> dtoMap = new HashMap<String, MyDto>();
    dtoMap.put(null, dtoObject);

    String dtoMapAsString = mapper.writeValueAsString(dtoMap);

    assertThat(dtoMapAsString, containsString("\"\""));
    assertThat(dtoMapAsString, containsString("dtoObjectString"));
}

4. Ignore Null Fields

Besides Maps, Jackson provides a lot of configuration and flexibility for ignoring / working with null fields in general. You can check out this tutorial to see exactly how that works.

5. Conclusion

Serializing a Map object is common enough that we need a library that’s able to handle the nuances of the serialization process well. Jackson provides a few handy customization options to help you shape the output of this serialization process quite well.

It also provides a lot of solid ways to work with collections in a more general sense.

The implementation of all these examples and code snippets can be found in my github project – this is an Eclipse based project, so it should be easy to import and run as it is.

I usually post about Jackson and JSON stuff on Twitter - you should follow me there:


Jackson – Decide What Fields Get Serialized/Deserializaed

$
0
0

1. Overview

In this article we’ll explore the various ways we can control if a field is serialized / deserialized by Jackson or not.

2. A Public Field

The simplest way to make sure a field is both serializable and deserializable is to make it public.

Let’s declare a simple class with a public, a package-private and a private

public class MyDtoAccessLevel {
    private String stringValue;
    int intValue;
    protected float floatValue;
    public boolean booleanValue;
    // NO setters or getters
}

Out of the four fields of the class, just the public booleanValue will be serialized to JSON by default:

@Test
public void givenDifferentAccessLevels_whenPublic_thenSerializable() 
  throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();

    MyDtoAccessLevel dtoObject = new MyDtoAccessLevel();

    String dtoAsString = mapper.writeValueAsString(dtoObject);
    assertThat(dtoAsString, not(containsString("stringValue")));
    assertThat(dtoAsString, not(containsString("intValue")));
    assertThat(dtoAsString, not(containsString("floatValue")));

    assertThat(dtoAsString, containsString("booleanValue"));
}

3. A Getter Makes a Non-Public Field Serializable and Deserializable

Now, another simple way to make a field – especially a non-public field – serializable, is to add a getter for it:

public class MyDtoWithGetter {
    private String stringValue;
    private int intValue;

    public String getStringValue() {
        return stringValue;
    }
}

We now expect the stringValue field to be serializable, while the other private field not to be, as it has no getter:

@Test
public void givenDifferentAccessLevels_whenGetterAdded_thenSerializable() 
  throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();

    MyDtoGetter dtoObject = new MyDtoGetter();

    String dtoAsString = mapper.writeValueAsString(dtoObject);
    assertThat(dtoAsString, containsString("stringValue"));
    assertThat(dtoAsString, not(containsString("intValue")));
}

Unintuitively, the getter also makes the private field deserializable as well – because once it has a getter, the field is considered a property.

Let’s look at how that work:

@Test
public void givenDifferentAccessLevels_whenGetterAdded_thenDeserializable() 
  throws JsonProcessingException, JsonMappingException, IOException {
    String jsonAsString = "{\"stringValue\":\"dtoString\"}";
    ObjectMapper mapper = new ObjectMapper();
    MyDtoWithGetter dtoObject = mapper.readValue(jsonAsString, MyDtoWithGetter.class);

    assertThat(dtoObject.getStringValue(), equalTo("dtoString"));
}

4. A Setter Makes a Non-Public Field Deserializable Only

We saw how the getter made the private field both serializable and deserializable. On the other hand, a setter will only mark the non-public field as deserializable:

public class MyDtoWithSetter {
    private int intValue;

    public void setIntValue(int intValue) {
        this.intValue = intValue;
    }

    public int accessIntValue() {
        return intValue;
    }
}

As you can see, the private intValue field only has a setter this time. We do have a way to access the value, but that’s not a standard getter.

The unmarshalling process for intValue should work correctly:

@Test
public void givenDifferentAccessLevels_whenSetterAdded_thenDeserializable() 
  throws JsonProcessingException, JsonMappingException, IOException {
    String jsonAsString = "{\"intValue\":1}";
    ObjectMapper mapper = new ObjectMapper();

    MyDtoSetter dtoObject = mapper.readValue(jsonAsString, MyDtoSetter.class);

    assertThat(dtoObject.anotherGetIntValue(), equalTo(1));
}

And as we mentioned, the setter should only make the field deserializable, but not serializable:

@Test
public void givenDifferentAccessLevels_whenSetterAdded_thenStillNotSerializable() 
  throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();

    MyDtoSetter dtoObject = new MyDtoSetter();

    String dtoAsString = mapper.writeValueAsString(dtoObject);
    assertThat(dtoAsString, not(containsString("intValue")));
}

5. Make All Fields Globally Serializable

In some cases where, for example, you might not actually be able to modify the source code directly – we need to configure the way Jackson deals with non-public fields from the outside.

That kind of global configuration can be done at the ObjectMapper level, by turning on the AutoDetect function to use either public fields or getter/setter methods for serialization, or maybe turn on serialization for all fields:

ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE);
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);

The following test case verifies all member fields (including non-public) of MyDtoAccessLevel are serializable:

@Test
public void givenDifferentAccessLevels_whenSetVisibility_thenSerializable() 
  throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();
    mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);

    MyDtoAccessLevel dtoObject = new MyDtoAccessLevel();

    String dtoAsString = mapper.writeValueAsString(dtoObject);
    assertThat(dtoAsString, containsString("stringValue"));
    assertThat(dtoAsString, containsString("intValue"));
    assertThat(dtoAsString, containsString("booleanValue"));
}

6. Change the Name of a Property on Serialization/Deserialization

Going beyond controlling which field gets serialized or deserializaed, you can also have control over the way a fields maps to JSON and back. I covered this configuration here.

7. Ignore a Field On Serialization or Deserialization

Following this tutorial, we have a guide for how to ignore a field completely on serialization and deserialization.

However, sometimes we only need to ignore the field on either, but not on both. Jackson is flexible enough to accommodate this interesting usecase as well.

The following example shows a User object which contains sensitive password information which shouldn’t be serialized to JSON.

To get there, we simply add the @JsonIgnore annotation on the getter of the password, and enable deserialization for the field by applying the @JsonProperty annotation on the setter:

@JsonIgnore
public String getPassword() {
    return password;
}
@JsonProperty
public void setPassword(String password) {
    this.password = password;
}

Now the password information won’t be serialized to JSON:

@Test
public void givenFieldTypeIsIgnoredOnlyAtSerialization_whenUserIsSerialized_thenIgnored() 
  throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();

    User userObject = new User();
    userObject.setPassword("thePassword");

    String userAsString = mapper.writeValueAsString(userObject);
    assertThat(userAsString, not(containsString("password")));
    assertThat(userAsString, not(containsString("thePassword")));
}

However, the JSON containing the password will be successfully deserialized to the User object:

@Test
public void givenFieldTypeIsIgnoredOnlyAtSerialization_whenUserIsDeserialized_thenCorrect() 
  throws JsonParseException, JsonMappingException, IOException {
    String jsonAsString = "{\"password\":\"thePassword\"}";
    ObjectMapper mapper = new ObjectMapper();

    User userObject = mapper.readValue(jsonAsString, User.class);

    assertThat(userObject.getPassword(), equalTo("thePassword"));
}

8. Conclusion

This tutorial goes over the basic of how Jackson chooses which field is serialized/deserialized and which gets ignored in the process and of course how to get fully control over it.

You may also get to the next step in understanding Jackson 2 by diving deeper with articles such as ignoring a fielddeserializing a JSON Array to a Java Array or Collection.

The implementation of all these examples and code snippets can be found in my github project – this is an Eclipse based project, so it should be easy to import and run as it is.


Configuring A Spring Boot Application

$
0
0

I usually post about Spring stuff on Twitter - you can follow me there:

1. Overview

Spring Boot can do a lot of things; in this tutorial, we’re going to go over a few of the more interesting configuration options in Boot.

2. The Port Number

In main standalone applications, the main HTTP port defaults to 8080; we can easily configure Boot to use a different port:

server.port=8083

And for, yaml based configuration:

server:
    port: 8083

We can also programatically customize the server port:

@Component
public class CustomizationBean implements EmbeddedServletContainerCustomizer {
    @Override
    public void customize(ConfigurableEmbeddedServletContainer container) {
        container.setPort(8083);
    }
}

3. The Context Path

By default, the context path is “/”. If that’s not ideal and you need to change it – to something like /app_name, here’s the quick and simple way to do it via properties:

server.contextPath=/springbootapp

And for yaml based configuration:

server:
    contextPath:/springbootapp

Finally – the change can be done programatically as well:

@Component
public class CustomizationBean implements EmbeddedServletContainerCustomizer {
    @Override
    public void customize(ConfigurableEmbeddedServletContainer container) {
        container.setContextPath("/springbootapp");
    }
}

4. The While Label Error Page

Spring Boot automatically registers a BasicErrorController bean if you don’t specify any custom implementation in the configuration. 

However, this default controller can of course be configured:

public class MyCustomErrorController implements ErrorController {
    private static final String PATH = "/error";
    
    @RequestMapping(value=PATH)
    public String error() {
        return "Error heaven";
    }

    @Override
    public String getErrorPath() {
        return PATH;
    }
}

5. Customize The Error Messages

Boot provides /error mappings by default to handle errors in a sensible way.

If you want to configure more specific error pages, there’s good support for a uniform Java DSL to customize error handling:

@Component
public class CustomizationBean implements EmbeddedServletContainerCustomizer {
    @Override
    public void customize(ConfigurableEmbeddedServletContainer container) {        
        container.addErrorPages(new ErrorPage(HttpStatus.BAD_REQUEST, "/400"));
        container.addErrorPages(new ErrorPage("/errorHeaven"));
    }
}

Here, we specifically handled Bad Request to match the /400 path and all others to match the common path.

And a very simple /errorHeaven implementation:

@RequestMapping("/errorHeaven")
String errorHeaven() {
    return "You have reached the heaven of errors!!!";
}

Output:

You have reached the heaven of errors!!!

6. Shut Down A Boot Application Programatically

You can programatically shut down a Boot app with the help of SpringApplication. This has a static exit() method that takes two arguments: the ApplicationContext and an ExitCodeGenerator:

@Autowired
public void shutDown(ExecutorServiceExitCodeGenerator exitCodeGenerator) {
    SpringApplication.exit(applicationContext, exitCodeGenerator);
}

It’s through this utility method that we can shut down the app.

7. Configure the Logging Levels

You can easily tune the logging levels in a Boot application; Starting with version 1.2.0 onwards, you can configure the log level in the main properties file:

logging.level.org.springframework.web: DEBUG
logging.level.org.hibernate: ERROR

And just as with a standard Spring app – you can activate different logging systems like Logback, log4j, log4j2, etc by adding their customized XML or properties file in the classpath and defining the libraries in the pom.

8. Register a New Servlet

If you’re deploying the application with the help of the embedded server, you can register new Servlets in a Boot application by exposing them as beans from conventional config:

@Bean
public HelloWorldServlet helloWorld() {
    return new HelloWorldServlet();
}

Alternatively you can use a ServletRegistrationBean:

@Bean
public SpringHelloServletRegistrationBean servletRegistrationBean() {
    SpringHelloServletRegistrationBean bean = new SpringHelloServletRegistrationBean(
      new SpringHelloWorldServlet(), "/springHelloWorld/*");
    bean.setLoadOnStartup(1);
    bean.addInitParameter("message", "SpringHelloWorldServlet special message");
    return bean;
}

9. Configure Jetty or Undertow in Boot Application

The Spring Boot starters generally uses Tomcat as the default embedded server. If that needs to be changed – you can exclude the Tomcat dependency and include Jetty or Undertow instead:

Configuring Jetty

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
@Bean
public JettyEmbeddedServletContainerFactory  jettyEmbeddedServletContainerFactory() {
    JettyEmbeddedServletContainerFactory jettyContainer = 
      new JettyEmbeddedServletContainerFactory();
    
    jettyContainer.setPort(9000);
    jettyContainer.setContextPath("/springbootapp");
    return jettyContainer;
}

Configuring Undertow

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
@Bean
public UndertowEmbeddedServletContainerFactory embeddedServletContainerFactory() {
    UndertowEmbeddedServletContainerFactory factory = 
      new UndertowEmbeddedServletContainerFactory();
    
    factory.addBuilderCustomizers(new UndertowBuilderCustomizer() {
        @Override
        public void customize(io.undertow.Undertow.Builde builder) {
            builder.addHttpListener(8080, "0.0.0.0");
        }
    });
    
    return factory;
}

10. Conclusion

In this quick article, we went over some of the more interesting and useful Spring Boot configuration options.

There are of course many, many more options to configure and tune a Boot app to your needs in the reference docs – these are just some of the more useful I found.

I usually post about Spring stuff on Twitter - you can follow me there:


Baeldung Weekly Review 23

$
0
0

I usually post about Dev stuff on Twitter - you can follow me there:

At the very beginning of 2014 I decided to track my reading habits and share the best stuff here, on Baeldung.

2014 has been quite the year, covering each week with a review. I’ve been doing a lot more reading to make sure I cover and curate stuff that has value and is actually worth reading.

Let me know in the comments if you’re finding my reviews interesting and useful.

Here we go…

1. Spring and Java

>> Spring Data JPA Tutorial: Introduction to Query Methods

A solid part of Petri’s Spring Data JPA series – introducing query methods.

>> Spring 4 and Java 8 adoption

Thoughts on the Spring 4, Spring Boot and Java 8 adoption data from the survey I ran last week. The adoption trend is definitely looking good.

>> The Next Twenty Years of Java: Where We’ve Been and Where We’re Going

An interesting history of Java.

>> Understanding Garbage Collection Logs

A very quick very to the point dive into GC logs – definitely worth a read and a bookmark.

Also worth reading:

Webinars and presentations:

Time to upgrade:

2. Technical

>> The Role of a Senior Developer

An introspective, well though out read on learning and working in our industry.

I would say it’s the “must read” of this week if there weren’t so many other damn good reads this week as well. Like the next one.

>> Utter Disregard for Git Commit History

Working with git? Then have a read. I learned a lot from this one.

>> Improving Your Craft with Static Analysis

A pragmatic intro to the why of static analysis; if you’re not already leveraging something like PMD, Checkstyle or Findbugs, this is some good motivation to get started.

>> MonolithFirst

A great read if you’re considering using a Microservice artchitecture on a green field project, or even have Microservices anywhere in your view.

>> Replacing booleans and enums with dates

Why storing not only current state state – but temporal data as well – is a generally a good idea.

Event Sourcing takes that concepts to the next level, but it’s definitely interesting to see that – event without it – there’s so much value in storing that data.

Also worth reading:

3. Musings

>> Career Advancement for the Low Price of Your Soul

A deep-dive into a concept I knew almost nothing about – self-improvement opportunities (courses, school) payed for by the company, with strings attached. Not cool.

>> Breaking down successful pair programming

A very interesting read on pair programming – more nuanced than the usual on the topic.

4. Comics

And my favorite Dilberts of the week:

>> Then the little one slapped me

>> Another stellar week

>> We pay ourselves to hose ourselves

5. Pick of the Week

Earlier this year I introduced the “Pick of the Week” section here in my “Weekly Review”. If you’re already on my email list – you got the pick already – hope you enjoyed it.

If not – you can share the review and unlock it right here:

A Guide to Jackson Annotations

$
0
0

I usually post about Jackson and JSON stuff on Twitter - you can follow me there:

1. Overview

In this article we’ll do a deep dive into Jackson Annotations.

We’ll see how to use the existing annotations, how to create custom ones and finally – how to disable them.

2. Jackson Serialization Annotations

First – let’s take a look at the serialization annotations.

2.1. @JsonAnyGetter

The @JsonAnyGetter annotation allows the flexibility of using a Map field as standard properties.

Here’s a quick example – the ExtendableBean entity has the name property and a set of extendable attributes in form of key/value pairs:

public class ExtendableBean {
    public String name;
    private Map<String, String> properties;

    @JsonAnyGetter
    public Map<String, String> getProperties() {
        return properties;
    }
}

When we serialize an instance of this entity, we get all the key-values in the Map as standard, plain properties:

{
    "name":"My bean",
    "attr2":"val2",
    "attr1":"val1"
}

And here how the serialization of this entity looks like in practice:

@Test
public void whenSerializingUsingJsonAnyGetter_thenCorrect()
  throws JsonProcessingException {
    ExtendableBean bean = new ExtendableBean("My bean");
    bean.add("attr1", "val1");
    bean.add("attr2", "val2");

    String result = new ObjectMapper().writeValueAsString(bean);
    assertThat(result, containsString("attr1"));
    assertThat(result, containsString("val1"));
}

2.2. @JsonGetter

The @JsonGetter annotation is an alternative to @JsonProperty annotation to mark the specified method as a getter method.

In the following example – we specify the method getTheName() as the getter method of name property of MyBean entity:

public class MyBean {
    public int id;
    private String name;

    @JsonGetter("name")
    public String getTheName() {
        return name;
    }
}

And here’s how this works in practice:

@Test
public void whenSerializingUsingJsonGetter_thenCorrect()
  throws JsonProcessingException {
    MyBean bean = new MyBean(1, "My bean");

    String result = new ObjectMapper().writeValueAsString(bean);
    assertThat(result, containsString("My bean"));
    assertThat(result, containsString("1"));
}

2.3. @JsonPropertyOrder

The @JsonPropertyOrder annotation is used to specify the order of properties on serialization.

Let’s set a custom order for the properties of a MyBean entity:

@JsonPropertyOrder({ "name", "id" })
public class MyBean {
    public int id;
    public String name;
}

And here is the output of serialization:

{
    "name":"My bean",
    "id":1
}

And a simple test:

@Test
public void whenSerializingUsingJsonPropertyOrder_thenCorrect()
  throws JsonProcessingException {
    MyBean bean = new MyBean(1, "My bean");

    String result = new ObjectMapper().writeValueAsString(bean);
    assertThat(result, containsString("My bean"));
    assertThat(result, containsString("1"));
}

2.4. @JsonRawValue

@JsonRawValue is used to instruct the Jackson to serialize a property exactly as is.

In the following example – we use @JsonRawValue to embed some custom JSON as value of an entity:

public class RawBean {
    public String name;

    @JsonRawValue
    public String json;
}

The output of serializing the entity is:

{
    "name":"My bean",
    "json":{
        "attr":false
    }
}

And a simple test:

@Test
public void whenSerializingUsingJsonRawValue_thenCorrect()
  throws JsonProcessingException {
    RawBean bean = new RawBean("My bean", "{"attr":false}");

    String result = new ObjectMapper().writeValueAsString(bean);
    assertThat(result, containsString("My bean"));
    assertThat(result, containsString("{"attr":false}"));
}

2.5. @JsonValue

@JsonValue indicates a single method that should be used to serialize the entire instance.

For example in an enum – we annotate the getName with @JsonValue so that any such entity is serialized via it’s name:

public enum TypeEnumWithValue {
    TYPE1(1, "Type A"), TYPE2(2, "Type 2");

    private Integer id;
    private String name;

    @JsonValue
    public String getName() {
        return name;
    }
}

Our test:

@Test
public void whenSerializingUsingJsonValue_thenCorrect()
  throws JsonParseException, IOException {
    String enumAsString = 
      new ObjectMapper().writeValueAsString(TypeEnumWithValue.TYPE1);

    assertThat(enumAsString, is(""Type A""));
}

2.6. @JsonRootName

The @JsonRootName annotation is used – if wrapping is enabled – to specify the name of the root wrapper to be used.

Wrapping means that instead of serializing a User to something like:

{
    "id": 1,
    "name": "John"
}

It’s going to be wrapped like this:

{
    "User": {
        "id": 1,
        "name": "John"
    }
}

So, let’s look at an example – we’re going to use the @JsonRootName annotation to indicate the name of this potential wrapper entity:

@JsonRootName(value = "user")
public class UserWithRoot {
    public int id;
    public String name;
}

By default, the name of the wrapper would be the name of the class – UserWithRoot. By using the annotation, we get the cleaner looking user: 

@Test
public void whenSerializingUsingJsonRootName_thenCorrect()
  throws JsonProcessingException {
    UserWithRoot user = new User(1, "John");

    ObjectMapper mapper = new ObjectMapper();
    mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
    String result = mapper.writeValueAsString(user);

    assertThat(result, containsString("John"));
    assertThat(result, containsString("user"));
}

Here is the output of serialization:

{
    "user":{
        "id":1,
        "name":"John"
    }
}

2.7. @JsonSerialize

@JsonSerialize is used to indicate a custom serializer will be used to marshall the entity.

Let’s look at a quick example – we’re going to use @JsonSerialize to serialize the eventDate property with a CustomDateSerializer:

public class Event {
    public String name;

    @JsonSerialize(using = CustomDateSerializer.class)
    public Date eventDate;
}

Here’s the simple custom Jackson serializer:

public class CustomDateSerializer extends JsonSerializer<Date> {

    private static SimpleDateFormat formatter = 
      new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");

    @Override
    public void serialize(Date value, JsonGenerator gen, SerializerProvider arg2) 
      throws IOException, JsonProcessingException {
        gen.writeString(formatter.format(value));
    }
}

Let’s use these in a test:

@Test
public void whenSerializingUsingJsonSerialize_thenCorrect()
  throws JsonProcessingException, ParseException {
    SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");

    String toParse = "20-12-2014 02:30:00";
    Date date = df.parse(toParse);
    Event event = new Event("party", date);

    String result = new ObjectMapper().writeValueAsString(event);
    assertThat(result, containsString(toParse));
}

3. Jackson Deserialization Annotations

Next – let’s explore the Jackson deserialization annotations.

3.1. @JsonCreator

The @JsonCreator annotation is used to tune the constructor/factory used in deserialization.

It’s very helpful when we need to deserialize some JSON that doesn’t exactly match the target entity we need to get.

Let’s look at an example; say we need to deserialize the following JSON:

{
    "id":1,
    "theName":"My bean"
}

However, there is no theName field in our target entity – there is only a name field. Now – we don’t want to change the entity itself – we just need a little more control over the unmarshalling process – by annotating the constructor with @JsonCreator and using the @JsonProperty annotation as well:

public class BeanWithCreator {
    public int id;
    public String name;

    @JsonCreator
    public BeanWithCreator(
      @JsonProperty("id") int id, 
      @JsonProperty("theName") String name) {
        this.id = id;
        this.name = name;
    }
}

Let’s see this in action:

@Test
public void whenDeserializingUsingJsonCreator_thenCorrect()
  throws JsonProcessingException, IOException {
    String json = "{"id":1,"theName":"My bean"}";

    BeanWithCreator bean = 
      new ObjectMapper().reader(BeanWithCreator.class).readValue(json);
    assertEquals("My bean", bean.name);
}

3.2. @JacksonInject

@JacksonInject is used to indicate a property that will get its value from injection and not from the JSON data.

In the following example – we use @JacksonInject to inject the property id:

public class BeanWithInject {
    @JacksonInject
    public int id;
    
    public String name;
}

And here’s how this works:

@Test
public void whenDeserializingUsingJsonInject_thenCorrect()
  throws JsonProcessingException, IOException {
    String json = "{"name":"My bean"}";
    
    InjectableValues inject = new InjectableValues.Std().addValue(int.class, 1);
    BeanWithInject bean = new ObjectMapper().reader(inject)
                            .forType(BeanWithInject.class)
                            .readValue(json);
    
    assertEquals("My bean", bean.name);
    assertEquals(1, bean.id);
}

3.3. @JsonAnySetter

@JsonAnySetter allows you the flexibility of using a Map as standard properties. On de-serialization, the properties from JSON will simply be added to the map.

Let’s see how this works – we’ll use @JsonAnySetter to deserialize the entity ExtendableBean:

public class ExtendableBean {
    public String name;
    private Map<String, String> properties;

    @JsonAnySetter
    public void add(String key, String value) {
        properties.put(key, value);
    }
}

This is the JSON we need to deserialize:

{
    "name":"My bean",
    "attr2":"val2",
    "attr1":"val1"
}

And here’s how this all ties in together:

@Test
public void whenDeserializingUsingJsonAnySetter_thenCorrect()
  throws JsonProcessingException, IOException {
    String json = "{"name":"My bean","attr2":"val2","attr1":"val1"}";

    ExtendableBean bean = 
      new ObjectMapper().reader(ExtendableBean.class).readValue(json);
    
    assertEquals("My bean", bean.name);
    assertEquals("val2", bean.getProperties().get("attr2"));
}

3.4. @JsonSetter

@JsonSetter is an alternative to @JsonProperty – used to mark the a method as a setter method.

This is super useful when we need to read some JSON data but the target entity class doesn’t exactly match that data and so we need to tune the process to make it fit.

In the following example, we’ll specify the method setTheName() as the setter of the name property in our MyBean entity:

public class MyBean {
    public int id;
    private String name;

    @JsonSetter("name")
    public void setTheName(String name) {
        this.name = name;
    }
}

Now, when we need to unmarshall some JSON data – this works perfectly well:

@Test
public void whenDeserializingUsingJsonSetter_thenCorrect()
  throws JsonProcessingException, IOException {
    String json = "{"id":1,"name":"My bean"}";

    MyBean bean = 
      new ObjectMapper().reader(MyBean.class).readValue(json);
    assertEquals("My bean", bean.getTheName());
}

3.5. @JsonDeserialize

@JsonDeserialize is used to indicate the use of a custom deserializer.

Let’s see how that plays out – we’ll use @JsonDeserialize to deserialize the eventDate property with the CustomDateDeserializer:

public class Event {
    public String name;

    @JsonDeserialize(using = CustomDateDeserializer.class)
    public Date eventDate;
}

Here’s the custom deserializer:

public class CustomDateDeserializer extends JsonDeserializer<Date> {

    private static SimpleDateFormat formatter = 
      new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");

    @Override
    public Date deserialize(JsonParser jsonparser, DeserializationContext context) 
      throws IOException, JsonProcessingException {
        String date = jsonparser.getText();
        try {
            return formatter.parse(date);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }
}

And here’s the back-to-back test:

@Test
public void whenDeserializingUsingJsonDeserialize_thenCorrect()
  throws JsonProcessingException, IOException {
    String json = "{"name":"party","eventDate":"20-12-2014 02:30:00"}";

    SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
    Event event = new ObjectMapper().reader(Event.class).readValue(json);
    
    assertEquals("20-12-2014 02:30:00", df.format(event.eventDate));
}

4. Jackson Property Inclusion Annotations

4.1. @JsonIgnoreProperties

@JsonIgnoreProperties – one of the most common annotations in Jackson – is used to mark a property or a list of properties to be ignored at the class level.

Let’s go over a quick example ignoring the property id from serialization:

@JsonIgnoreProperties({ "id" })
public class BeanWithIgnore {
    public int id;
    public String name;
}

And here’s the test making sure the ignore happens:

@Test
public void whenSerializingUsingJsonIgnoreProperties_thenCorrect()
  throws JsonProcessingException, IOException {
    BeanWithIgnore bean = new BeanWithIgnore(1, "My bean");

    String result = new ObjectMapper().writeValueAsString(bean);
    
    assertThat(result, containsString("My bean"));
    assertThat(result, not(containsString("id")));
}

4.2. @JsonIgnore

The @JsonIgnore annotation is used to mark a property to be ignored at the field level.

Let’s use @JsonIgnore to ignore the property id from serialization:

public class BeanWithIgnore {
    @JsonIgnore
    public int id;

    public String name;
}

And the test making sure that id was successfully ignored:

@Test
public void whenSerializingUsingJsonIgnore_thenCorrect()
  throws JsonProcessingException, IOException {
    BeanWithIgnore bean = new BeanWithIgnore(1, "My bean");

    String result = new ObjectMapper().writeValueAsString(bean);
    
    assertThat(result, containsString("My bean"));
    assertThat(result, not(containsString("id")));
}

4.3. @JsonIgnoreType

@JsonIgnoreType is used to mark all properties of annotated type to be ignored.

Let’s use the annotation to mark all properties of type Name to be ignored:

public class User {
    public int id;
    public Name name;

    @JsonIgnoreType
    public static class Name {
        public String firstName;
        public String lastName;
    }
}

Here’s the simple test making sure the ignore works correctly:

@Test
public void whenSerializingUsingJsonIgnoreType_thenCorrect()
  throws JsonProcessingException, ParseException {
    User.Name name = new User.Name("John", "Doe");
    User user = new User(1, name);

    String result = new ObjectMapper().writeValueAsString(user);

    assertThat(result, containsString("1"));
    assertThat(result, not(containsString("name")));
    assertThat(result, not(containsString("John")));
}

4.4. @JsonInclude

@JsonInclude is used to exclude properties with empty/null/default values.

Let’s look at an example – excluding nulls from serialization:

@JsonInclude(Include.NON_NULL)
public class MyBean {
    public int id;
    public String name;
}

Here’s the full test:

public void whenSerializingUsingJsonInclude_thenCorrect()
  throws JsonProcessingException, IOException {
    MyBean bean = new MyBean(1, null);

    String result = new ObjectMapper().writeValueAsString(bean);
    
    assertThat(result, containsString("1"));
    assertThat(result, not(containsString("name")));
}

4.5. @JsonAutoDetect

@JsonAutoDetect is used to override the default semantics of which properties are visible and which are not.

Let’s take a look at how the annotation can be very helpful with a simple example – let’s enable serializing private properties:

@JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class PrivateBean {
    private int id;
    private String name;
}

And the test:

@Test
public void whenSerializingUsingJsonAutoDetect_thenCorrect()
  throws JsonProcessingException, IOException {
    PrivateBean bean = new PrivateBean(1, "My bean");

    String result = new ObjectMapper().writeValueAsString(bean);
    
    assertThat(result, containsString("1"));
    assertThat(result, containsString("My bean"));
}

5. Jackson Polymorphic Type Handling Annotations

Next – let’s take a look at Jackson polymorphic type handling annotations:

  • @JsonTypeInfo is used to indicate details of what type information is included in serialization
  • @JsonSubTypes is used to indicate sub-types of annotated type
  • @JsonTypeName is used to define logical type name to use for annotated class

Let’s look over a more complex example and use all three – @JsonTypeInfo, @JsonSubTypes and @JsonTypeName – to serialize/desrialize the entity Zoo:

public class Zoo {
    public Animal animal;

    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, 
                  include = As.PROPERTY, 
                  property = "type")
    @JsonSubTypes({
        @JsonSubTypes.Type(value = Dog.class, name = "dog"),
        @JsonSubTypes.Type(value = Cat.class, name = "cat")
    })
    public static class Animal {
        public String name;
    }

    @JsonTypeName("dog")
    public static class Dog extends Animal {
        public double barkVolume;
    }

    @JsonTypeName("cat")
    public static class Cat extends Animal {
        boolean likesCream;
        public int lives;
    }
}

When we do serialization:

@Test
public void whenSerializingPolymorphic_thenCorrect()
  throws JsonProcessingException, IOException {
    Zoo.Dog dog = new Zoo.Dog("lacy");
    Zoo zoo = new Zoo(dog);

    String result = new ObjectMapper().writeValueAsString(zoo);

    assertThat(result, containsString("type"));
    assertThat(result, containsString("dog"));
}

Here’s what serializing the Zoo instance with the Dog will result in:

{
    "animal": {
        "type": "dog",
        "name": "lacy",
        "barkVolume": 0
    }
}

Now for de-serialization – let’s start from the following JSON input:

{
    "animal":{
        "name":"lacy",
        "type":"cat"
    }
}

And let’s see how that gets unmarshalled to a Zoo instance:

@Test
public void whenDeserializingPolymorphic_thenCorrect()
throws JsonProcessingException, IOException {
    String json = "{"animal":{"name":"lacy","type":"cat"}}";

    Zoo zoo = new ObjectMapper().reader()
                                .withType(Zoo.class)
                                .readValue(json);

    assertEquals("lacy", zoo.animal.name);
    assertEquals(Zoo.Cat.class, zoo.animal.getClass());
}

6. Jackson General Annotations

Next – let’s discuss some of Jackson more general annotations.

6.1. @JsonProperty

@JsonProperty is used to indicate the property name in JSON.

Let’s go over the annotation with a simple example – and use @JsonProperty to serialize/desrialize the property name when we’re dealing with non-standard getters and setters:

public class MyBean {
    public int id;
    private String name;

    @JsonProperty("name")
    public void setTheName(String name) {
        this.name = name;
    }

    @JsonProperty("name")
    public String getTheName() {
        return name;
    }
}

Our test:

@Test
public void whenUsingJsonProperty_thenCorrect()
  throws IOException {
    MyBean bean = new MyBean(1, "My bean");

    String result = new ObjectMapper().writeValueAsString(bean);
    
    assertThat(result, containsString("My bean"));
    assertThat(result, containsString("1"));

    MyBean resultBean = new ObjectMapper().reader(MyBean.class)
                                          .readValue(result);
    assertEquals("My bean", resultBean.getTheName());
}

6.2. @JsonFormat

The @JsonFormat annotation can be used to specify a format when serializing Date/Time values.

In the following example – we use @JsonFormat to control the format of the property eventDate:

public class Event {
    public String name;

    @JsonFormat(
      shape = JsonFormat.Shape.STRING,
      pattern = "dd-MM-yyyy hh:mm:ss")
    public Date eventDate;
}

And here’s the test:

@Test
public void whenSerializingUsingJsonFormat_thenCorrect()
  throws JsonProcessingException, ParseException {
    SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
    df.setTimeZone(TimeZone.getTimeZone("UTC"));

    String toParse = "20-12-2014 02:30:00";
    Date date = df.parse(toParse);
    Event event = new Event("party", date);
    
    String result = new ObjectMapper().writeValueAsString(event);
    
    assertThat(result, containsString(toParse));
}

6.3. @JsonUnwrapped

@JsonUnwrapped is used to define that a value should be unwrapped / flattened when serialized.

Let’s see exactly how that works; we’ll use the annotation to unwrap the property name:

public class UnwrappedUser {
    public int id;

    @JsonUnwrapped
    public Name name;

    public static class Name {
        public String firstName;
        public String lastName;
    }
}

Let’s now serialize an instance of this class:

@Test
public void whenSerializingUsingJsonUnwrapped_thenCorrect()
  throws JsonProcessingException, ParseException {
    UnwrappedUser.Name name = new UnwrappedUser.Name("John", "Doe");
    UnwrappedUser user = new UnwrappedUser(1, name);

    String result = new ObjectMapper().writeValueAsString(user);
    
    assertThat(result, containsString("John"));
    assertThat(result, not(containsString("name")));
}

Here’s how the output looks like – the fields of the static nested class unwrapped along with the other field:

{
    "id":1,
    "firstName":"John",
    "lastName":"Doe"
}

6.4. @JsonView

@JsonView is used to indicate the View in which the property will be included for serialization/deserialization.

An example will show exactly how that works – we’ll use @JsonView to serialize an instance of Item entity.

Let’s start with the views:

public class Views {
    public static class Public {}
    public static class Internal extends Public {}
}

And now here’s the Item entity, using the views:

public class Item {
    @JsonView(Views.Public.class)
    public int id;

    @JsonView(Views.Public.class)
    public String itemName;

    @JsonView(Views.Internal.class)
    public String ownerName;
}

Finally – the full test:

@Test
public void whenSerializingUsingJsonView_thenCorrect()
  throws JsonProcessingException {
    Item item = new Item(2, "book", "John");

    String result = new ObjectMapper().writerWithView(Views.Public.class)
                                      .writeValueAsString(item);

    assertThat(result, containsString("book"));
    assertThat(result, containsString("2"));
    assertThat(result, not(containsString("John")));
}

6.5. @JsonManagedReference, @JsonBackReference

The @JsonManagedReference and @JsonBackReference annotations are used to handle parent/child relationships, and work around loops.

In the following example – we use @JsonManagedReference and @JsonBackReference to serialize our ItemWithRef entity:

public class ItemWithRef {
    public int id;
    public String itemName;

    @JsonManagedReference
    public UserWithRef owner;
}

Our UserWithRef entity:

public class UserWithRef {
    public int id;
    public String name;

    @JsonBackReference
    public List<ItemWithRef> userItems;
}

And the test:

@Test
public void whenSerializingUsingJacksonReferenceAnnotation_thenCorrect()
  throws JsonProcessingException {
    UserWithRef user = new UserWithRef(1, "John");
    ItemWithRef item = new ItemWithRef(2, "book", user);
    user.addItem(item);

    String result = new ObjectMapper().writeValueAsString(item);

    assertThat(result, containsString("book"));
    assertThat(result, containsString("John"));
    assertThat(result, not(containsString("userItems")));
}

6.6. @JsonIdentityInfo

@JsonIdentityInfo is used to indicate that Object Identity is to be used when serializing/deserializing values – for instance to deal with infinite recursion type of problems.

In the following example – we have an ItemWithIdentity entity with a bidirectional realtionship with the UserWithIdentity entity:

@JsonIdentityInfo(
  generator = ObjectIdGenerators.PropertyGenerator.class,
  property = "id")
public class ItemWithIdentity {
    public int id;
    public String itemName;
    public UserWithIdentity owner;
}

And the UserWithIdentity entity:

@JsonIdentityInfo(
  generator = ObjectIdGenerators.PropertyGenerator.class,
  property = "id")
public class UserWithIdentity {
    public int id;
    public String name;
    public List<ItemWithIdentity> userItems;
}

Now, let’s see how the infinite recursion problem is handled:

@Test
public void whenSerializingUsingJsonIdentityInfo_thenCorrect()
  throws JsonProcessingException {
    UserWithIdentity user = new UserWithIdentity(1, "John");
    ItemWithIdentity item = new ItemWithIdentity(2, "book", user);
    user.addItem(item);

    String result = new ObjectMapper().writeValueAsString(item);

    assertThat(result, containsString("book"));
    assertThat(result, containsString("John"));
    assertThat(result, containsString("userItems"));
}

Here’s the full output of the serialized item and user:

{
    "id": 2,
    "itemName": "book",
    "owner": {
        "id": 1,
        "name": "John",
        "userItems": [
            2
        ]
    }
}

6.7. @JsonFilter

The @JsonFilter annotation specifies a filter to be used during serialization.

Let’s take a look at an example; first, we define the entity and we point to the filter:

@JsonFilter("myFilter")
public class BeanWithFilter {
    public int id;
    public String name;
}

Now, in the full test, we define the filter – which excludes all other properties except name from serialization:

@Test
public void whenSerializingUsingJsonFilter_thenCorrect()
  throws JsonProcessingException {
    BeanWithFilter bean = new BeanWithFilter(1, "My bean");

    FilterProvider filters = 
      new SimpleFilterProvider().addFilter("myFilter", 
      SimpleBeanPropertyFilter.filterOutAllExcept("name"));

    String result = new ObjectMapper().writer(filters)
                                      .writeValueAsString(bean);

    assertThat(result, containsString("My bean"));
    assertThat(result, not(containsString("id")));
}

7. Custom Jackson Annotation

Next – let’s see how to create a custom Jackson annotation; we can make use of the @JacksonAnnotationsInside annotation – as in the following example:

@Retention(RetentionPolicy.RUNTIME)
    @JacksonAnnotationsInside
    @JsonInclude(Include.NON_NULL)
    @JsonPropertyOrder({ "name", "id", "dateCreated" })
    public @interface CustomAnnotation {}

Now, if we use the new annotation on an entity:

@CustomAnnotation
public class BeanWithCustomAnnotation {
    public int id;
    public String name;
    public Date dateCreated;
}

We can see how it does combine the existing annotations into a simpler, custom one that we can use as a shorthand:

@Test
public void whenSerializingUsingCustomAnnotation_thenCorrect()
  throws JsonProcessingException {
    BeanWithCustomAnnotation bean = 
      new BeanWithCustomAnnotation(1, "My bean", null);

    String result = new ObjectMapper().writeValueAsString(bean);

    assertThat(result, containsString("My bean"));
    assertThat(result, containsString("1"));
    assertThat(result, not(containsString("dateCreated")));
}

The output of the serialization process:

{
    "name":"My bean",
    "id":1
}

8. Jackson MixIn Annotations

Next – let’s see how to use Jackson MixIn annotations.

Let’s use the MixIn annotations to – for example – ignore properties of type String:

public class User {
    public int id;
    public String name;
}
@JsonIgnoreType
public class MyMixInForString {}

Let’s see this in action:

@Test
public void whenSerializingUsingMixInAnnotation_thenCorrect()
  throws JsonProcessingException {
    User user = new User(1, "John");
    String result = new ObjectMapper().writeValueAsString(user);
    assertThat(result, containsString("John"));

    ObjectMapper mapper = new ObjectMapper();
    mapper.addMixInAnnotations(String.class, MyMixInForString.class);
    result = mapper.writeValueAsString(user);
    assertThat(result, not(containsString("John")));
}

9. Disable Jackson Annotation

Finally – let’s see how we can disable all Jackson annotations.We can do this by disabling the MapperFeature.USE_ANNOTATIONS as in the following example:

@JsonInclude(Include.NON_NULL)
@JsonPropertyOrder({ "name", "id" })
public class MyBean {
    public int id;
    public String name;
}

Now, after disabling annotations, these should have no effect and the defaults of the library should apply:

@Test
public void whenDisablingAllAnnotations_thenAllDisabled()
  throws JsonProcessingException, IOException {
    MyBean bean = new MyBean(1, null);

    ObjectMapper mapper = new ObjectMapper();
    mapper.disable(MapperFeature.USE_ANNOTATIONS);
    String result = mapper.writeValueAsString(bean);
    
    assertThat(result, containsString("1"));
    assertThat(result, containsString("name"));
}

The result of serialization before disabling annotations:

{"id":1}

The result of serialization after disabling annotations:

{
    "id":1,
    "name":null
}

10. Conclusion

This tutorial has been a deep-dive into Jackson annotations, just scratching the surface of the kind of flexibility you can get using them properly.

The implementation of all these examples and code snippets can be found in my github project – this is an Eclipse based project, so it should be easy to import and run as it is.

I usually post about Jackson and JSON stuff on Twitter - you should follow me there:


Baeldung Weekly Review 24

$
0
0

I usually post about Dev stuff on Twitter - you can follow me there:

At the very beginning of 2014 I decided to track my reading habits and share the best stuff here, on Baeldung.

2014 has been quite the year, covering each week with a review. I’ve been doing a lot more reading to make sure I cover and curate stuff that has value and is actually worth reading.

Let me know in the comments if you’re finding my reviews interesting and useful.

Here we go…

1. Spring and Java

>> CORS support in Spring Framework

Very nice support for CORS getting here in Spring 4.2 – this is going to help quite a bit.

>> 12 Tools That I Use for Writing Unit and Integration Tests

This is THE testing toolbox in the Java ecosystem. Lots of good libraries to pick up from here.

>> ETags and Spring Data REST

A good, to the point intro to ETags in the upcoming version of Spring Data REST. Cool stuff.

Also worth reading:

Webinars and presentations:

Time to upgrade:

2. Technical

>> Don’t start with a monolith

If your goal for a new project is a microservice architecture, you have a foundational question to ask yourself as you get going. Should I start on easy monolith route and break it apart into microservices and responsibilities later, or will it be better to enforce these clear responsibilities from the start?

This piece, along with the one from last week, is a nuanced and well-balanced discussion of this choice.

It has been my experience that both approaches work given the team has very high discipline. If I were to make the choice today, and there would be no question that the goal is the microservice architecture – then I would start with microservices from the very beginning.

>> Why we need Resilient Software Design

A good, short interview on system resiliency at scale.

>> Comparing Application Deployment: 2005 vs. 2015

The Times They Are a-Changin’.

Also worth reading:

3. Musings

>> Speaker style bingo: 10 presentation anti-patterns

Great insights into public speaking, specifically about getting better at it.

Also worth reading:

4. Comics

And my favorite Dilberts of the week:

>> Never been cubicled…

>> Training Customers

>> You’ll have to do some actual work

5. Pick of the Week

Earlier this year I introduced the “Pick of the Week” section here in my “Weekly Review”. If you’re already on my email list – you got the pick already – hope you enjoyed it.

If not – you can share the review and unlock it right here:

I usually post about Dev stuff on Twitter - you can follow me there:


Second Round of Improvements to the Reddit Application

$
0
0

1. Overview

Let’s continue our ongoing Reddit web app case study with a new round of improvements, with the goal of making the application more user friendly and easier to use.

2. Scheduled Posts Pagination

First – let’s list the scheduled posts with pagination, to make the whole thing easier to look at and understand.

2.1. The Paginated Operations

We’ll use Spring Data to generate the operation we need, making good use of the Pageable interface to retrieve user’s scheduled posts:

public interface PostRepository extends JpaRepository<Post, Long> {
    Page<Post> findByUser(User user, Pageable pageable);
}

And here is our controller method getScheduledPosts():

private static final int PAGE_SIZE = 10;

@RequestMapping("/scheduledPosts")
@ResponseBody
public List<Post> getScheduledPosts(
  @RequestParam(value = "page", required = false) int page) {
    User user = getCurrentUser();
    Page<Post> posts = 
      postReopsitory.findByUser(user, new PageRequest(page, PAGE_SIZE));
    
    return posts.getContent();
}

2.2. Display Paginated Posts

Now – let’s implement a simple pagination control in front end:

<table>
<thead><tr><th>Post title</th></thead>
</table>
<br/>
<button id="prev" onclick="loadPrev()">Previous</button> 
<button id="next" onclick="loadNext()">Next</button>

And here is how we load the pages with plain jQuery:

$(function(){ 
    loadPage(0); 
}); 

var currentPage = 0;
function loadNext(){ 
    loadPage(currentPage+1);
} 

function loadPrev(){ 
    loadPage(currentPage-1); 
}

function loadPage(page){
    currentPage = page;
    $('table').children().not(':first').remove();
    $.get("api/scheduledPosts?page="+page, function(data){
        $.each(data, function( index, post ) {
            $('.table').append('<tr><td>'+post.title+'</td><td></tr>');
        });
    });
}

As we move forward, this manual table will get quickly replaced with a more mature table plugin, but for now, this works just fine.

3. Show The Login Page To Non Logged In Users

When a user accesses the root, they should get different pages if they’re logged in or not.

If the user is logged in, they should see their homepage/dashboard. If they’re not logged in – they should see the login page:

@RequestMapping("/")
public String homePage() {
    if (SecurityContextHolder.getContext().getAuthentication() != null) {
        return "home";
    }
    return "index";
}

4. Advanced Options for Post Resubmit

Removing and resubmitting posts in Reddit is a useful, highly effective functionality. However, we want to be careful with it and have full control over when we should and when we shouldn’t do it.

For example – we might not want to remove a post if it already has comments. At the end of the day, comments are engagement and we want to respect the platform and the people commenting on the post.

So – that’s the first small yet highly useful feature we’ll add – a new option that’s going to allow us to only remove a post if it doesn’t have comments on it.

Another very interesting question to answer is – if the post is resubmitted for however many times but still doesn’t get the traction it needs – do we leave it on after the last attempt or not? Well, like all interesting questions, the answer here is – “it depends”. If it’s a normal post, we might just call it a day and leave it up. However, if it’s a super-important post and we really really want to make sure it gets some traction, we might delete it at the end.

So this is the second small but very handy feature we’ll build here.

Finally – what about controversial posts? A post can have 2 votes on reddit because there it has to positive votes, or because it has 100 positive and 98 negative votes. The first option means it’s not getting traction, while the second means that it’s getting a lot of traction and that the voting is split.

So – this is the third small feature we’re going to add – a new option to take this upvote to downvote ration into account when determining if we need to remove the post or not.

4.1. The Post Entity

First, we need to modify our Post entity:

@Entity
public class Post {
    ...
    private int minUpvoteRatio;
    private boolean keepIfHasComments;
    private boolean deleteAfterLastAttempt;
}

Here are the 3 fields:

  • minUpvoteRatio: The minimum upvote ratio the user wants his post to reach – the upvote ratio represents how % of total votes ara upvotes [max = 100, min =0]
  • keepIfHasComments: Determine whether the user want to keep his post if it has comments despite not reaching required score.
  • deleteAfterLastAttempt: Determine whether the user want to delete the post after the final attempt ends without reaching required score.

4.2. The Scheduler

Let’s now integrate these interesting new options into the scheduler:

@Scheduled(fixedRate = 3 * 60 * 1000)
public void checkAndDeleteAll() {
    List<Post> submitted = 
      postReopsitory.findByRedditIDNotNullAndNoOfAttemptsAndDeleteAfterLastAttemptTrue(0);
    
    for (Post post : submitted) {
        checkAndDelete(post);
    }
}

On the the more interesting part – the actual logic of checkAndDelete():

private void checkAndDelete(Post post) {
    if (didIntervalPass(post.getSubmissionDate(), post.getTimeInterval())) {
        if (didPostGoalFail(post)) {
            deletePost(post.getRedditID());
            post.setSubmissionResponse("Consumed Attempts without reaching score");
            post.setRedditID(null);
            postReopsitory.save(post);
        } else {
            post.setNoOfAttempts(0);
            post.setRedditID(null);
            postReopsitory.save(post);
        }
    }
}

And here’s the didPostGoalFail() implementation – checking if the post failed to reach the predefined goal/score:

private boolean didPostGoalFail(Post post) {
    PostScores postScores = getPostScores(post);
    int score = postScores.getScore();
    int upvoteRatio = postScores.getUpvoteRatio();
    int noOfComments = postScores.getNoOfComments();
    return (((score < post.getMinScoreRequired()) || 
             (upvoteRatio < post.getMinUpvoteRatio())) && 
           !((noOfComments > 0) && post.isKeepIfHasComments()));
}

We also need to modify the logic that retrieves the Post information from Reddit – to make sure we gather more data:

public PostScores getPostScores(Post post) {
    JsonNode node = restTemplate.getForObject(
      "http://www.reddit.com/r/" + post.getSubreddit() + 
      "/comments/" + post.getRedditID() + ".json", JsonNode.class);
    PostScores postScores = new PostScores();

    node = node.get(0).get("data").get("children").get(0).get("data");
    postScores.setScore(node.get("score").asInt());
    
    double ratio = node.get("upvote_ratio").asDouble();
    postScores.setUpvoteRatio((int) (ratio * 100));
    
    postScores.setNoOfComments(node.get("num_comments").asInt());
    
    return postScores;
}

We’re using a simple value object to represent the scores as we’re extracting them from the Reddit API:

public class PostScores {
    private int score;
    private int upvoteRatio;
    private int noOfComments;
}

Finally, we need to modify checkAndReSubmit() to set the successfully resubmitted post’s redditID to null:

private void checkAndReSubmit(Post post) {
    if (didIntervalPass(post.getSubmissionDate(), post.getTimeInterval())) {
        if (didPostGoalFail(post)) {
            deletePost(post.getRedditID());
            resetPost(post);
        } else {
            post.setNoOfAttempts(0);
            post.setRedditID(null);
            postReopsitory.save(post);
        }
    }
}

Note that:

  • checkAndDeleteAll(): runs every 3 minutes through to see if any posts have consumed their attempts and can be deleted
  • getPostScores(): return post’s {score, upvote ratio, number of comments}

4.3. Modify The Schedule Page

We need to add the new modifications to our schedulePostForm.html:

<input type="number" name="minUpvoteRatio"/>
<input type="checkbox" name="keepIfHasComments" value="true"/>
<input type="checkbox" name="deleteAfterLastAttempt" value="true"/>

5. Email Important Logs

Next, we’ll implement a quick but highly useful setting in our logback configuration – emailing of important logs (ERROR level). This is of course quite handy to easily track errors early on in the lifecycle of an application.

First, we’ll add a few required dependencies to our pom.xml:

<dependency>
    <groupId>javax.activation</groupId>
    <artifactId>activation</artifactId>
    <version>1.1.1</version>
</dependency>
<dependency>
    <groupId>javax.mail</groupId>
    <artifactId>mail</artifactId>
    <version>1.4.1</version>
</dependency>

Then, we will add a SMTPAppender to our logback.xml:

<configuration>

    <appender name="STDOUT" ...

    <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>

        <smtpHost>smtp.example.com</smtpHost>
        <to>example@example.com</to>
        <from>example@example.com</from>
        <username>example@example.com</username>
        <password>password</password>
        <subject>%logger{20} - %m</subject>
        <layout class="ch.qos.logback.classic.html.HTMLLayout"/>
    </appender>

    <root level="INFO">
        <appender-ref ref="STDOUT" />
        <appender-ref ref="EMAIL" />
    </root>

</configuration>

And that’s about it – now, the deployed application will email any problem as it happens.

6. Cache Subreddits

Turns out, auto-completing subreddits expensive. Every time a user starts typing in a subreddit when scheduling a post – we need to hit the Reddit API to get these subreddits and show the user some suggestions. Not ideal.

Instead of calling the Reddit API – we’ll simply cache the popular subreddits and use them to autocomplete.

6.1. Retrieve Subreddits

First, let’s retrieve the most popular subreddits and save them to a plain file:

public void getAllSubreddits() {
    JsonNode node;
    String srAfter = "";
    FileWriter writer = null;
    try {
        writer = new FileWriter("src/main/resources/subreddits.csv");
        for (int i = 0; i < 20; i++) {
            node = restTemplate.getForObject(
              "http://www.reddit.com/" + "subreddits/popular.json?limit=100&after=" + srAfter, 
              JsonNode.class);
            srAfter = node.get("data").get("after").asText();
            node = node.get("data").get("children");
            for (JsonNode child : node) {
                writer.append(child.get("data").get("display_name").asText() + ",");
            }
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                logger.error("Error while getting subreddits", e);
            }
        }
        writer.close();
    } catch (Exception e) {
        logger.error("Error while getting subreddits", e);
    }
}

Is this a mature implementation? No. Do we need anything more? No we don’t. We need to move on.

6.2. Subbreddit Autocomplete

Next, let’s make sure the subreddits are loaded into memory on application startup – by having the service implement InitializingBean:

public void afterPropertiesSet() {
    loadSubreddits();
}
private void loadSubreddits() {
    subreddits = new ArrayList<String>();
    try {
        Resource resource = new ClassPathResource("subreddits.csv");
        Scanner scanner = new Scanner(resource.getFile());
        scanner.useDelimiter(",");
        while (scanner.hasNext()) {
            subreddits.add(scanner.next());
        }
        scanner.close();
    } catch (IOException e) {
        logger.error("error while loading subreddits", e);
    }
}

Now that the subreddit data is all loaded up into memory, we can search over the subreddits without hitting the Reddit API:

public List<String> searchSubreddit(String query) {
    return subreddits.stream().
      filter(sr -> sr.startsWith(query)).
      limit(9).
      collect(Collectors.toList());
}

The API exposing the subreddit suggestions of course remains the same:

@RequestMapping(value = "/subredditAutoComplete")
@ResponseBody
public List<String> subredditAutoComplete(@RequestParam("term") String term) {
    return service.searchSubreddit(term);
}

7. Metrics

Finally – we’ll integrate some simple metrics into the application. For a lot more on building out these kinds of metrics, I wrote about them in some detail here.

7.1. Servlet Filter

Here the simple MetricFilter:

@Component
public class MetricFilter implements Filter {

    @Autowired
    private IMetricService metricService;

    @Override
    public void doFilter(
      ServletRequest request, ServletResponse response, FilterChain chain) 
      throws IOException, ServletException {
        HttpServletRequest httpRequest = ((HttpServletRequest) request);
        String req = httpRequest.getMethod() + " " + httpRequest.getRequestURI();

        chain.doFilter(request, response);

        int status = ((HttpServletResponse) response).getStatus();
        metricService.increaseCount(req, status);
    }
}

We also need to add it in our ServletInitializer:

@Override
public void onStartup(ServletContext servletContext) throws ServletException {
    super.onStartup(servletContext);
    servletContext.addListener(new SessionListener());
    registerProxyFilter(servletContext, "oauth2ClientContextFilter");
    registerProxyFilter(servletContext, "springSecurityFilterChain");
    registerProxyFilter(servletContext, "metricFilter");
}

7.2. Metric Service

And here is our MetricService:

public interface IMetricService {
    void increaseCount(String request, int status);
    
    Map getFullMetric();
    Map getStatusMetric();
    
    Object[][] getGraphData();
}

7.3. Metric Controller

And her’s the basic controller responsible with exposing these metrics over HTTP:

@Controller
public class MetricController {
    
    @Autowired
    private IMetricService metricService;

    // 
    
    @RequestMapping(value = "/metric", method = RequestMethod.GET)
    @ResponseBody
    public Map getMetric() {
        return metricService.getFullMetric();
    }

    @RequestMapping(value = "/status-metric", method = RequestMethod.GET)
    @ResponseBody
    public Map getStatusMetric() {
        return metricService.getStatusMetric();
    }

    @RequestMapping(value = "/metric-graph-data", method = RequestMethod.GET)
    @ResponseBody
    public Object[][] getMetricGraphData() {
        Object[][] result = metricService.getGraphData();
        for (int i = 1; i < result[0].length; i++) {
            result[0][i] = result[0][i].toString();
        }
        return result;
    }
}

8. Conclusion

This case study is growing nicely. The app actually started as a simple tutorial on doing OAuth with the Reddit API; now, it’s evolving into a useful tool for the Reddit power-user – especially around the scheduling and re-submitting options.

Finally, since I’ve been using it, it looks like my own submissions to Reddit are generally picking up a lot more steam, so that’s always good to see.

Viewing all 3871 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>