Fork me on GitHub

Programming, Internet & more

Log request body with Spring Boot

Recently, one of my spring boot applications was running into a severe internal server error. I’ve tried to debug the issue but had a really hard time because I had no clue what the actual JSON payload of the problematic HTTP request was. The HTTP headers were not much of a help because the issue really was dependent on the quite large JSON body.

To make it even worse, it was not possible to just log the RequestBody in my RestController because spring boot was not even able to deserialize the JSON payload!

To capture the problematic request, I’ve had rather simple requirements: Just log the whole JSON payload in an HTTP request everytime an internal server error occurs.

After some time of searching on the net, I’ve realized that it’s not easy to find a simple and good solution without introducing a ton of external libraries.

Solution – A filter to log the JSON request payload

I’ve written my own solution which is based on the tracing actuator from spring boot. Unfortunately, spring boots tracing filter does not allow to log the body of HTTP requests. Furthermore, the tracing filter will log ALL requests, which is not what I wanted.

My solution is a custom logging filter which will write the body of the request in case of an internal server error to the standard logger. So, it’s up to the logger where to put these data. Additionally, it will only print the request body in case of an error, which will keep the log clean most of the time.

The filter is placed near the very end of the filter chain to make sure all other filters in the chain (which might change/enrich the request) are already passed. It is also worth noting that you cannot just naively read the body of a request. That’s because the body is realized as an InputStream which gets consumed on the first read. Instead, it is necessary to wrap the body and copy the content of the stream while reading. Therefore, it is only possible to access the body content AFTER the actual processing of the request was done.

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.Principal;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.autoconfigure.web.ErrorAttributes;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.WebUtils;

/**
* A filter which logs web requests that lead to an error in the system.
*
*/

@Component
public class LogRequestFilter extends OncePerRequestFilter implements Ordered {

    private final Log logger = LogFactory.getLog(getClass());

    // put filter at the end of all other filters to make sure we are processing after all others
    private int order = Ordered.LOWEST_PRECEDENCE - 8;
    private ErrorAttributes errorAttributes;

    @Override
    public int getOrder() {
        return order;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {
        ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request);

        int status = HttpStatus.INTERNAL_SERVER_ERROR.value();

        // pass through filter chain to do the actual request handling
        filterChain.doFilter(wrappedRequest, response);
        status = response.getStatus();

        // only log request if there was an error
        if (status == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
            Map<String, Object> trace = getTrace(wrappedRequest, status);

            // body can only be read after the actual request handling was done!
            getBody(wrappedRequest, trace);
            logTrace(wrappedRequest, trace);
        }
    }

    private void getBody(ContentCachingRequestWrapper request, Map<String, Object> trace) {
        // wrap request to make sure we can read the body of the request (otherwise it will be consumed by the actual
        // request handler)
        ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
        if (wrapper != null) {
            byte[] buf = wrapper.getContentAsByteArray();
            if (buf.length > 0) {
                String payload;
                try {
                    payload = new String(buf, 0, buf.length, wrapper.getCharacterEncoding());
                }
                catch (UnsupportedEncodingException ex) {
                    payload = "[unknown]";
                }

                trace.put("body", payload);
            }
        }
    }

    private void logTrace(HttpServletRequest request, Map<String, Object> trace) {
        Object method = trace.get("method");
        Object path = trace.get("path");
        Object statusCode = trace.get("statusCode");

        logger.info(String.format("%s %s produced an error status code '%s'. Trace: '%s'", method, path, statusCode,
        trace));
    }

    protected Map<String, Object> getTrace(HttpServletRequest request, int status) {
        Throwable exception = (Throwable) request.getAttribute("javax.servlet.error.exception");

        Principal principal = request.getUserPrincipal();

        Map<String, Object> trace = new LinkedHashMap<String, Object>();
        trace.put("method", request.getMethod());
        trace.put("path", request.getRequestURI());
        trace.put("principal", principal.getName());
        trace.put("query", request.getQueryString());
        trace.put("statusCode", status);

        if (exception != null && this.errorAttributes != null) {
            trace.put("error", this.errorAttributes
                .getErrorAttributes(new ServletRequestAttributes(request), true));
        }

        return trace;
    }

}
Posted in spring-boot | Leave a comment

