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

REST Query Language – Advanced Search Operations

$
0
0

I usually post about REST APIs and HTTP on Twitter - you can follow me there:

1. Overview

In this article, we’ll extend the REST Query Language we developed in the previous parts of the series to include more search operations.

We now support the following operations: Equality, Negation, Greater than, Less than, Starts with, Ends with, Contains and Like.

Note that we explored three implementations – JPA Criteria, Spring Data JPA Specifications and Query DSL; we’re going forward with Specifications in this article because it’s a clean and flexible way to represent our operations.

2. The SearchOperation enum

First – let’s start by defining a better representation of our various supported search operations – via an enumeration:

public enum SearchOperation {
    EQUALITY, NEGATION, GREATER_THAN, LESS_THAN, LIKE, STARTS_WITH, ENDS_WITH, CONTAINS;

    public static final String[] SIMPLE_OPERATION_SET = { ":", "!", ">", "<", "~" };

    public static SearchOperation getSimpleOperation(char input) {
        switch (input) {
        case ':':
            return EQUALITY;
        case '!':
            return NEGATION;
        case '>':
            return GREATER_THAN;
        case '<':
            return LESS_THAN;
        case '~':
            return LIKE;
        default:
            return null;
        }
    }
}

We have two sets of operations:

1. Simple – can be represented by one character:

  • Equality: represented by colon (:)
  • Negation: represented by Exclamation mark (!)
  • Greater than: represented by (>)
  • Less than: represented by (<)
  • Like: represented by tilde (~)

2. Complex – need more than one character to be represented:

  • Starts with: represented by (=prefix*)
  • Ends with: represented by (=*suffix)
  • Contains: represented by (=*substring*)

We also need to modify our SearchCriteria class to use the new SearchOperation:

public class SearchCriteria {
    private String key;
    private SearchOperation operation;
    private Object value;
}

3. Modify UserSpecification

Now – let’s include the newly supported operations into our UserSpecification implementation:

public class UserSpecification implements Specification<User> {

    private SearchCriteria criteria;

    @Override
    public Predicate toPredicate(
      Root<User> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
    
        switch (criteria.getOperation()) {
        case EQUALITY:
            return builder.equal(root.get(criteria.getKey()), criteria.getValue());
        case NEGATION:
            return builder.notEqual(root.get(criteria.getKey()), criteria.getValue());
        case GREATER_THAN:
            return builder.greaterThan(root.<String> get(
              criteria.getKey()), criteria.getValue().toString());
        case LESS_THAN:
            return builder.lessThan(root.<String> get(
              criteria.getKey()), criteria.getValue().toString());
        case LIKE:
            return builder.like(root.<String> get(
              criteria.getKey()), criteria.getValue().toString());
        case STARTS_WITH:
            return builder.like(root.<String> get(criteria.getKey()), criteria.getValue() + "%");
        case ENDS_WITH:
            return builder.like(root.<String> get(criteria.getKey()), "%" + criteria.getValue());
        case CONTAINS:
            return builder.like(root.<String> get(
              criteria.getKey()), "%" + criteria.getValue() + "%");
        default:
            return null;
        }
    }
}

4. Persistence Tests

Next – we let’s test our new search operations – at the persistence level:

4.1. Test Equality

In the following example – we’ll search for a user by their first and last name:

@Test
public void givenFirstAndLastName_whenGettingListOfUsers_thenCorrect() {
    UserSpecification spec = new UserSpecification(
      new SearchCriteria("firstName", SearchOperation.EQUALITY, "john"));
    UserSpecification spec1 = new UserSpecification(
      new SearchCriteria("lastName", SearchOperation.EQUALITY, "doe"));
    List<User> results = repository.findAll(Specifications.where(spec).and(spec1));

    assertThat(userJohn, isIn(results));
    assertThat(userTom, not(isIn(results)));
}

4.2. Test Negation

Next, let’s search for users that by the their first name not “john”:

@Test
public void givenFirstNameInverse_whenGettingListOfUsers_thenCorrect() {
    UserSpecification spec = new UserSpecification(
      new SearchCriteria("firstName", SearchOperation.NEGATION, "john"));
    List<User> results = repository.findAll(Specifications.where(spec));

    assertThat(userTom, isIn(results));
    assertThat(userJohn, not(isIn(results)));
}

4.3. Test Greater Than

Next – we will search for users with age greater than “25”:

@Test
public void givenMinAge_whenGettingListOfUsers_thenCorrect() {
    UserSpecification spec = new UserSpecification(
      new SearchCriteria("age", SearchOperation.GREATER_THAN, "25"));
    List<User> results = repository.findAll(Specifications.where(spec));

    assertThat(userTom, isIn(results));
    assertThat(userJohn, not(isIn(results)));
}

4.4. Test Starts With

Next – users with their first name starting with “jo”:

@Test
public void givenFirstNamePrefix_whenGettingListOfUsers_thenCorrect() {
    UserSpecification spec = new UserSpecification(
      new SearchCriteria("firstName", SearchOperation.STARTS_WITH, "jo"));
    List<User> results = repository.findAll(spec);

    assertThat(userJohn, isIn(results));
    assertThat(userTom, not(isIn(results)));
}

4.5. Test Ends With

Next we’ll search for users with their first name ending with “n”:

@Test
public void givenFirstNameSuffix_whenGettingListOfUsers_thenCorrect() {
    UserSpecification spec = new UserSpecification(
      new SearchCriteria("firstName", SearchOperation.ENDS_WITH, "n"));
    List<User> results = repository.findAll(spec);

    assertThat(userJohn, isIn(results));
    assertThat(userTom, not(isIn(results)));
}

4.6. Test Contains

Now, we’ll search for users with their first name containing “oh”:

@Test
public void givenFirstNameSubstring_whenGettingListOfUsers_thenCorrect() {
    UserSpecification spec = new UserSpecification(
      new SearchCriteria("firstName", SearchOperation.CONTAINS, "oh"));
    List<User> results = repository.findAll(spec);

    assertThat(userJohn, isIn(results));
    assertThat(userTom, not(isIn(results)));
}

4.7. Test Range

Finally, we’ll search for users with ages between “20” and “25”:

@Test
public void givenAgeRange_whenGettingListOfUsers_thenCorrect() {
    UserSpecification spec = new UserSpecification(
      new SearchCriteria("age", SearchOperation.GREATER_THAN, "20"));
    UserSpecification spec1 = new UserSpecification(
      new SearchCriteria("age", SearchOperation.LESS_THAN, "25"));
    List<User> results = repository.findAll(Specifications.where(spec).and(spec1));

    assertThat(userJohn, isIn(results));
    assertThat(userTom, not(isIn(results)));
}

5. The UserSpecificationBuilder

Now that persistence is done and tested, let’s move our attention to the web layer.

We’ll build on top of the UserSpecificationBuilder implementation from the previous article to incorporate the new new search operations:

public class UserSpecificationsBuilder {

    private List<SearchCriteria> params;

    public UserSpecificationsBuilder with(
      String key, String operation, Object value, String prefix, String suffix) {
    
        SearchOperation op = SearchOperation.getSimpleOperation(operation.charAt(0));
        if (op != null) {
            if (op == SearchOperation.EQUALITY) {
                boolean startWithAsterisk = prefix.contains("*");
                boolean endWithAsterisk = suffix.contains("*");

                if (startWithAsterisk && endWithAsterisk) {
                    op = SearchOperation.CONTAINS;
                } else if (startWithAsterisk) {
                    op = SearchOperation.ENDS_WITH;
                } else if (endWithAsterisk) {
                    op = SearchOperation.STARTS_WITH;
                }
            }
            params.add(new SearchCriteria(key, op, value));
        }
        return this;
    }

    public Specification<User> build() {
        if (params.size() == 0) {
            return null;
        }

        List<Specification<User>> specs = new ArrayList<Specification<User>>();
        for (SearchCriteria param : params) {
            specs.add(new UserSpecification(param));
        }

        Specification<User> result = specs.get(0);
        for (int i = 1; i < specs.size(); i++) {
            result = Specifications.where(result).and(specs.get(i));
        }
        return result;
    }
}

6. The UserController

Next – we need to modify our UserController to correctly parse the new operations:

@RequestMapping(method = RequestMethod.GET, value = "/users")
@ResponseBody
public List<User> findAllBySpecification(@RequestParam(value = "search") String search) {
    UserSpecificationsBuilder builder = new UserSpecificationsBuilder();
    String operationSetExper = Joiner.on("|").join(SearchOperation.SIMPLE_OPERATION_SET);
    Pattern pattern = Pattern.compile(
      "(\\w+?)(" + operationSetExper + ")(\\p{Punct}?)(\\w+?)(\\p{Punct}?),");
    Matcher matcher = pattern.matcher(search + ",");
    while (matcher.find()) {
        builder.with(
          matcher.group(1), 
          matcher.group(2), 
          matcher.group(4), 
          matcher.group(3), 
          matcher.group(5));
    }

    Specification<User> spec = builder.build();
    return dao.findAll(spec);
}

We can now hit the API and get back the right results with any combination of criteria. For example – here’s a what a complex operation would look like using API with the query language:

http://localhost:8080/users?search=firstName:jo*,age<25

And the response:

[{
    "id":1,
    "firstName":"john",
    "lastName":"doe",
    "email":"john@doe.com",
    "age":24
}]

7. Tests for the Search API

Finally – let’s make sure our API works well by writing a suite of API tests.

We’ll start with the simple configuration of the test and the data initialization:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  classes = { ConfigTest.class, PersistenceConfig.class }, 
  loader = AnnotationConfigContextLoader.class)
@ActiveProfiles("test")
public class JPASpecificationLiveTest {

    @Autowired
    private UserRepository repository;

    private User userJohn;
    private User userTom;

    private final String URL_PREFIX = "http://localhost:8080/users?search=";

    @Before
    public void init() {
        userJohn = new User();
        userJohn.setFirstName("John");
        userJohn.setLastName("Doe");
        userJohn.setEmail("john@doe.com");
        userJohn.setAge(22);
        repository.save(userJohn);

        userTom = new User();
        userTom.setFirstName("Tom");
        userTom.setLastName("Doe");
        userTom.setEmail("tom@doe.com");
        userTom.setAge(26);
        repository.save(userTom);
    }

