• Fat Cats Boardgame
  • Wicket UI Library
  • About Me

Java and Wicket web development thoughts, tutorials, and tips

roman@coderdreams.com
Coder DreamsCoder Dreams
  • Fat Cats Boardgame
  • Wicket UI Library
  • About Me

Customizing your codebase per customer: Part 2 – Conditions

April 25, 2020 Posted by Roman Sery customization, software design, wicket No Comments

In part 1, we discussed using a “SiteConfig” enumeration to easily allow toggling features on or off depending on the needs of the specific customer/client. The enumeration would contain different items usually representing a boolean value for toggling features of the site.

That approach is fine for some purposes but is a bit limited and inflexible when you need more complex variations among customers. It might not just be a matter of toggling features. It’s very likely that for some areas of functionality, their requirements will vary greatly among different customers.

Let’s look at a more flexible solution using Spring framework conditional service classes.

Defining a CustomerType

The first thing we will do is define an enumeration containing all of our customers:

public enum CustomerType {
    FB,
    GOOGLE,
    MSFT
}

Now, using CustomerType, let’s create a helper class that will define customer conditions. A customer condition is used to tell Spring which version of our service class to use depending on the current customer.

public class CustomerConditions {

    public static final class FbCondition implements Condition {
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            return isMatch(CustomerType.FB, context);
        }
    };

    public static final class GoogleCondition implements Condition {
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            return isMatch(CustomerType.GOOGLE, context);
        }
    };


    private static boolean isMatch(CustomerType customerType, ConditionContext context) {
        Environment env = context.getEnvironment();
        if(env == null) {
            return false;
        }
        String customer = Utils.getCustomer();
        return customerType.name().equals(customer);
    }
}

Creating our service classes

Our service classes will contain all the business logic for our application (in theory!). What we would like to do is have a single class that will contain the default functionality and separate customer-specific classes which can override some or all of the defaults.

  • SiteFunctions – Our default functionality/logic
  • FbSiteFunctions – Will contain overrides for one of our wonderful customers Facebook.
  • GoogleSiteFunctions – Overrides for another great customer, Google.

In this simple example, we will only have a single overridable function that will determine if a user account can be deleted. It will look something like this:

public interface SiteFunctions {
	default boolean canDeleteUserAccounts(User u) {
		//by default only allow deleting user accounts if inactive
		return u != null && !u.isActive();
    }
}

@Service @Conditional(CustomerConditions.FbCondition.class)
class FbSiteFunctions implements SiteFunctions {
    @Override
    public boolean canDeleteUserAccounts(User u) {
        //FB wants to be able to delete accounts regardless of status
        return u != null;
    }
}

@Service @Conditional(CustomerConditions.GoogleCondition.class)
class GoogleSiteFunctions implements SiteFunctions {
    @Override
    public boolean canDeleteUserAccounts(User u) {
        //Google doesnt want to ever delete user accounts
        return false;
    }
}

Notice in the two highlighted lines, we are using the helper conditions we talked about earlier. When doing dependency injection, this will tell Spring to pick the correct implementation of SiteFunctions depending on the current customer.

How to use in Wicket pages

Now we have everything we need to use this in our Wicket pages. Let’s create a simple test page which will be given a User object and determine if it can be deleted. Notice that it doesn’t need to have any awareness for which customer this code is being executed:

public class CustomerTestPage extends WebPage {
    @SpringBean private SiteFunctions siteFunctions;
    public CustomerTestPage() {        
        User u = new User();
        u.setActive(true);
        setMarkup(Markup.of("Can delete user: " + siteFunctions.canDeleteUserAccounts(u)));
    }
}

The key is to always reference our default SiteFunctions interface as opposed to the customer-specific overrides. Spring will take care of making sure the correct implementation is injected via the @SpringBean annotation.

In this case, the above code would return false because our default functionality says that an active user cannot be deleted. However, If we execute this application with the customer JVM parameter set to “FB”, it will return true.

The full source code can be found on GitHub.

No Comments
Share
2

About Roman Sery

I've been a software developer for over 10 years and still loving Java!

You also might be interested in

Launching LetsDefinitely.com – A dating website

Jul 28, 2020

Launching LetsDefinitely.com - A dating website

How to write a JSON response in Wicket

Jan 26, 2020

Learn how to create JSON response endpoints in Wicket

Happy holidays

Dec 24, 2019

Just a short note

Categories

  • aws
  • customization
  • database
  • debugging
  • enum
  • java
  • models
  • performance
  • projects
  • react
  • software design
  • Spring
  • tool
  • Uncategorized
  • wicket

Recent Posts

  • Rent Day
  • Self-contained Wicket Fragments
  • Pros and cons of unit testing
  • Themeable React Monopoly board
  • Please dont use client-specific release branches

Recent Comments

  • TCI Express Thanks for sharing such insightful information. TCI Express truly stands out as the best air logistics company, offering fast, secure, and efficient air express and cold chain transportation services....

    Tracking down a bug in production Wicket application ·  March 25, 2025

  • Tom Error: A zip file cannot include itself Can you please correct the plugin part so it doesn't use the same folder as input?

    Deploying Spring Boot app to AWS Beanstalk with Nginx customization ·  September 3, 2021

  • Golfman: Reality always wins I've used both Wicket and front-end JS frameworks and, having worked extensively on both, I can tell you that "Speed of development" is definitely NOT with the JS frameworks. You basically end up...

    Five reasons you should use Apache Wicket ·  August 29, 2021

  • Kiriller Sorry can not agree with you, wicket might be a well built technical framework. But the advantages of using a front-end framework like react.js and vue.js can not be beaten by Wicket nowadays. - Speed...

    Five reasons you should use Apache Wicket ·  August 23, 2021

  • Bernd Lauert Sorry but i have to refute your claims with the following arguments: 1. the Wicket community may be small but it is also very responsive, you usually get a helpful answer from the core devs on the...

    Five reasons you should use Apache Wicket ·  July 1, 2021

Archives

  • May 2021
  • October 2020
  • September 2020
  • August 2020
  • July 2020
  • June 2020
  • May 2020
  • April 2020
  • March 2020
  • February 2020
  • January 2020
  • December 2019
  • November 2019
  • October 2019
  • September 2019
  • August 2019
  • July 2019

Contact Me

Send Message
Prev Next