Java 9 Private Interface Methods

This is another post about a new feature in Java 9. Today, we will talk about interfaces.

Private Interface Methods

Since Java 8 it is possible to use default methods in interfaces. These methods allowed us to implement functionality directly into an interface method. However, interface methods neededed to use one of the following visibility modifiers:

  • public
  • abstract
  • default
  • static

Please notice the absence of the private modifier. Since Java 9 it is now allowed to also use private methods in interfaces. Let’s take a look at this example:

public interface NumberCheck {

    public default boolean is42(int number) {
        if (isGreaterThan42(number) || isLessThan42(number)) {
            return false;
        }

        return true;
    }

    private boolean isGreaterThan42(int number) {
        return number > 42;
    }

    private boolean isLessThan42(int number) {
        return number < 42;
    }
}

The default method is42() makes use of the private helper methods to reduce the complexity of itself.

Why do we need private methods in interfaces?

Private interface methods become interesting while using them along with default methods. Before Java 9 it was possible to use default methods but eventually they become longer and longer and it was impossible to structure them properly. With Java 9 we can mainly see private methods as helper methods to split up default methods into several smaller ones like we would do with normal methods in classes.

What about static methods?

The rules of private methods also apply to private static methods. This means we can also use private static methods in interfaces:

public interface NumberCheck {

    public static boolean is42(int number) {
        if (isGreaterThan42(number) || isLessThan42(number)) {
            return false;
        }

        return true;
    }

    private static boolean isGreaterThan42(int number) {
        return number > 42;
    }

    private static boolean isLessThan42(int number) {
        return number < 42;
    }
}

Conclusion

Private interface methods are a great addition to the default methods introduced in Java 8. They play very well together and help to make code more structured and clean.

Posted in java9 | Leave a comment

Java 9 Diamond Operator and anonymous classes

This is another post about a new feature in Java 9. Today, I will show you that you can use the diamond operator along with anonymous classes.

Diamond Operator

We all know about the Diamond Operator since Java 7. It allows us to leave out the generic type on the right side of an expression. For example, let’s declare a simple list:

// before Java 7, explicit generic type declaration required
List<String> myList = new ArrayList<String>();

// since Java 7, diamond operator can be used
List<String> myList = new ArrayList<>();

Unfortunately, the use of the Diamond Operator was not possible for anonymous inner classes. The situation was not very consistent because the generic type of an anonymous inner class can be deduced just as from a normal class.

Since Java 9 the situation has improved and it is now possible to use the Diamond Operator also in anonymous inner classes. Let’s take a look at this example:

// possible since Java 9, diamond operator can be used
Predicate<Integer> predicate = new Predicate<>() {
    @Override
    public boolean test(Integer input) {
        return input == 42;
    }
};

Conclusion

With the introduction of lambda expressions in Java 8, anonymous inner classes have become less important. However, sometimes they are still useful and even can improve readability. Therefore, it is very good that the inconsistency with the Diamond Operator has been removed and it is now possible to use it also on anonymous inner classes.

Posted in java9 | Leave a comment

Java 9 Collection Initializer

Finally, after 3 years of waiting Java 9 has reached the world. This major release was delayed several times due to the integration of its famous modularization feature called “Project Jigsaw”. But besides that major feature, Java 9 contains several other minor improvements which make the life of a programmer easier but are not that popular.

Today, I would like to introduce the collection initializer feature.

Collection initializer

From time to time everyone has to initialize a collection of something. The typical Java way to do this was not very elegant compared to other programming languages. The most common way was to create a new collection and afterwards adding all the elements to it.

List<String> oldList = new ArrayList<>();
oldList.add("list");
oldList.add("initializer");
oldList.add("classic");
oldList.add("style");

There were a few other ways to initialize collections but none was as elegant as the new collection initializer feature. With Java 9 it is possible to do the initialization in one line as we know it from other languages.

List<String> newList = List.of("list", "initializer", "java 9", "style");

One important thing to notice is that the new “of” method does not return one of the common list implementations like ArrayList. Instead, a new immutable list implementation is used, which is not available in the public API. This means you cannot add or remove an item in the list. Doing this will result in an UnsupportedOperationException.