    private RequestSpecification givenAuth() {
        return RestAssured.given().auth()
                                  .preemptive()
                                  .basic("username", "password");
    }
}

7.1. Test Equality

First – let’s search for a user with the first name “john” and last name “doe:

@Test
public void givenFirstAndLastName_whenGettingListOfUsers_thenCorrect() {
    Response response = givenAuth().get(URL_PREFIX + "firstName:john,lastName:doe");
    String result = response.body().asString();

    assertTrue(result.contains(userJohn.getEmail()));
    assertFalse(result.contains(userTom.getEmail()));
}

7.2. Test Negation

Now – we’ll search for users when their first name isn’t “john”:

@Test
public void givenFirstNameInverse_whenGettingListOfUsers_thenCorrect() {
    Response response = givenAuth().get(URL_PREFIX + "firstName!john");
    String result = response.body().asString();

    assertTrue(result.contains(userTom.getEmail()));
    assertFalse(result.contains(userJohn.getEmail()));
}

7.3. Test Greater Than

Next – we will look for users with age greater than “25”:

@Test
public void givenMinAge_whenGettingListOfUsers_thenCorrect() {
    Response response = givenAuth().get(URL_PREFIX + "age>25");
    String result = response.body().asString();

    assertTrue(result.contains(userTom.getEmail()));
    assertFalse(result.contains(userJohn.getEmail()));
}

7.4. Test Starts With

Next – users with their first name starting with “jo”:

@Test
public void givenFirstNamePrefix_whenGettingListOfUsers_thenCorrect() {
    Response response = givenAuth().get(URL_PREFIX + "firstName:jo*");
    String result = response.body().asString();

    assertTrue(result.contains(userJohn.getEmail()));
    assertFalse(result.contains(userTom.getEmail()));
}

7.5. Test Ends With

Now – users with their first name ending with “n”:

@Test
public void givenFirstNameSuffix_whenGettingListOfUsers_thenCorrect() {
    Response response = givenAuth().get(URL_PREFIX + "firstName:*n");
    String result = response.body().asString();

    assertTrue(result.contains(userJohn.getEmail()));
    assertFalse(result.contains(userTom.getEmail()));
}

7.6. Test Contains

Next, we’ll search for users with their first name containing “oh”:

@Test
public void givenFirstNameSubstring_whenGettingListOfUsers_thenCorrect() {
    Response response = givenAuth().get(URL_PREFIX + "firstName:*oh*");
    String result = response.body().asString();

    assertTrue(result.contains(userJohn.getEmail()));
    assertFalse(result.contains(userTom.getEmail()));
}

7.7. Test Range

Finally, we’ll search for users with ages between “20” and “25”:

@Test
public void givenAgeRange_whenGettingListOfUsers_thenCorrect() {
    Response response = givenAuth().get(URL_PREFIX + "age>20,age<25");
    String result = response.body().asString();

    assertTrue(result.contains(userJohn.getEmail()));
    assertFalse(result.contains(userTom.getEmail()));
}

8. Conclusion

In this article we brought the query language of our REST Search API forward to a mature, tested, production grade implementation. We now support a wide variety of operations and constraints, which should make it quite easy to cut across any dataset elegantly and get to the exact resources we’re looking for.

The full implementation of this article can be found in the github project – this is an Eclipse based project, so it should be easy to import and run as it is.

I usually post about REST APIs and HTTP on Twitter - you can follow me there:


Spring Security Registration Tutorial

$
0
0

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

Building a full-fledged, production ready registration for your web application is oh so much more than just putting together a simple registration page.

There are a lot of questions that need to be answered:

  • How do I verify the email addresses of new users?
  • How do I properly and safely store user credentials?
  • What if a user forgets their password?
  • What about users changing their own password?
  • How strong should passwords be? How can I enforce some sensible defaults in the app so that my users have good, strong passwords?
  • What if I have more than one type of user? I need a good way to store roles and privileges.
  • What about security questions? Should I even have them?
  • How do I do all of this with good localization support? There are a lot of messages involved.

>> The Registration Process

Starting at the top – this is how to set up a basic Registration process for your web app. Doesn’t have to many bells and whistles, but it’s clean and functional to begin with.

>> Registration – Activate a New Account by Email

Now we want to make sure that users verify their emails before being able to just log into the app.

>> Resend the Verification Email

If a user signs up and doesn’t verify their email for a while – their verifications expires at some point. This is how they request a new verification link.

>> Registration – Password Encoding

The age old question – how to store passwords? Simple answer? You don’t!

>> Reset Your Password

Users are forgetful creatures – so they’ll forget their passwords sooner rather than later. You should have a good way for your users to reset their passwords if they need to.

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


Baeldung Weekly Review 9

$
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

>> Discovering Where Threads Are Being Constructed

An interesting example for getting in control of where threads are created from – and displaying some cool use of Java 8 syntax in the process.

>> Journey to idempotency and temporal decoupling

If your work involves HTTP in any way, shape or form, this one is a must read – slowly. It may make the difference between a resilient, mature system and one that just works sometimes.

>> Joining Strings in JDK 8

We don’t need to use Guava for joining Strings any more – Java 8 has some useful APIs to do the job well.

>> Hibernate Locking Patterns – How do PESSIMISTIC_READ and PESSIMISTIC_WRITE work

Vlad continues his very useful foray into the Hibernate pessimistic lock modes.

Also worth reading:

Webinars and presentations:

Time to upgrade:

2. Technical and Musings

>> Experience Report: Weak Code Ownership

Being intentional about code ownership in a team is an important first step a lot of teams don’t take. But once that’s explicitly considered, there’s a choice between Collective and Weak code ownership – and I fully agree with this article – in my experience, weak ownership works better.

>> 10x Developer, Reconsidered

The concept of the 10x Developer with a pinch of nuance thrown in. You know – like real life.

>> is-a vs. has-a

An interesting look at a Maslow’s hierarchy of needs modified for working in a modern day company.

>> You Have to Know When to Stop

A benefit of TDD that I haven’t considered – and know that I think about it, makes perfect sense – is you know When To Stop.

3. Comics

And my favorite Dilberts of the week:

>> Your Annual Performance Review

>> Leadership

>> Data to Ignore

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:

Authenticating with Reddit OAuth2 and Spring Security

$
0
0

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

1. Overview

In this tutorial, we’ll use Spring Security OAuth to authenticate with the Reddit API.

2. Maven Configuration

First, in order to use Spring Security OAuth – we need to add the following dependency to our pom.xml (of course along any other Spring dependency you might use):

<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
    <version>2.0.6.RELEASE</version>
</dependency>

3. Configure OAuth2 Client

Next – let’s configure our OAuth2 client – the OAuth2RestTemplate – and a reddit.properties file for all the authentication related properties:

@Configuration
@EnableOAuth2Client
@PropertySource("classpath:reddit.properties")
protected static class ResourceConfiguration {

    @Value("${accessTokenUri}")
    private String accessTokenUri;

    @Value("${userAuthorizationUri}")
    private String userAuthorizationUri;

    @Value("${clientID}")
    private String clientID;

    @Value("${clientSecret}")
    private String clientSecret;

    @Bean
    public OAuth2ProtectedResourceDetails reddit() {
        AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
        details.setId("reddit");
        details.setClientId(clientID);
        details.setClientSecret(clientSecret);
        details.setAccessTokenUri(accessTokenUri);
        details.setUserAuthorizationUri(userAuthorizationUri);
        details.setTokenName("oauth_token");
        details.setScope(Arrays.asList("identity"));
        return details;
    }

    @Bean
    public OAuth2RestTemplate redditRestTemplate(OAuth2ClientContext clientContext) {
        OAuth2RestTemplate template = new OAuth2RestTemplate(reddit(), clientContext);
        AccessTokenProvider accessTokenProvider = new AccessTokenProviderChain(
          Arrays.<AccessTokenProvider> asList(
            new MyAuthorizationCodeAccessTokenProvider(), 
            new ImplicitAccessTokenProvider(), 
            new ResourceOwnerPasswordAccessTokenProvider(),
            new ClientCredentialsAccessTokenProvider())
        );
        template.setAccessTokenProvider(accessTokenProvider);
        return template;
    }

}

And “reddit.properties“:

clientID=xxxxxxxx
clientSecret=xxxxxxxx
accessTokenUri=https://www.reddit.com/api/v1/access_token
userAuthorizationUri=https://www.reddit.com/api/v1/authorize

You can get your own secret code by creating a Reddit app from https://www.reddit.com/prefs/apps/

We’re going to use the OAuth2RestTemplate to:

  1. Acquire the access token needed to access the remote resource.
  2. Access the remote resource after getting the access token.

Also note how we added the scope “identity” to Reddit OAuth2ProtectedResourceDetails so that we can retrieve the users account information later.

4. Custom AuthorizationCodeAccessTokenProvider

The Reddit OAuth2 implementation is a little different from the standard. And so – instead of elegantly extending the AuthorizationCodeAccessTokenProvider – we need to actually override some portions of it.

There are github issues tracking improvements that will make this not necessary, but these issues are not yet done.

One of the non-standard things that Reddit does is – when we redirect the user and prompt him to authenticate with Reddit, we need to have some custom parameters in the redirect URL. More specifically – if we’re asking for a permanent access token from Reddit – we need to add a parameter “duration” with the value “permanent“.

So, after extending AuthorizationCodeAccessTokenProvider – we have added this parameter in the getRedirectForAuthorization() method:

    requestParameters.put("duration", "permanent");

You can check the full source code from here.

5. MVC Configuration

Now – let’s take a look at our MVC configuration of our simple web-app:

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "org.baeldung.web" })
public class WebConfig extends WebMvcConfigurerAdapter {

    @Bean
    public static PropertySourcesPlaceholderConfigurer 
      propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }

    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/jsp/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }

    @Override
    public void configureDefaultServletHandling(
      DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
    }
}

6. Custom ServerInitializer

Next – let’s create our custom ServerInitializer.

We need to add a filter bean with id oauth2ClientContextFilter, so that we can use it to store the current context:

public class ServletInitializer extends AbstractDispatcherServletInitializer {

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

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

    @Override
    protected WebApplicationContext createRootApplicationContext() {
        return null;
    }

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

    private void registerProxyFilter(ServletContext servletContext, String name) {
        DelegatingFilterProxy filter = new DelegatingFilterProxy(name);
        filter.setContextAttribute(
          "org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher");
        servletContext.addFilter(name, filter).addMappingForUrlPatterns(null, false, "/*");
    }
}

7. RedditController

Now – let’s take a look at our controller RedditController.

We use method getInfo() to get the user information from his Reddit account – as in the following example:

@Controller
public class RedditController {

    @Autowired
    private OAuth2RestTemplate redditRestTemplate;

    @RequestMapping("/info")
    public String getInfo(Model model) {
        JsonNode node = redditRestTemplate.getForObject(
          "https://oauth.reddit.com/api/v1/me", JsonNode.class);
        String name = node.get("name").asText();
        model.addAttribute("info", name);
        return "reddit";
    }
}

An interesting detail of this deceptively simple method – the reddit template checks if the access token is available before executing any request; it acquires a token if one is not available.

Next – we present the information to our very simplistic front end.

8. reddit.jsp

Finally – let’s take a look at reddit.jsp – to display the information retrieved form user’s Reddit account:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Spring Security OAuth</title>
</head>
<body>
<c:choose>
    <c:when test="${info != null}">
        <h1>Your Reddit Info</h1>
        <b>Your reddit username is </b>${info}
    </c:when>
    <c:otherwise> 
        <b>Sorry, error occurred</b> 
        <br><br>
        <div>${error}</div>
    </c:otherwise>
</c:choose>
</body>
</html>

9. Conclusion

In this introductory article, we explored authenticating with the Reddit OAuth2 API and displaying some very basic information in a simple front end.

Now that we’re authenticated, we’re going to explore doing more interesting things with the Reddit API in the next article of this new series.

The full implementation of this tutorial can be found in the github project – this is an Eclipse based project, so it should be easy to import and run as it is.

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


Post a Link to the Reddit API

$
0
0

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

1. Overview

In this article, we’ll build some simple functionality to post on Reddit from our application, via their API.

2. Necessary Security

First – let’s get the security aspect out of the way.

In order to Submit a Link to Reddit, we need to define an OAuth protected Resource with the scope of “submit“:

@Bean
public OAuth2ProtectedResourceDetails reddit() {
    AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
    details.setId("reddit");
    details.setClientId(clientID);
    details.setClientSecret(clientSecret);
    details.setAccessTokenUri(accessTokenUri);
    details.setUserAuthorizationUri(userAuthorizationUri);
    details.setTokenName("oauth_token");
    details.setScope(Arrays.asList("identity", "submit"));
    details.setGrantType("authorization_code");
    return details;
}

Note that we’re also specifying the scopeidentity” because we also need access the user account information.

3. Is Captcha Needed?

Users that are new to Reddit have to fill in a Captcha in order to submit; that is before they pass a certain karma threshold within Reddit.

For these users, we first need to check if the Captcha is needed:

private String needsCaptcha() {
    String result = redditRestTemplate.getForObject(
      "https://oauth.reddit.com/api/needs_captcha.json", String.class);
    return result;
}

private String getNewCaptcha() {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    HttpEntity req = new HttpEntity(headers);

    Map<String, String> param = new HashMap<String, String>();
    param.put("api_type", "json");

    ResponseEntity<String> result = redditRestTemplate.postForEntity(
      "https://oauth.reddit.com/api/new_captcha", req, String.class, param);
    String[] split = result.getBody().split("\""); 
    return split[split.length - 2];
}

4. The “Submit Post” Form

Next, let’s create the main form for submitting new posts to Reddit. Submitting a Link requires the following details:

  • title – the title of the article
  • url – the URL of the article
  • subreddit – the sub-reddit to submit the link to

So let’s see how we can display this simple submission page:

@RequestMapping("/post")
public String showSubmissionForm(Model model) throws JsonProcessingException, IOException {
    String needsCaptchaResult = needsCaptcha();
    if (needsCaptchaResult.equalsIgnoreCase("true")) {
        String iden = getNewCaptcha();
        model.addAttribute("iden", iden);
    }
    return "submissionForm";
}

And of course the basic  submissionForm.jsp:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Spring Security OAuth</title>
    <link rel="stylesheet" 
      href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
</head>
<body>
<div class="container">
<h1>Submit to Reddit</h1>
<form action="submit" method="post">
<div class="row">
<div class="form-group">
    <label class="col-sm-3">Title</label>
    <span class="col-sm-9">
        <input name="title" placeholder="title" class="form-control" />
    </span>
</div>
<br><br>
<div class="form-group">
    <label class="col-sm-3">Url</label>
    <span class="col-sm-9">
        <input name="url" placeholder="url" class="form-control" />
    </span>
</div>
<br><br>  
<div class="form-group">
    <label class="col-sm-3">Subreddit</label>
    <span class="col-sm-9">
        <input name="sr" placeholder="Subreddit" class="form-control" />
    </span>
</div>
<br><br>
    <c:if test="${iden != null}">
    <input type="hidden" name="iden" value="${iden}"/>
	<div class="form-group">   
	    <label class="col-sm-3">Captcha</label>
	    <span class="col-sm-9">
                <input name="captcha" placeholder="captcha" class="form-control"/>
            </span>
	</div>
	<br><br>
    <img src="http://www.reddit.com/captcha/${iden}" alt="captcha" width="200"/>
    </c:if>
    <br><br>
    <button type="submit" class="btn btn-primary">Post</button>
   </div>
</form>
</div>
</body>
</html>

5. Submit a Link to Reddit

Now – let’s take a look at the final step – submitting the actual link via the Reddit API.

We’ll post a submit request to Reddit using the parameters from our submissionForm:

@RequestMapping("/submit")
public String submit(Model model, @RequestParam Map<String, String> formParams) {
    MultiValueMap<String, String> param = new LinkedMultiValueMap<String, String>();
    param.add("api_type", "json");
    param.add("kind", "link");
    param.add("resubmit", "true");
    param.add("sendreplies", "false");
    param.add("then", "comments");
    for (Map.Entry<String, String> entry : formParams.entrySet()) {
        param.add(entry.getKey(), entry.getValue());
    }

    logger.info("Submitting Link with these parameters: " + formParams.entrySet());
    String result = redditRestTemplate.postForObject(
      "https://oauth.reddit.com/api/submit", param, String.class);
    logger.info("Submitted Link - Full Response from Reddit" + result);
    
    String responseMsg = parseResponseMessage(result);
    model.addAttribute("msg", responseMsg);
    
    return "submissionResponse";
}

And the simple parseResponse() logic:

private String parseResponse(String responseBody) throws JsonProcessingException, IOException {
    String result = "";
    JsonNode node = new ObjectMapper().readTree(responseBody);
    JsonNode errorNode = node.get("json").get("errors").get(0);
    for (JsonNode child : errorNode) {
        result = result + child.toString().replaceAll("\"|null", "") + "<br>";
    }
    if (result.length() == 0) {
        if (node.get("json").get("data") != null && node.get("json").get("data").get("url") != null) {
            return "Post submitted successfully 
              <a href=\"" + node.get("json").get("data").get("url").asText() + "\"> check it out </a>";
        }
        else {
            return "Error Occurred";
        }
    }
    return result;
}

Finally – the submissionResponse.jsp:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Spring Security OAuth</title>
    <link rel="stylesheet" href=
      "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
</head>
<body>
    <div class="container">
        <h1>${msg}</h1>
        <a href="post" class="btn btn-primary">Submit another link to Reddit</a>
    </div>
</body>
</html>

6. Conclusion

In this quick tutorial we implemented some basic Submit to Reddit functionality – simplistic but fully functional.

In the next part of this case study, we’ll implement a Schedule Post for Later functionality into our app.

The full implementation of this tutorial can be found in the github project – this is an Eclipse based project, so it should be easy to import and run as it is.

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


Baeldung Weekly Review 10

$
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

>> The Portable, Cloud-Ready HTTP Session

After Spring Boot, I’m most excited about Spring Session among all the new Spring projects being worked on. This piece teases a medium rare Redis backed Http Session with deployment to boot.

>> A beginner’s guide to JPA and Hibernate Cascade Types

A huge resource for understanding cascading in Hibernate and JPA – lots of examples here worth exploring.

>> Java Bootstrap: Dropwizard vs. Spring Boot

Good intro to both frameworks.

One important point to understand is that Dropwizard has been around since 2011, whereas Spring Boot is a much newer framework. From that POV, it’s quite impressive to see it reach good adoption and maturity in such a short time.

>> Go for the Money! JSR 354 Adds First Class Money and Currency Support to Java

A look at the upcoming Money JSR – looks useful if you’re dealing with money.

>> Walking Recursive Data Structures Using Java 8 Streams

A cool usecase for Streams when traversing trees elegantly.

Also worth reading:

Webinars and presentations:

Time to upgrade:

2. Technical and Musings

>> Unorthodocs: Abandon your DVCS and Return to Sanity

This was fun to read. Someone, somewhere doesn’t think git is the bees knees? I say blasphemy.

>> Performance Reviews Simplified

Whenever I encountered performance reviews in my career, both on the receiving and giving end, I always though – “There must be a better way to do this.

This article circles the same idea and offers up an interesting, simple alternative – well worth reading.

>> Why Non-Blocking?

Some very interesting considerations of a non-blocking vs blocking architecture.

Having worked with an event-sourced system as well, I can definitely see how it both introduces a lot of cool stuff that would simply not be possible with a more traditional architecture, but also makes things a lot more difficult to write and reason about.

Also worth reading:

3. Comics

And my favorite Dilberts of the week:

>> The Brain Wuss Subroutine

>> Getting Funding

>> Failure is guaranteed

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 11

$
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

>> Mark Reinhold on Java 9 and Beyond

The plans for Java going forward.

>> JDK 8 Streams and Grouping

Powerful use of Streams to reorganize and classify the backing collections.