newList.add("boom"); // UnsupportedOperationException
newList.remove(0); // UnsupportedOperationException
newList.clear(); // UnsupportedOperationException

The “of” method can also be applied to the collection types Map and Set.

Set<String> set = Set.of("set", "initializer", "java 9", "style");
Map<Integer, String> map = Map.of(1, "map", 2, "initializer");

Conclusion

After so many years of waiting it was about time that we also got a simple way to initialize collections. There are some drawbacks, like immutability, but I think it was a reasonable decision to design it like that in regard to performance aspects.

Posted in java9 | Leave a comment

Authentication with Spring Boot, Angular 2 and Keycloak

Posted in spring, spring-boot | Leave a comment

CORS with Keycloak and Spring Boot

A quick tip regarding Keycloak, Spring Boot and some kind of JavaScript UI technology. When you’re trying to connect a JavaScript UI like Angular to a backend which is secured by Keycloak you have to be aware of CORS. I’m pretty sure you already know this.

However, there is a common pitfall, you have to enable CORS 2 times. First you have to enable CORS on Spring Boot level to make sure your origin is allowed to make calls to the REST api.

This can be done with a WebMvcConfigurerAdapter like this:

@Configuration
public class FilterProvider {

    @Bean
    public WebMvcConfigurer corsConfiguration() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/api/**")
                        .allowedMethods(HttpMethod.GET.toString(), HttpMethod.POST.toString(),
                                HttpMethod.PUT.toString(), HttpMethod.DELETE.toString(), HttpMethod.OPTIONS.toString())
                        .allowedOrigins("*");
            }
        };
    }
}

The second time you have to enable CORS is explicitly for Keycloak. If you forget this, your UI won’t be able to connect to your REST api. To enable CORS for Keycloak you can simply add the following to your application.properties file:

# Keycloak Enable CORS
keycloak.cors = true

The configuration is simple but trust me this could easily drive you crazy if you forget it. The browser will constantly complain about missing CORS headers. Additionally this error message could be misleading because you already enabled CORS for Spring Boot, right?

So, hopefully this tip will help you the next time when you’re running into this problem with Keycloak.

Posted in spring-boot | 3 Comments

Openkeepass: Android Support

Finally, openkeepass version 0.6.0 is out and its major feature in this version is the Android support.

The whole Xml serialization core was rewritten and now uses the Android friendly Simple XML Api.

Release Notes of openkeepass v0.6.0

Fixed

  • Fixed an issue that could bring the password of an entry into illegal state.
  • Fixed circular reference that occured when moving an entry multiple times to history.
  • Fixed incorrect data check in HashedBlockInputStream.
  • Fixed invalid placeholder in CrsAlgorithm.
  • Fixed leak of resources.
  • Changed

  • Changed XML library from JAXB to SimpleXML (Android support)
  • Changed crypto library from BouncyCastle to SpongyCastle (Android support)
  • Added

  • Contracts between builders and domain objects to support loose coupling
  • KeePass files can now be cloned by using cloneKeePassFile() in GroupZipper
  • GroupBuilder can now add and remove a list of entries
  • Times support in EntryBuilder
  • A lot of refactoring under the hood
  • Removed

  • getZippper() from KeePassFileBuilder
  • Where can I get it

    You can download it with maven or directly from GitHub.

    <dependency>
        <groupId>de.slackspace</groupId>
        <artifactId>openkeepass</artifactId>
        <version>0.6.0</version>
    </dependency>
    Posted in open-source | Leave a comment

    Authentication with Spring Boot, AngularJS and Keycloak

    Update

    There is a new version of this tutorial available for Angular 2 and Keycloak.

    Intro

    In this tutorial, I want to show you how to combine Keycloak with AngularJS and Spring Boot.

    The result will be a small application where you will get a frontend written in AngularJS and the big topics regarding authentication like user-registration, password reset, login page etc. are already solved.

    The backend is implemented with Spring Boot and will provide a REST API with business services exposed. Authentication in the backend is also solved by using Keycloak which means that all REST endpoints will be secured and that you will get all the user information in the backend as well as in the frontend.

    Requirements

    We will use the following tools and frameworks to build the application:

    Maven (Version 3.3.9)
    Spring Boot (Version 1.3.3.RELEASE)
    Keycloak (Version 1.9.1.Final)
    AngularJS (Version 1.5.0)

    Create a realm in Keycloak

    Start up Keycloak by using one of the Standalone executeables in the /bin directory of Keycloak which will start Keycloak immediately by using the bundled WildFly application server.

    After the startup, open up a browser and navigate to http://localhost:8080.

    If this is the first time you start Keycloak it is required to create an admin user.

    After you have created the admin user, use this user to login into the Administration Console. By default Keycloak uses the Master realm to manage its own users. You should never use this realm to authenticate your own application. Instead create your own realm for the authentication of your application.

    Create a new realm by hovering over the Master realm and click on the “Add realm” button. Enter a name, we will use Demo-Realm, and click on “Create”. After that you will be navigated to the configuration page of the realm.

    Open the “Login” tab and enable the features “User registration” and “Forgot password”. By enabling, these features will be added to the login page automatically and will cover the common use-cases that appear in nearly every web application.

    Create roles in Keycloak

    Now, lets add some roles that can be assigned to users later on to handle authorization. We need two roles that will come into play later on. One is the admin role and the other one is the manager role.

    Click on the “Roles” link in the navigation and after that, click on the button “Add Role” at the right side of the screen. This will navigate you to the “Add Role” screen. Enter admin as role name and click on “Save”. Create another role named manager.

    Create clients in Keycloak

    After we have created our roles we can create the clients for our application. We will need two clients. One for the frontend application and one for the REST backend.

    Let’s start by creating the frontend client. Click on the “Clients” link in the navigation and then use the “Create” button to create a new client.

    Use tutorial-frontend as client id and click on “Save”.

    You will be navigated to the client settings. Use the access type public and the following URLs and save the client:

    • Valid Redirect URIs: http://localhost:8000/*
    • Base URL: http://localhost:8000/index.html
    • Web Origins: http://localhost:8000

    Now the frontend client is completely configured and can be used.

    Lets create another client for the backend and name it tutorial-backend. This time configure the access type as bearer-only because the REST backend should only be called when a user has already logged in.

    Now the basic configuration in Keycloak is done and we can start building the applications.

    Frontend application with AngularJS

    We want to build a small Angular application that is secured by a login page and where access is restricted to registered users only. Therefore every request which goes to the Angular application should be checked if it comes from a registered user or not. If a request does not come from a registered user, the request will be redirected to the login page where a user can register.

    To interact with Keycloak from our AngularJS application, Keycloak is providing a JavaScript-Adapter directly on the Keycloak server. This adapter will be used to check if a request is authenticated and can be integrated in our application by including the JavaScript file into our html page:

    <script src="//localhost:8080/auth/js/keycloak.js"></script>

    Futhermore we have to configure the KeyCloak adapter to ensure it knows who our application is and where to find the Keycloak server. We can provide this information as a json file which can be downloaded directly from the Keycloak server.

    To get the json file, open the Keycloak administration console and navigate to the frontend client page. Then open the “Installation” tab and choose “Keycloak OIDC JSON” as format. Then download the JSON file and store it in the angular application as /keycloak/keycloak.json.

    One important thing to know is because we are only allowing registered users to have access to the application we have to manually bootstrap AngularJS and we cannot rely on the automatic bootstrapping with the ng-app directive.

    The following code demonstrates how to authenticate a request and bootstrap angular only if the request comes from a registered user:

    // on every request, authenticate user first
    angular.element(document).ready(() => {
        window._keycloak = Keycloak('keycloak/keycloak.json');

        window._keycloak.init({
            onLoad: 'login-required'
        })
        .success((authenticated) => {
            if(authenticated) {
                window._keycloak.loadUserProfile().success(function(profile){
                    angular.bootstrap(document, ['keycloak-tutorial']); // manually bootstrap Angular
                });
            }
            else {
                window.location.reload();
            }
        })
        .error(function () {
            window.location.reload();
        });
    });

    If an unregistered user opens the application he will be automatically redirected to the Keycloak login page. In the application we can access user information like login name by getting the user profile with loadUserProfile().

    That is basically all what is needed to secure our Angular application. To ensure the user information is transmitted to the backend we also have to add the users access token to the request header while calling a backend REST service. This can be done like this application wide:

    // use bearer token when calling backend
    app.config(['$httpProvider', function($httpProvider) {
        var token = window._keycloak.token;    
        $httpProvider.defaults.headers.common['Authorization'] = 'BEARER ' + token;
    }]);

    Backend application with Spring Boot

    The backend application should be secured against unauthorized access. Therefore, like in the frontend, only requests coming from registered users should be accepted.

    First, it is important to add the maven Keycloak dependencies for Tomcat and Spring Boot:

    <dependency>
                <groupId>org.keycloak</groupId>
                <artifactId>keycloak-tomcat8-adapter</artifactId>
                <version>${keycloak.version}</version>
            </dependency>
            <dependency>
                <groupId>org.keycloak</groupId>
                <artifactId>keycloak-spring-boot-adapter</artifactId>
                <version>${keycloak.version}</version>
            </dependency>

    Then we have to configure keycloak just like we did in the frontend application. This time the configuration is done the Spring Boot way in the application.properties file:

    keycloak.realm = Demo-Realm
    keycloak.realmKey = MI...
    keycloak.auth-server-url = http://localhost:8080/auth
    keycloak.ssl-required = external
    keycloak.resource = tutorial-backend
    keycloak.bearer-only = true
    keycloak.credentials.secret = e12cdacf-0d79-4945-a57a-573a833c1acc

    The values can be retrieved from the “Installation” tab in the administration console of Keycloak for the backend client. One important thing here is to not forget the secret. The secret can be retrieved from the “Credentials” tab of the backend client.

    To secure the REST API endpoints a few other entries in the application.properties file are important:

    keycloak.securityConstraints[0].securityCollections[0].name = spring secured api
    keycloak.securityConstraints[0].securityCollections[0].authRoles[0] = admin
    keycloak.securityConstraints[0].securityCollections[0].authRoles[1] = manager
    keycloak.securityConstraints[0].securityCollections[0].patterns[0] = /api/*

    The patterns property defines the pattern of the API endpoints with * acting as wildcard. That means every endpoint under api like /api/contracts or /api/users is protected by Keycloak. Every other endpoint that is not explicitly listed is NOT secured by Keycloak and is publicly available.

    The authRoles property defines which Keycloak roles are allowed to access the defined endpoints.

    If everything is configured correctly the Keycloak adapter for Spring Boot should intercept incoming request automatically and reject unauthorized requests.

    To access detailed user information in the backend we can use the KeycloakPrincipal class from the Keycloak-SpringBoot adapter. The KeycloakPrincipal will automatically be injected by Spring if used in a REST controller class as method parameter. Detailed user information can then be retrieved by using the AccessToken like this example in a REST controller:

        @RequestMapping(method = RequestMethod.GET)
        @ResponseBody
        public void getUserInformation(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal) {
            AccessToken token = principal.getKeycloakSecurityContext().getToken();
           
            String id = token.getId();
            String firstName = token.getGivenName();
            String lastName = token.getFamilyName();

            // ...
        }

    In the full example you will see that I have build a MethodArgument Resolver to avoid having to deal with the Principal in the REST controllers but that is absolutely not necessary and just for convenience.

    Demo

    The simplest way to start up a demo is to clone the application source with:

    git clone https://github.com/cternes/slackspace-angular-spring-keycloak

    Then you can either configure the frontend and backend application with the correct settings from Keycloak as described above OR use the existing KeyCloak configuration in keycloak/demo-realm.json to import the realm in Keycloak and avoid having to manually configure the applications.

    After that use maven to start both applications with the following command:

    mvn spring-boot:run

    Then navigate to http://localhost:8000 and you should find yourself landing on the Keycloak login page. Register yourself as a new user. After registering you will be redirected back to the Angular application and should see some details about your user.

    Please note that currently your user is not associated to any role defined earlier. That means accessing the backend is impossible because we have only allowed managers and admins to access the backend. To give your user access to the backend we have to map your user to a role.

    To do this, open the Keycloak admin console and navigate to “Users”. Then click on the button “View all users” and click on your username. After that navigate to the “Role Mappings” tab and assign the role “manager” to your user.

    Now open up the Angular app again and you should see a “Call backend service” button that only managers can see. Click on it and some contracts from the backend should be returned together with your user information which comes also from the backend.

    This proves that the frontend and the backend is correctly secured by Keycloak.

    Source-Code

    As always, you can find a full working example at GitHub.

    Posted in spring, spring-boot | 18 Comments

    Openkeepass: Feature release with write support

    Openkeepass is around for a while now and has proved to be a major player when it comes to reading of KeePass files (especially KeePass 2.x).

    However, since its initial release I’ve received a lot of requests regarding write support of KeePass files. After a lot of work this is finally done and has found its way into a new feature release of openkeepass.

    Writing KeePass files

    The major feature of version 0.5.0 is to support writing of KeePass files. To simply create a new KeePass file from scratch you can use the fluent API of the builders. A very simple example to write a KeePass file with only one single entry would be this:

    // Create an entry
    Entry entryOne = new EntryBuilder("First entry")
    .username("Carl")
    .password("Carls secret")
    .build();

    // Create the database file
    KeePassFile keePassFile = new KeePassFileBuilder("myNewKeePassFile")
    .addTopEntries(entryOne)
    .build();

    // Write database file to disk
    KeePassDatabase.write(keePassFile, "masterPassword", "myNewKeePassFile.kdbx");

    By nesting the available builders it is also possible to create much more complex structures in the KeePass file.

    The code to create this structure looks like this:

    // Create the more complex tree structure
    Group root = new GroupBuilder("TestDb")
                .addEntry(new EntryBuilder("First entry").build())
                .addGroup(new GroupBuilder("Banking").build())
                .addGroup(new GroupBuilder("Internet")
                        .addGroup(new GroupBuilder("Shopping")
                                .addEntry(new EntryBuilder("Second entry").build())
                                .build())
                        .build())
                .build();

    // Create the database file    
    KeePassFile keePassFile = new KeePassFileBuilder("TestDb")
            .addTopGroups(root)
            .build();

    // Write database file to disk     
    KeePassDatabase.write(keePassFile, "masterPassword", "myNewKeePassFile.kdbx");

    Modify existing KeePass files

    There is also a new concept called zipper which comes into play when a KeePass structure should be modified instead of creating a new one from scratch. The zipper can be used to easily navigate through the tree structure of a KeePass file and can be compared with the concept of an iterator. There is always a pointer to an element in the tree and by navigating through the tree, the pointer to the current element will be shifted around.

    This is very helpful if you want to replace some nodes in the tree. Please note that entries cannot be replaced directly in the tree, you have to modify the parent group of an entry if you want to modify or replace an entry.

    The following example shows how to rename an existing group by using the zipper:

    // Open keepass file
    KeePassFile database = KeePassDatabase.getInstance("database.kdbx").openDatabase("password");
           
    // Navigate through tree to group
    GroupZipper zipper = new KeePassFileBuilder(database).getZipper()
                .down()
                .right();
           
    // Rename group
    Group group = zipper.getNode();
    Group modifiedGroup = new GroupBuilder(group).name("test2").build();
           
    // Replace old group with new one
    KeePassFile modifiedDatabase = zipper.replace(modifiedGroup).close();

    // Write database file to disk
    KeePassDatabase.write(modifiedDatabase, "password", "modifiedDatabase.kdbx");

    Where can I get it

    You can download it with maven or directly from GitHub.

    <dependency>
        <groupId>de.slackspace</groupId>
        <artifactId>openkeepass</artifactId>
        <version>0.5.0</version>
    </dependency>
    Posted in open-source | 2 Comments

    How to check for updated dependencies with maven

    Regularly updating your dependencies in a project is important because it ensures that you will get all the nice bugfixes that were done in the meantime in some of the libraries you use. However this can be a time-consuming and annoying task.

    Good news if you’re using maven because there is a nice command which exactly does this for you, checking if a new version is available for one of your project dependencies. The following maven command can be used:

    mvn versions:display-dependency-updates

    And the output looks like this:

    [INFO] --- versions-maven-plugin:2.2:display-dependency-updates (default-cli) @ openkeepass ---
    [INFO] The following dependencies in Dependencies have newer versions:
    [INFO]   org.bouncycastle:bcprov-jdk15on ......................... 1.53 -> 1.54

    Just be careful while updating because of eventually breaking changes.

    Posted in programming | Leave a comment