>> Hibernate CascadeType.LOCK gotchas

Further code-heavy, in depth exploration of locking in Hibernate.

>> Test Collection Implementations with Guava

An interesting way to test a custom collection implementation using tests that already exist in Guava. Didn’t know Guava can do that.

>> Become a DevOps with Spring Boot

This is a very good place to start for monitoring a Spring Boot app.

>> Peter Lawrey Describes Petabyte JVMs

An very cool exploration of JVMs with very large heaps (32GB+).

>> Getting started with Activiti and Spring Boot

A solid, code focused introduction to Activity within the Spring Boot ecosystem. I especially like the testing part – that usually takes a bit of API exploration to get right.

>> Getting Started with Gradle: Creating a Web Application Project

Although I’m personally not using Gradle, this series will definitely be my go-to if I ever decide to jump on the Gradle wagon.

Also worth reading:

Webinars and presentations:

Time to upgrade:

2. Technical and Musings

>> Fired

This took a pair to write.

>> Yak Shaving is a Good Way to Improve an API

It’s widely understood that building APIs is hard. And more nuanced, some of the good practices that would otherwise apply in most cases of building software may not apply when building APIs. This piece is about that.

3. Comics

And my favorite Dilberts of the week:

>> Nothing to fear but fear itself

>> I summon the vast power of CERTIFICATION

>> I fixed the Internet

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 12

$
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 to batch INSERT and UPDATE statements with Hibernate

Batching write operations is super useful, and configuring it properly with Hibernate is a must.

>> Roll Your Own Pirate-Elvis Operator

The next best thing, since the Elvis Operator won’t likely be introduced into Java.

>> Getting Started with Couchbase and Spring Data Couchbase

A good, ops-focused intro to Couchbase within the Spring ecosystem.

>> Custom Jackson Polymorphic Deserialization Without Type Metadata

An interesting approach to dealing with semi-structured data by leveraging Jackson.

Also worth reading:

Webinars and presentations:

Time to upgrade:

2. Technical

>> My Answers for Microservices Awkward Questions

Insightful thoughts on Microservices, just in case you’re jumping on this particular trend.

>> The 10 Things Everyone does Wrong when Committing Pull Requests

2 must reads in one week? Crazy I know, but if you’re doing any kind of open source work, please do read this one – it’s important if you actually want your work to go in.

Also worth reading:

3. Musings

>> Getting Started as a Blogger: A Contract with Your Readers

I find myself dispensing with advice on how to start a blog at least a few times a month. Sometimes I need to talk someone out of wasting time with fancy tech stuff. Other times drive home the point that you don’t start with 100 topics in your queue. You start with a handful and a commitment to show up.

If you’ve been circling around the idea of blogging – get with the clicky clicky and read this piece.

On an unrelated note, I need to clear my fridge.

>> Reflections on a year in DevOps

You’re going to have to do Operations. Maybe not on the scale of TripAdvisor, but if you have software running in production, you’re going to have to do Operations. This is about that.

>> Goldilocks process

A must read about intentional process, understanding tradeoffs and what they mean for your organizations ability to ship software.

>> How to Land a Software Engineering Job?

A bit of sanity in the hiring process – if you’re on either side of the line, it’s a quick good read.

>> Employers: Put Your Money where Your Mouth Is

An interesting thought exercise about auditing the practices of various companies. It would be cool to ask the question – does company X do TDD? – and have an answer you can trust.

Also worth reading:

4. Comics

And my favorite Dilberts of the week:

>> Engineer credibility

>> Indecision

>> Dumbing down jargon

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:


Schedule Post to Reddit with Spring

$
0
0

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

1. Overview

In the earlier parts of this case study, we set up a simple app and an OAuth authentication process with the Reddit API.

Let’s now build something useful with Reddit – support for scheduling Posts for latter.

2. The User and the Post

First, let’s create the 2 main entities – the User and the Post. The User will keep track of the username plus some additional Oauth info:

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

    @Column(nullable = false)
    private String username;

    private String accessToken;
    private String refreshToken;
    private Date tokenExpiration;

    private boolean needCaptcha;

    // standard setters and getters
}

Next – the Post entity – holding the information necessary for submitting a link to Reddit: title, URL, subreddit, … etc.

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

    @Column(nullable = false) private String title;
    @Column(nullable = false) private String subreddit;
    @Column(nullable = false) private String url;
    private boolean sendReplies;

    @Column(nullable = false) private Date submissionDate;

    private boolean isSent;

    private String submissionResponse;

    @ManyToOne
    @JoinColumn(name = "user_id", nullable = false)
    private User user;
    // standard setters and getters
}

3. The Persistence Layer

We’re going to use Spring Data JPA for persistence, so there’s not a whole lot to look at here, other than the well-known interface definitions for our repositories:

  • UserRepository:
public interface UserRepository extends JpaRepository<User, Long> {

    User findByUsername(String username);

    User findByAccessToken(String token);
}
  • PostRepository:
public interface PostRepository extends JpaRepository<Post, Long> {

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

    List<Post> findByUser(User user);
}

4. A Scheduler

For the scheduling aspects of the app, we’re also going to make good use of the out-of-the-box Spring support.

We’re defining a task to run every minute; this will simply look for Posts that are due to be submitted to Reddit:

public class ScheduledTasks {
    private final Logger logger = LoggerFactory.getLogger(getClass());
    
    private OAuth2RestTemplate redditRestTemplate;
    
    @Autowired
    private PostRepository postReopsitory;

    @Scheduled(fixedRate = 1 * 60 * 1000)
    public void reportCurrentTime() {
        List<Post> posts = 
          postReopsitory.findBySubmissionDateBeforeAndIsSent(new Date(), false);
        for (Post post : posts) {
            submitPost(post);
        }
    }

    private void submitPost(Post post) {
        try {
            User user = post.getUser();
            DefaultOAuth2AccessToken token = 
              new DefaultOAuth2AccessToken(user.getAccessToken());
            token.setRefreshToken(new DefaultOAuth2RefreshToken((user.getRefreshToken())));
            token.setExpiration(user.getTokenExpiration());
            redditRestTemplate.getOAuth2ClientContext().setAccessToken(token);
            
            UsernamePasswordAuthenticationToken userAuthToken = 
              new UsernamePasswordAuthenticationToken(
              user.getUsername(), token.getValue(), 
              Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")));
            SecurityContextHolder.getContext().setAuthentication(userAuthToken);

            MultiValueMap<String, String> param = new LinkedMultiValueMap<String, String>();
            param.add("api_type", "json");
            param.add("kind", "link");
            param.add("resubmit", "true");
            param.add("then", "comments");
            param.add("title", post.getTitle());
            param.add("sr", post.getSubreddit());
            param.add("url", post.getUrl());
            if (post.isSendReplies()) {
                param.add(RedditApiConstants.SENDREPLIES, "true");
            }

            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.setSent(true);
                post.setSubmissionResponse("Successfully sent");
                postReopsitory.save(post);
            } else {
                post.setSubmissionResponse(errorNode.toString());
                postReopsitory.save(post);
            }
        } catch (Exception e) {
            logger.error("Error occurred", e);
        }
    }
}

Note that, in case of anything going wrong, the Post will not be marked as sent – so the next cycle will try to submit it again after one minute.

5. The Login Process

With the new User entity, holding some security specific information, we’ll need to modify our simple login process to store that information:

@RequestMapping("/login")
public String redditLogin() {
    JsonNode node = redditRestTemplate.getForObject(
      "https://oauth.reddit.com/api/v1/me", JsonNode.class);
    loadAuthentication(node.get("name").asText(), redditRestTemplate.getAccessToken());
    return "redirect:home.html";
}

And loadAuthentication():

private void loadAuthentication(String name, OAuth2AccessToken token) {
    User user = userReopsitory.findByUsername(name);
    if (user == null) {
        user = new User();
        user.setUsername(name);
    }

    if (needsCaptcha().equalsIgnoreCase("true")) {
        user.setNeedCaptcha(true);
    } else {
        user.setNeedCaptcha(false);
    }

    user.setAccessToken(token.getValue());
    user.setRefreshToken(token.getRefreshToken().getValue());
    user.setTokenExpiration(token.getExpiration());
    userReopsitory.save(user);

    UsernamePasswordAuthenticationToken auth = 
      new UsernamePasswordAuthenticationToken(user, token.getValue(), 
      Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")));
    SecurityContextHolder.getContext().setAuthentication(auth);
}

Note how the user is automatically created if it doesn’t already exist. This makes the “Login with Reddit” process create a local user in the system on the first login.

6. The Schedule Page

Next – let’s take a look at the page that allows scheduling of new Posts:

@RequestMapping("/postSchedule")
public String showSchedulePostForm(Model model) {
    boolean isCaptchaNeeded = getCurrentUser().isCaptchaNeeded();
    if (isCaptchaNeeded) {
        model.addAttribute("msg", "Sorry, You do not have enought karma");
        return "submissionResponse";
    }
    return "schedulePostForm";
}
private User getCurrentUser() {
    return (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}

schedulePostForm.jsp:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<body>

<form action="schedule" method="post">
    <input name="title" />
    <input name="url" />
    <input name="sr" />
    <input type="checkbox" name="sendreplies" value="true"/> 
    <input name="date">
    <button type="submit">Post</button>
</form>
</body>
</html>

Note how we need to check the Captcha. This is because – if the user has less than 10 karma – they can’t schedule a post without filling in the Captch.

7. The POST

When the Schedule Form is submitted (POSTed), the information is stored in the database to be submitted later:

@RequestMapping("/schedule")
public String schedule(Model model, @RequestParam Map<String, String> formParams) 
  throws ParseException {
    User user = getCurrentUser();
    Post post = new Post();
    post.setUser(user);
    post.setSent(false);
    post.setTitle(formParams.get("title"));
    post.setSubreddit(formParams.get("sr"));
    post.setUrl(formParams.get("url"));
    if (formParams.containsKey("sendreplies")) {
        post.setSendReplies(true);
    }
    post.setSubmissionDate(dateFormat.parse(formParams.get("date")));
    if (post.getSubmissionDate().before(new Date())) {
        model.addAttribute("msg", "Invalid date");
        return "submissionResponse";
    }
    postReopsitory.save(post);
    List<Post> posts = postReopsitory.findByUser(user);
    model.addAttribute("posts", posts);
    return "postListView";
}

8. The List of Scheduled

We can check the scheduled posts status using a simple posts view – as follows:

@RequestMapping("/posts")
public String getScheduledPosts(Model model) {
    User user = getCurrentUser();
    List<Post> posts = postReopsitory.findByUser(user);
    model.addAttribute("posts", posts);
    return "postListView";
}

postListView.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<html>
<body>
<table>
<c:forEach var="post" items="${posts}" >
<tr>
    <td><c:out value="${post.getTitle()}"/></td>
    <td><fmt:formatDate type="both" 
      dateStyle="long" timeStyle="long" value="${post.getSubmissionDate()}" />    </td>
    <td><c:out value="${post.getSubmissionResponse()}"/></td>
    <td>
        <a href="editPost/${post.getId()}">Edit</a>
        <a href="#" onclick="confirmDelete(${post.getId()})">Delete</a>
    </td>
</tr>
</c:forEach>
</table>
</body>
</html>

9. Edit a Scheduled Post

Now – let’s see how we can edit a scheduled post.

We’ll use showEditPostForm() to show the editPostForm.jsp – as follows:

@RequestMapping(value = "/editPost/{id}", method = RequestMethod.GET)
public String showEditPostForm(Model model, @PathVariable Long id) {
    Post post = postReopsitory.findOne(id);
    model.addAttribute("post", post);
    model.addAttribute("dateValue", dateFormat.format(post.getSubmissionDate()));
    return "editPostForm";
}

And the editPostForm.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<body>

<form action="<c:url value="/updatePost/${post.getId()}" />" method="post">
    <input type="hidden" name="id" value="${post.getId()}" />
    <input name="title" value="${post.getTitle()}" />
    <input name="url" value="${post.getUrl()}" />
    <input name="sr" value="${post.getSubreddit()}" />
    <input type="checkbox" name="sendreplies" value="true" 
      <c:if test="${post.isSendReplies()=='true'}"> checked </c:if> />
    <input name="date" value="${dateValue}">
    <button type="submit">Save Changes</button>
</form>
</body>
</html>

When this for is POSTed – we just save the chances:

@RequestMapping(value = "/updatePost/{id}", method = RequestMethod.POST)
public String updatePost(
  Model model, @PathVariable("id") Long id, @RequestParam Map<String, String> formParams) 
  throws ParseException {
    Post post = postReopsitory.findOne(id);
    post.setTitle(formParams.get("title"));
    post.setSubreddit(formParams.get("sr"));
    post.setUrl(formParams.get("url"));
    if (formParams.containsKey("sendreplies")) {
        post.setSendReplies(true);
    } else {
        post.setSendReplies(false);
    }
    post.setSubmissionDate(dateFormat.parse(formParams.get("date")));
    if (post.getSubmissionDate().before(new Date())) {
        model.addAttribute("msg", "Invalid date");
        return "submissionResponse";
    }
    postReopsitory.save(post);
    return "redirect:/posts";
}

10. Unschedule/Delete a Post

We’ll also provide a simple delete operation for any of the scheduled Posts:

@RequestMapping(value = "/deletePost/{id}", method = RequestMethod.DELETE)
@ResponseStatus(HttpStatus.OK)
public void deletePost(@PathVariable("id") Long id) {
    postReopsitory.delete(id);
}

Here’s how we call it from client side:

<a href="#" onclick="confirmDelete(${post.getId()})">Delete</a>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script>
function confirmDelete(id) {
    if (confirm("Do you really want to delete this post?") == true) {
    	deletePost(id);
    } 
}

function deletePost(id){
	$.ajax({
	    url: 'deletePost/'+id,
	    type: 'DELETE',
	    success: function(result) {
	    	window.location.href="posts"
	    }
	});
}
</script>

11. Conclusion

In this part of our Reddit case-study, we built the first non-trivial bit of functionality using the Reddit API – scheduling Posts.

This is a super-useful feature for a serious Reddit user, especially considering how time-sensitive Reddit submissions are.

Next – we’ll build out an even more helpful functionality to help with getting content upvoted on Reddit – machine learning suggestions.

The full implementation of this tutorial can be found in the github project – this is an Eclipse based project, so it should be easy to import and run as it is.

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

Baeldung Weekly Review 13

$
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

Let’s start with the major Spring releases this week:

>> Multiple UI Applications and a Gateway: Single Page Application with Spring and Angular JS Part VI

Part 6 of a highly useful series that I – for one – am following closely. This one is exploring the common Gateway further and also focuses on the front-end Angular aspects.

>> Using Google Protocol Buffers with Spring MVC-based REST Services

Another format to serialize data for a REST API – that’s not JSON. This can really come in handy for some APIs.

2. Java

>> Digging Deeper Into Java’s HashMap

Back to basics with a solid, in-depth intro to the HashMap.

>> Google Guava: 5 Things You Never Knew It Could Do

Very cool Guava usecases – these will definitely come in handy. Guava is an endless treasure-trove of super useful utilities.

>> How to batch DELETE statements with Hibernate

Like clockwork, another week, another in-depth foray into Hibernate batching. Can you tell I like high quality, consistent output?

>> jOOQ vs. Hibernate: When to Choose Which

Some perspective when it comes to using SQL (and jOOQ) either in conjunction with or as a replacement for an ORM such as Hibernate.

Most of us have some projects under our belts. However, unless you’re involved in a product targeted at developers, you’re very unlikely to have experience with more than – say – high double digits number of projects on the top end. Because of that, it’s always interesting to get insights from a wide customer base in the industry – for more perspective.

Also worth reading:

Webinars and presentations:

Time to upgrade:

3. Technical

>> Your Constructors Are Completely Irrational

Good notes on intelligently designing your constructors for the long haul. Also, constructors – most of the time – API, so it’s especially worth designing them well.

Also worth reading:

4. Musings

>> Some Changes to DaedTech Because I Want to Build a Thing

A bit of inspiration to put out work.

>> Salary Negotiations: Win by Losing

This one falls into the category of “Do Yourself a Favor and Read It”.

>> Fifty

An introspective piece from someone I quite enjoy reading.

>> Finding Topics as a New Blogger

Finding topics to blog about can be tricky. It can also get you pumped up – here are a few ideas and techniques of how to do it.

>> Someone just quit

Some good insights and takeaways into management how to deal with someone quitting.

5. Comics

And my favorite Dilberts of the week:

>> The Google 20%

>> The Smartphone

>> Awkward

6. 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:

Metrics for your Spring REST API

$
0
0

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

1. Overview

In this tutorial we’ll integrate basic Metrics into a Spring REST API.

We’ll build out the metric functionality first using simple Servlet Filters, then using a Spring Boot Actuator.

2. The web.xml

Let’s start by registering a filter – “MetricFilter” – into the web.xml of our app:

<filter>
    <filter-name>metricFilter</filter-name>
    <filter-class>org.baeldung.web.metric.MetricFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>metricFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Note how we’re mapping the filter to cover all requests coming in – “/*” – which is of course fully configurable.

3. The Servlet Filter

Now – let’s create our custom filter:

public class MetricFilter implements Filter {

    private MetricService metricService;

    @Override
    public void init(FilterConfig config) throws ServletException {
        metricService = (MetricService) WebApplicationContextUtils
         .getRequiredWebApplicationContext(config.getServletContext())
         .getBean("metricService");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
      throws java.io.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);
    }
}

Since the filter isn’t a standard bean, we’re not going to inject the metricService but instead retrieve it manually – via the ServletContext.

Also note that we’re continuing the execution of the filter chain by calling the doFilter API here.

4. Metric – Status Code Counts

Next – let’s take a look at our simple MetricService:

@Service
public class MetricService {

    private Map<Integer, Integer> statusMetric;

    public void increaseCount(String request, int status) {
        Integer statusCount = statusMetric.get(status);
        if (statusCount == null) {
            statusMetric.put(status, 1);
        } else {
            statusMetric.put(status, statusCount + 1);
        }
    }

    public Map getStatusMetric() {
        return statusMetric;
    }
}

We’re using an in memory Map to hold the counts for each type of HTTP status code.

Now – to display this basic metric – we’re going to map it to a Controller method:

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

And here is a sample response:

{  
    "404":1,
    "200":6,
    "409":1
}

5. Metric – Status Codes by Request

Next – let’s record metrics for Counts by Request:

@Service
public class MetricService {

    private Map<String, HashMap<Integer, Integer>> metricMap;

    public void increaseCount(String request, int status) {
        HashMap<Integer, Integer> statusMap = metricMap.get(request);
        if (statusMap == null) {
            statusMap = new HashMap<Integer, Integer>();
        }

        Integer count = statusMap.get(status);
        if (count == null) {
            count = 1;
        } else {
            count++;
        }
        statusMap.put(status, count);
        metricMap.put(request, statusMap);
    }

    public Map getFullMetric() {
        return metricMap;
    }
}

We’ll display the metric results via the API:

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

Here’s how these metrics look like:

{
    "GET /users":
    {
        "200":6,
        "409":1
    },
    "GET /users/1":
    {
        "404":1
    }
}

According to the above example the API had the following activity:

  • “7” requests to “GET /users“.
  • “6” of them resulted in “200” status code responses and only one in a “409”.

6. Metric – Time Series Data

Overall counts are somewhat useful in an application, but if the system has been running for a significant amount of time – it’s hard to tell what these metrics actually mean.

You need the context of time in order for the data to make sense and be easily interpreted.

Let’s now build a simple time-based metric; we’ll keep record of status code counts per minute – as follows:

@Service
public class MetricService{

    private Map<String, HashMap<Integer, Integer>> timeMap;
    private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");

    public void increaseCount(String request, int status) {
        String time = dateFormat.format(new Date());
        HashMap<Integer, Integer> statusMap = timeMap.get(time);
        if (statusMap == null) {
            statusMap = new HashMap<Integer, Integer>();
        }

        Integer count = statusMap.get(status);
        if (count == null) {
            count = 1;
        } else {
            count++;
        }
        statusMap.put(status, count);
        timeMap.put(time, statusMap);
    }
}

And the getGraphData():

public Object[][] getGraphData() {
    int colCount = statusMetric.keySet().size() + 1;
    Set<Integer> allStatus = statusMetric.keySet();
    int rowCount = timeMap.keySet().size() + 1;
    Object[][] result = new Object[rowCount][colCount];
    result[0][0] = "Time";

    int j = 1;
    for (int status : allStatus) {
        result[0][j] = status;
        j++;
    }
    int i = 1;
    Map<Integer, Integer> tempMap;
    for (Entry<String, HashMap<Integer, Integer>> entry : timeMap.entrySet()) {
        result[i][0] = entry.getKey();
        tempMap = entry.getValue();
        for (j = 1; j < colCount; j++) {
            result[i][j] = tempMap.get(result[0][j]);
            if (result[i][j] == null) {
                result[i][j] = 0;
            }
        }
        i++;
    }

    return result;
}

We’re now going to map this to the API:

@RequestMapping(value = "/metric-graph-data", method = RequestMethod.GET)
@ResponseBody
public Object[][] getMetricData() {
    return metricService.getGraphData();
}

And finally – we’re going to render it out using Google Charts:

<html>
<head>
<title>Metric Graph</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">
google.load("visualization", "1", {packages : [ "corechart" ]});

function drawChart() {
$.get("/metric-graph-data",function(mydata) {
    var data = google.visualization.arrayToDataTable(mydata);
    var options = {title : 'Website Metric',
                   hAxis : {title : 'Time',titleTextStyle : {color : '#333'}},
                   vAxis : {minValue : 0}};

    var chart = new google.visualization.AreaChart(document.getElementById('chart_div'));
    chart.draw(data, options);

});

}
</script>
</head>
<body onload="drawChart()">
    <div id="chart_div" style="width: 900px; height: 500px;"></div>
</body>
</html>

7. Using Spring Boot Actuator

In the next few sections, we’re going to hook into the Actuator functionality in Spring Boot to present our metrics.

First – we’ll need to add the actuator dependency to our pom.xml:

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

8. The MetricFilter

Next – we can turn the MetricFilter – into an actual Spring bean:

@Component
public class MetricFilter implements Filter {

    @Autowired
    private MetricService metricService;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
      throws java.io.IOException, ServletException {
        chain.doFilter(request, response);

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

This is of course a minor simplification – but one that’s worth doing to get rid of the previously manual wiring of dependencies.

9. Using CounterService

Let’s now use the CounterService to count occurrences for each Status Code:

@Service
public class MetricService {

    @Autowired
    private CounterService counter;

    private List<String> statusList;

    public void increaseCount(int status) {
        counter.increment("status." + status);
        if (!statusList.contains("counter.status." + status)) {
            statusList.add("counter.status." + status);
        }
    }
}

10. Export Metrics using MetricRepository

Next – we need to export the metrics – using the MetricRepository:

@Service
public class MetricService {

    @Autowired
    private MetricRepository repo;

    private List<ArrayList<Integer>> statusMetric;
    private List<String> statusList;
    
    @Scheduled(fixedDelay = 60000)
    private void exportMetrics() {
        Metric<?> metric;
        ArrayList<Integer> statusCount = new ArrayList<Integer>();
        for (String status : statusList) {
            metric = repo.findOne(status);
            if (metric != null) {
                statusCount.add(metric.getValue().intValue());
                repo.reset(status);
            } else {
                statusCount.add(0);
            }
        }
        statusMetric.add(statusCount);
    }
}

Note that we’re storing counts of status codes per minute.

11. Draw Graph using Metrics

Finally – let’s represent these metrics via a 2 dimension array – so that we can then graph them:

public Object[][] getGraphData() {
    Date current = new Date();
    int colCount = statusList.size() + 1;
    int rowCount = statusMetric.size() + 1;
    Object[][] result = new Object[rowCount][colCount];
    result[0][0] = "Time";

    int j = 1;
    for (String status : statusList) {
        result[0][j] = status;
        j++;
    }

    ArrayList<Integer> temp;
    for (int i = 1; i < rowCount; i++) {
        temp = statusMetric.get(i - 1);
        result[i][0] = dateFormat.format
          (new Date(current.getTime() - (60000 * (rowCount - i))));
        for (j = 1; j <= temp.size(); j++) {
            result[i][j] = temp.get(j - 1);
        }
        while (j < colCount) {
            result[i][j] = 0;
            j++;
        }
    }

    return result;
}

And here is our Controller method getMetricData():

@RequestMapping(value = "/metric-graph-data", method = RequestMethod.GET)
@ResponseBody
public Object[][] getMetricData() {
    return metricService.getGraphData();
}

And here is a sample response:

[
    ["Time","counter.status.302","counter.status.200","counter.status.304"],
    ["2015-03-26 19:59",3,12,7],
    ["2015-03-26 20:00",0,4,1]
]

12. Conclusion

This article we explored a few simple ways to build out some basic metrics capabilities into your Spring web application.

Note that the counters aren’t thread-safe – so they might not be exact without using something like atomic numbers. This was deliberate just because the delta should be small and 100% accuracy isn’t the goal – rather, spotting trends early is.

There are of course more mature ways to record HTTP metrics in an application, but this is a simple, lightweight and super-useful way to do it without the extra complexity of a full-fledged tool.

The full implementation of this article can be found in the github project – this is an Eclipse based project, so it should be easy to import and run as it is.

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

Baeldung Weekly Review 14

$
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

>> Writing Clean Tests – Trouble in Paradise

Maybe you’ve been writing tests for a while. And maybe you’re doing TDD, so – overall, you think you have testing dialed in. But, when it comes to testing, there’s always room for improvement. A lot of improvement.

I like this piece because of that – it’s not going to necessarily give you solutions, but it does raise a few damn good questions.

>> While You Were Sleeping: The Top New Java 8 Additions

Some interesting features I knew nothing about.

>> SELECT statements batch fetching with JDBC and Hibernate

Batching of SELECTs was the next natural step in this series.

>> Check out Dave Syer’s “Spring Security and AngularJS” blog series converted to tutorial

I covered each piece of this super-useful series over the course of several weekly reviews. The entire six parter has been converted to a single, clean tutorial.

Also worth reading:

Webinars and presentations:

Time to upgrade:

2. Technical

>> Your Tests Are Dragging You Down

Not keeping things in your head – seems simple enough but it can have a profound impact. When coding, it will lead to TDD purely for the design benefit, to thinking incrementally about your system and to a huge productivity increase.

And overall, it will of course lead you to overusing Trello.

>> Ed Burns Discusses HTTP/2 and the Java EE Servlet 4 Specification

HTTP/2 is coming. We live in interesting times.

Also worth reading:

3. Musings

>> The Myth of the Full-stack Developer

Very interesting read on being a full-stack developer and what that actually means.

>> Introverts as Leaders (Briefly)

A quick read about how being introverted can play into being a good leader.

>> Management style

A fun read discussing different styles of management / personas.

Also worth reading:

4. Comics

And my favorite Dilberts and XKCD of the week:

>> Introverted

>> A human vibe

>> Opportunity (xkcd)

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:

Registration – Password Strength and Rules

$
0
0

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

1. Overview

In this quick tutorial, we’ll look at how to implement and show proper password constraints during registration. Things like – the password should contain a special character, or it should be at least 8 characters long.

We want to be able to use powerful password rules – but we don’t want to actually implement these rules manually. So, we’re going to make good use of the mature Passay library.

2. Custom Password Constraint

First – let’s create a custom constraint ValidPassword:

@Documented
@Constraint(validatedBy = PasswordConstraintValidator.class)
@Target({ TYPE, FIELD, ANNOTATION_TYPE })
@Retention(RUNTIME)
public @interface ValidPassword {

    String message() default "Invalid Password";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

}

And use it in the UserDto:

@ValidPassword
private String password;

3. Custom Password Validator

Now – let’s use the library to create some powerful password rules without having to actually manually implement any of them.

We’ll create the password validator PasswordConstraintValidator – and we’ll define the rules for the password:

public class PasswordConstraintValidator implements ConstraintValidator<ValidPassword, String> {

    @Override
    public void initialize(ValidPassword arg0) {
    }

    @Override
    public boolean isValid(String password, ConstraintValidatorContext context) {
        PasswordValidator validator = new PasswordValidator(Arrays.asList(
           new LengthRule(8, 30), 
           new UppercaseCharacterRule(1), 
           new DigitCharacterRule(1), 
           new SpecialCharacterRule(1), 
           new WhitespaceRule()));

        RuleResult result = validator.validate(new PasswordData(password));
        if (result.isValid()) {
            return true;
        }
        context.disableDefaultConstraintViolation();
        context.buildConstraintViolationWithTemplate(
          Joiner.on("\n").join(validator.getMessages(result)))
          .addConstraintViolation();
        return false;
    }
}

Also note how we used the ConstraintValidatorContext to intelligently display a better error message than the default.

Finally, let’s also add the Passay library into our pom:

<dependency>
	<groupId>org.passay</groupId>
	<artifactId>passay</artifactId>
	<version>1.0</version>
</dependency>

For a bit of historical info, Passay is the descendant of the venerable vt-password Java library.

4. JS Password Meter

Now that the server side is done, let’s take a look at the client side and implement a simple Password Strength” functionality with JavaScript.

We’ll use a simple jQuery plugin – jQuery Password Strength Meter for Twitter Bootstrap – to show the password strength in registration.html:

<input id="password" name="password" type="password"/>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="pwstrength.js"></script>                  
<script type="text/javascript">
$(document).ready(function () {
    options = {
        common: {minChar:8},
        ui: {
            showVerdictsInsideProgressBar:true,
            showErrors:true,
            errorMessages:{
                wordLength: '<spring:message code="error.wordLength"/>',
                wordNotEmail: '<spring:message code="error.wordNotEmail"/>',
                wordSequences: '<spring:message code="error.wordSequences"/>',
                wordLowercase: '<spring:message code="error.wordLowercase"/>',
                wordUppercase: '<spring:message code="error.wordUppercase"/>',
                wordOneNumber: '<spring:message code="error.wordOneNumber"/>',
                wordOneSpecialChar: '<spring:message code="error.wordOneSpecialChar"/>'
            }
        }
    };
    $('#password').pwstrength(options);
});
</script>

5. Conclusion

And that’s it – a simple but very useful way to show the strength of the password on the client side and enforce certain password rules on the server side.

The full implementation of this tutorial can be found in the github project – this is an Eclipse based project, so it should be easy to import and run as it is.

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

Baeldung Weekly Review 15

$
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 Spring achieves compatibility with Java 6, 7 and 8

Insight into intelligently building Spring with Java 8 support but not actually requiring Java 8.

There are takeaways here for anyone who’s maintaining a library/framework/API in Java land.

>> Spring From the Trenches: Injecting Property Values Into Configuration Beans

If you’re dealing with a lot of properties in your project, this is definitely solid advice to follow – makes managing all of that complexity a lot easier.

>> Result Set Mapping: The Basics

The first of what looks to be a promising series about working with the JPA and Result Sets.

>> How does Hibernate store second-level cache entries

Exploring the Hibernate Cache fun and speed. Lot of things to learn from this piece.

>> Spring Enable* annotation – writing a custom Enable annotation

An @EnableFoo style annotation can definitely come in handy in complex systems and there’s no reason to be restricted at just the default ones in the framework.

>> Log Management Tools Face-Off: Splunk vs. Logstash vs. Sumo Logic

Log Management, visibility, exploration and analysis are oh-so important even for medium-sized projects. There’s just so much you can get out of having a proper log solution implemented in production.

These tools also pretty much pay for themselves the first few times you save a good few hours of grepping through your text based log files. The one I regularly use these days is the ELK stack.

Also worth reading:

Webinars and presentations:

Time to upgrade:

2. Technical

>> What Story Does Your Code Tell?

There’s always one more step on the path to writing clean, maintainable code.

How that code reads for someone who’s not you definitely worth being aware of since so much of the teams time is spent reading code.

Also worth reading:

3. Musings

>> Given Enough Money, All Bugs Are Shallow

This is an important lesson for all open source projects and for our industry in general. It’s very pragmatic and it’s definitely a long read, but it has a good chance of nudging the ecosystem along in the right direction.

>> Please Direct all Inquiries to My Agent

An interesting idea – no clue if it’s practical or not, but it was fun to read.

>> Abandon “sorta high”/”P1″ priority.

Have you ever paused for a brief second looking at the “Priority” field when opening an issue in JIRA?

It’s an indication that something about this can be improved, and this piece is a solid proposal on how to do it.

Also worth reading:

4. Comics

And my favorite Dilberts of the week:

>> Practical Joke

>> Pick Another Defense

>> Instead of a Raise

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:

REST Query Language with RSQL

$
0
0

I usually post about REST APIs and HTTP on Twitter - you can follow me there:

Overview

In this fifth article of the series we’ll illustrate building the REST API Query language with the help of a cool library – rsql-parser.

RSQL is a super-set of the Feed Item Query Language (FIQL) – a clean and simple filter syntax for feeds; so it fits quite naturally into a REST API.

1. Preparations

First, let’s add a maven dependency to the library:

<dependency>
    <groupId>cz.jirutka.rsql</groupId>
    <artifactId>rsql-parser</artifactId>
    <version>2.0.0</version>
</dependency>

And also define the main entity we’re going to be working with throughout the examples – User:

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
 
    private String firstName;
    private String lastName;
    private String email;
 
    private int age;
}

2. Parse the Request to

The way RSQL expressions are represented internally is in the form of nodes and the visitor pattern is used parse out the input.

With that in mind, we’re going to implement the RSQLVisitor interface and create our own visitor implementation – CustomRsqlVisitor:

public class CustomRsqlVisitor<T> implements RSQLVisitor<Specification<User>, Void> {

    private UserRsqlSpecBuilder builder;

    public CustomRsqlVisitor() {
        builder = new UserRsqlSpecBuilder();
    }

    @Override
    public Specification<User> visit(AndNode node, Void param) {
        return builder.createSpecification(node);
    }

    @Override
    public Specification<User> visit(OrNode node, Void param) {
        return builder.createSpecification(node);
    }

    @Override
    public Specification<User> visit(ComparisonNode node, Void params) {
        return builder.createSpecification(node);
    }
}

Now we need to deal with persistence and construct our query out of each of these nodes.

We’re going to use the Spring Data JPA Specifications we used before – and we’re going to implement a Specification builder to construct Specifications out of each of these nodes we visit:

public class UserRsqlSpecBuilder {

    public Specifications<User> createSpecification(Node node) {
        if (node instanceof LogicalNode) {
            return createSpecification((LogicalNode) node);
        }
        if (node instanceof ComparisonNode) {
            return createSpecification((ComparisonNode) node);
        }
        return null;
    }

    public Specifications<User> createSpecification(LogicalNode logicalNode) {
        List<Specifications<User>> specs = new ArrayList<Specifications<User>>();
        Specifications<User> temp;
        for (Node node : logicalNode.getChildren()) {
            temp = createSpecification(node);
            if (temp != null) {
                specs.add(temp);
            }
        }

        Specifications<User> result = specs.get(0);
        if (logicalNode.getOperator() == LogicalOperator.AND) {
            for (int i = 1; i < specs.size(); i++) {
                result = Specifications.where(result).and(specs.get(i));
            }
        } else if (logicalNode.getOperator() == LogicalOperator.OR) {
            for (int i = 1; i < specs.size(); i++) {
                result = Specifications.where(result).or(specs.get(i));
            }
        }

        return result;
    }

    public Specifications<User> createSpecification(ComparisonNode comparisonNode) {
        Specifications<User> result = Specifications.where(
          new UserRsqlSpecification(
            comparisonNode.getSelector(), 
            comparisonNode.getOperator(), 
            comparisonNode.getArguments()
          )
        );
        return result;
    }
}

Note how:

  • LogicalNode is an AND/OR Node and has multiple children
  • ComparisonNode has no children and it hold the Selector, Operator and the Arguments

For example, for a query “name==john” – we have:

  1. Selector: “name”
  2. Operator: “==”
  3. Arguments:[john]

3. Create Custom Specification

When constructing the query we made use of a custom User Specification – “UserRsqlSpecification“:

public class UserRsqlSpecification implements Specification<User> {
    private String property;
    private ComparisonOperator operator;
    private List<String> arguments;

    public UserRsqlSpecification(
      String property, ComparisonOperator operator, List<String> arguments) {
        super();
        this.property = property;
        this.operator = operator;
        this.arguments = arguments;
    }

    @Override
    public Predicate toPredicate(
      Root<User> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
        
        List<Object> args = castArguments(root);
        Object argument = args.get(0);
        switch (RsqlSearchOperation.getSimpleOperator(operator)) {

        case EQUAL: {
            if (argument instanceof String) {
                return builder.like(
                  root.<String> get(property), argument.toString().replace('*', '%'));
            } else if (argument == null) {
                return builder.isNull(root.get(property));
            } else {
                return builder.equal(root.get(property), argument);
            }
        }
        case NOT_EQUAL: {
            if (argument instanceof String) {
                return builder.notLike(
                  root.<String> get(property), argument.toString().replace('*', '%'));
            } else if (argument == null) {
                return builder.isNotNull(root.get(property));
            } else {
                return builder.notEqual(root.get(property), argument);
            }
        }
        case GREATER_THAN: {
            return builder.greaterThan(root.<String> get(property), argument.toString());
        }
        case GREATER_THAN_OR_EQUAL: {
            return builder.greaterThanOrEqualTo(
              root.<String> get(property), argument.toString());
        }
        case LESS_THAN: {
            return builder.lessThan(root.<String> get(property), argument.toString());
        }
        case LESS_THAN_OR_EQUAL: {
            return builder.lessThanOrEqualTo(
              root.<String> get(property), argument.toString());
        }
        case IN:
            return root.get(property).in(args);
        case NOT_IN:
            return builder.not(root.get(property).in(args));
        }

        return null;
    }

    private List<Object> castArguments(Root<User> root) {
        List<Object> args = new ArrayList<Object>();
        Class<? extends Object> type = root.get(property).getJavaType();

        for (String argument : arguments) {
            if (type.equals(Integer.class)) {
                args.add(Integer.parseInt(argument));
            } else if (type.equals(Long.class)) {
                args.add(Long.parseLong(argument));
            } else {
                args.add(argument);
            }
        }

        return args;
    }
}

And here is our enum “RsqlSearchOperation which holds default rsql-parser operators:

public enum RsqlSearchOperation {
    EQUAL(RSQLOperators.EQUAL), 
    NOT_EQUAL(RSQLOperators.NOT_EQUAL), 
    GREATER_THAN(RSQLOperators.GREATER_THAN), 
    GREATER_THAN_OR_EQUAL(RSQLOperators.GREATER_THAN_OR_EQUAL), 
    LESS_THAN(RSQLOperators.LESS_THAN), 
    LESS_THAN_OR_EQUAL(RSQLOperators.LESS_THAN_OR_EQUAL), 
    IN(RSQLOperators.IN), 
    NOT_IN(RSQLOperators.NOT_IN);

    private ComparisonOperator operator;

    private RsqlSearchOperation(ComparisonOperator operator) {
        this.operator = operator;
    }

    public static RsqlSearchOperation getSimpleOperator(ComparisonOperator operator) {
        for (RsqlSearchOperation operation : values()) {
            if (operation.getOperator() == operator) {
                return operation;
            }
        }
        return null;
    }
}

4. Test Search Queries

Let’s now start testing our new and flexible operations through some real-world scenarios:

First – let’s initialize the data:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { PersistenceConfig.class })
@Transactional
@TransactionConfiguration
public class RsqlTest {

    @Autowired
    private UserRepository repository;

    private User userJohn;

    private User userTom;

    @Before
    public void init() {
        userJohn = new User();
        userJohn.setFirstName("john");
        userJohn.setLastName("doe");
        userJohn.setEmail("john@doe.com");
        userJohn.setAge(22);
        repository.save(userJohn);

        userTom = new User();
        userTom.setFirstName("tom");
        userTom.setLastName("doe");
        userTom.setEmail("tom@doe.com");
        userTom.setAge(26);
        repository.save(userTom);
    }
}

Now let’s test the different operations:

4.1. Test Equality

In the following example – we’ll search for users by their first and last name:

@Test
public void givenFirstAndLastName_whenGettingListOfUsers_thenCorrect() {
    Node rootNode = new RSQLParser().parse("firstName==john;lastName==doe");
    Specification<User> spec = rootNode.accept(new CustomRsqlVisitor<User>());
    List<User> results = repository.findAll(spec);

    assertThat(userJohn, isIn(results));
    assertThat(userTom, not(isIn(results)));
}

4.2. Test Negation

Next, let’s search for users that by the their first name not “john”:

@Test
public void givenFirstNameInverse_whenGettingListOfUsers_thenCorrect() {
    Node rootNode = new RSQLParser().parse("firstName!=john");
    Specification<User> spec = rootNode.accept(new CustomRsqlVisitor<User>());
    List<User> results = repository.findAll(spec);

    assertThat(userTom, isIn(results));
    assertThat(userJohn, not(isIn(results)));
}

4.3. Test Greater Than

Next – we will search for users with age greater than “25”:

@Test
public void givenMinAge_whenGettingListOfUsers_thenCorrect() {
    Node rootNode = new RSQLParser().parse("age>25");
    Specification<User> spec = rootNode.accept(new CustomRsqlVisitor<User>());
    List<User> results = repository.findAll(spec);

    assertThat(userTom, isIn(results));
    assertThat(userJohn, not(isIn(results)));
}

4.4. Test Like

Next – we will search for users with their first name starting with “jo”:

@Test
public void givenFirstNamePrefix_whenGettingListOfUsers_thenCorrect() {
    Node rootNode = new RSQLParser().parse("firstName==jo*");
    Specification<User> spec = rootNode.accept(new CustomRsqlVisitor<User>());
    List<User> results = repository.findAll(spec);

    assertThat(userJohn, isIn(results));
    assertThat(userTom, not(isIn(results)));
}

4.5. Test IN

Next – we will search for users their first name is “john” or “jack“:

@Test
public void givenListOfFirstName_whenGettingListOfUsers_thenCorrect() {
    Node rootNode = new RSQLParser().parse("firstName=in=(john,jack)");
    Specification<User> spec = rootNode.accept(new CustomRsqlVisitor<User>());
    List<User> results = repository.findAll(spec);

    assertThat(userJohn, isIn(results));
    assertThat(userTom, not(isIn(results)));
}

5. UserController

Finally – let’s tie it all in with the controller:

@RequestMapping(method = RequestMethod.GET, value = "/users")
@ResponseBody
public List<User> findAllByRsql(@RequestParam(value = "search") String search) {
    Node rootNode = new RSQLParser().parse(search);
    Specification<User> spec = rootNode.accept(new CustomRsqlVisitor<User>());
    return dao.findAll(spec);
}

Here’s a sample URL:

http://localhost:8080/users?search=firstName==jo*;age<25

And the response:

[{
    "id":1,
    "firstName":"john",
    "lastName":"doe",
    "email":"john@doe.com",
    "age":24
}]

6. Conclusion

This tutorial illustrated how to build out a Query/Search Language for a REST API without having to re-invent the syntax and instead using FIQL / RSQL.

The full implementation of this article can be found in the github project – this is an Eclipse based project, so it should be easy to import and run as it is.

I usually post about REST APIs and HTTP on Twitter - you can follow me there:


Building a REST Query Language

$
0
0

I usually post about REST APIs and HTTP on Twitter - you can follow me there:

A mature REST API can be a lot of work, and publishing Resources in a flexible way is usually a balancing act.

On the one hand, you want to allow the client to search for information in many flexible ways. On the other hand, you don’t want to implement to many operations.

A Search/Query Language for the API makes the most sense – it allows for a single, clean operation while still opening up the API for powerful searching.

>> REST Query Language with Spring and JPA Criteria

Explores the implementation of the Query Language using JPA Criteria for the persistence layer.

>> REST Query Language with Spring Data JPA Specifications

Explores Spring Data JPA Specifications for the persistence layer of the Query Language.

>> REST Query Language with Spring Data JPA and QueryDSL

Next – the persistence layer is implemented using QueryDSL.

>> REST Query Language – Advanced Search Operations

Additional, more advanced operations added to the Query Language of the API.

>> REST Query Language with RSQL

Finally, instead of a custom Query Language we’re going to use Feed Item Query Language (FIQL) and its super-set – RSQL.

I usually post about REST APIs and HTTP on Twitter - you can follow me there:

HttpClient 4 Tutorial

$
0
0

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

This is a comprehensive guide to using Apache HttpClient 4 – from starting out to advanced configuration and best practices.

1. HttpClient Basics

Learn the basics of sending a Request, receiving and interpreting the Response and configure the Client for basic usage.

 

2. Advanced Usage

Go into more advanced usecases of configuring the client with SSL, Custom Cookies, different types of Requests, etc.

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 Dev stuff on Twitter - you can follow me there:

Jackson JSON Tutorial

$
0
0

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

This tutorial illustrates the most common Jackson 2 tasks, problems and solutions while marshalling and unmarshalling JSON.

Basic Jackson Marshalling

Learn quickly how to serialize a Java Entity to a JSON String – and control the mapping process perfectly, to reach the exact JSON format you’re aiming for.

 

Basic Jackson Unmarshalling

Learn how to deserialize a JSON String into a Java Entity – no matter how weird the JSON input is, we need to map it to a well defined Java Entity class.

 

Advanced Jackson Marshalling

Learn advanced serialization configuration and tuning to deal with conditions, various data types and custom Jackson exceptions.

 

Advanced Jackson Unmarshalling

Learn more advanced tips and tricks for unmarshalling JSON input into Java Entity classes.

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 Dev stuff on Twitter - you can follow me there:

Baeldung Weekly Review 16

$
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 From the Trenches: Returning Runtime Configuration as JSON

Having the core properties my application uses logged on startup is important – it makes it easy to see the exact layout of the environment and spot potential problems quickly.

The simple but powerful mechanism presented in this article makes that straightforward to set up and can easily be extended to whatever family of properties you might use in your application.

>> Result Set Mapping: Complex Mappings

Last week this series explored basic mappings – now it takes a deeper dive into more advanced usecases such as mapping a query result to multiple entities.

>> Using Apache Kafka for Integration and Data Processing Pipelines with Spring

A good starting point for both Kafka as well as Spring Integration and Spring XD.

>> Storytelling with Tests #1: test names and granularity

Another article about testing? Hold your horses – this one actually discusses a real-world, sufficiently complex example and brings some valuable insights to the table. Good stuff.

Also worth reading:

Webinars and presentations:

Time to upgrade:

2. Technical

>> KISS With Essential Complexity

Complexity in code – definitely worth a read.

I like reading pieces by people that are actually developing software for a living – there’s something pragmatic and real about this kind of writing. Yes – you can and should make things better, but don’t expect textbook-simple code in your project.

>> Microservice Design Patterns

An solid article illustrating the ways you can lay out a microservices architecture. Pushing past the hype – this is actually useful.

>> Things to consider before jumping to enterprise caching

A word of warning not to enable caching without first understanding what potential data consistency issues you are introducing into your app. There are certainly ways – good ways – to deal with these issues, but it’s important to have a good grasp of what these are first.

Also worth reading:

3. Musings

>> My Blog: If I Build It, Will They Come?

A very useful read if you’re thinking of starting up a blog, putting your work out there and iterating in public.

What I would add is that – if you are thinking of starting – do that today, not tomorrow – tomorrow is never.

4. Comics

And my favorite comics of the week:

>> Code Quality

>> I guess I don’t really need it

>> Are you trying to kill me?

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 17

$
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

>> Java version statistics: 2015 edition

An inside look into Java 8 adoption in the industry.

>> Domain Mapping with the ModelMapper library

A solid introduction to a very common problem – mapping between different entities. I’ve been looking at using this library for a while now – maybe it’s time to give it a go.

>> Maven Integration Tests in Extra Source Folder

Quick and to the point guide for separating the integration tests in your Maven project.

An alternative approach that saves you from using the extra plugin is to simply define naming patterns for your tests and run them based on a regular expression.

>> Spring From the Trenches: Returning Git Commit Information as JSON

A detailed, clear guide to publishing Git information about a deployed application. This is the kind of thing where you know you’re no longer working on a toy app.

>> Spring Security 4.0: WebSocket, Spring Data and Test Support

A succinct rundown of the newly available features of Spring Security 4.

>> Spring: injecting lists, maps, optionals and getBeansOfType() pitfalls

Wiring in more complex beans in Spring can be tricky business – but it can certainly be done and done well.

>> 7 Advanced JVM Production Debugging Techniques To Skyrocket Your High-Scale Environment

Looks like it’s going to be an interesting event about working with Java systems in production.

Also worth reading:

Webinars and presentations:

Time to upgrade:

2. Technical

>> Your Password is Too Damn Short

Mandatory reading (again).

Also worth reading:

3. Musings

>> The Swamp

A quick, beautifully simple analysis of the oh-so common problem of unwarranted optimism when tackling complex, difficult problems.

>> A Dreyfus model for Agile adoption

Very interesting article on the maturity with which we’re practicing Agile.

This week is packed with a surprising number of great pieces.

>> Defining The Corporate Hierarchy

A promising re-definition of Loser/Clueless/Sociopath corporate archetypes to a milder but perhaps more nuanced topology. A book is in the works as well.

>> Carnival Cash: The Cult of Seniority

A good dose of pragmatism and a great read for engineers starting out.

>> To be creative, work alone

It’s hard – almost impossible – to get into a state of flow when you’re not alone; a case well made.

4. Comics

And my favorite Dilberts of the week:

>> Would you like to be my idiot?

>> Was alcohol involved?

>> Easiest round of layoffs ever

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:

Viewing all 3450 articles
Browse latest View live


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