Click here to Skip to main content
15,891,529 members
Articles / Web Development / Spring

Access Spring Profiles in JSP using Custom Tag

Rate me:
Please Sign up or sign in to vote.
3.75/5 (3 votes)
28 Sep 2016CPOL2 min read 13K   4
This post explains how to restrict access to an area, based on active Spring profile.

Spring MVC - Access Spring profiles in JSP

This post explains how to restrict access to an area, based on active Spring profile. By reading this, you will be able to access Spring profiles in JSP in order to achieve this functionality:

XML
<qcm:profile value="dev">
    <form action="login-as-admin" method="POST">
        <input type="submit" value="login as admin"/>
    </form>
</qcm:profile>

Spring Profiles

Spring profiles allow to create and run a separate configuration per environment. A common use case is the declaration of multiple data source configuration beans according to the environment (h2 for development purposes, PostgreSQL in production).

To enable any class / method for a given profile, simply annotate the class/bean method with @Profile(“…”):

Java
@Profile(value = {QcmProfile.HEROKU, QcmProfile.PROD})
@Bean
public DataSource dataSource() {
    final HikariDataSource dataSource = new HikariDataSource();
    dataSource.setMaximumPoolSize(properties.getMaximumPoolSize());
    dataSource.setDataSourceClassName(properties.getDataSourceClassName());
    dataSource.setDataSourceProperties(dataSourceProperties());

    return dataSource;
}

@Profile(QcmProfile.TEST)
@Bean
public DataSource testDataSource() {
    final HikariDataSource dataSource = new HikariDataSource();
    dataSource.setMaximumPoolSize(properties.getMaximumPoolSize());
    dataSource.setDataSourceClassName(properties.getDataSourceClassName());
    dataSource.setDataSourceProperties(testDataSourceProperties());

    return dataSource;
}

You can see the complete configuration class here.

Any component (@Component / @Configuration) annotated with @Profile(“…”) will be loaded if the given profile(s) is/are enabled.

This behavior is achieved by using @Conditional(ProfileCondition.class) on the @Profile annotation itself.

As you can see, the ProfileCondition simply checks the given value with current environment profile:

Java
/**
 * {@link Condition} that matches based on the value of a {@link Profile @Profile}
 * annotation.
 *
 * @author Chris Beams
 * @author Phillip Webb
 * @author Juergen Hoeller
 * @since 4.0
 */
class ProfileCondition implements Condition {

   @Override
   public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
      if (context.getEnvironment() != null) {
         MultiValueMap<String, Object> attrs = 
                   metadata.getAllAnnotationAttributes(Profile.class.getName());
         if (attrs != null) {
            for (Object value : attrs.get("value")) {
               if (context.getEnvironment().acceptsProfiles(((String[]) value))) {
                  return true;
               }
            }
            return false;
         }
      }
      return true;
   }
}

Use Spring Profiles in JSP

It may be useful to display a piece of content in a JSP file, based on a specific environment profile.
In my use case, I wanted to display an admin-login button to facilitate tests, only in development phase (development profile).

I found that the best way to achieve this behavior was to develop a custom JSP tag as the Servlet class gives helpers to show/hide a piece of text.

The main concern was to find out how to access Spring profiles inside a tag. Fortunately, Spring provides with a useful tag class: RequestContextAwareTag.

Access Spring Profiles in a Tag

To gain access to the Spring context, you need your tag to extend RequestContextAware class, which exposes the current “RequestContext” according to the JavaDoc:

Java
/**
 * Superclass for all tags that require a {@link RequestContext}.
 *
 * <p>The {@code RequestContext} instance provides easy access
 * to current state like the
 * {@link org.springframework.web.context.WebApplicationContext},
 * the {@link java.util.Locale}, the
 * {@link org.springframework.ui.context.Theme}, etc.
 *
 * <p>Mainly intended for
 * {@link org.springframework.web.servlet.DispatcherServlet} requests;
 * will use fallbacks when used outside {@code DispatcherServlet}.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @see org.springframework.web.servlet.support.RequestContext
 * @see org.springframework.web.servlet.DispatcherServlet
 */

This class extends the TagSupport Servlet class and override the main method doStartTag() to inject the RequestContext:

Java
/**
* Create and expose the current RequestContext.
* Delegates to {@link #doStartTagInternal()} for actual work.
* @see #REQUEST_CONTEXT_PAGE_ATTRIBUTE
* @see org.springframework.web.servlet.support.JspAwareRequestContext
*/
@Override
public final int doStartTag() throws JspException {
    try {
        this.requestContext = (RequestContext) this.pageContext.getAttribute
                              (REQUEST_CONTEXT_PAGE_ATTRIBUTE);
        if (this.requestContext == null) {
            this.requestContext = new JspAwareRequestContext(this.pageContext);
            this.pageContext.setAttribute(REQUEST_CONTEXT_PAGE_ATTRIBUTE, this.requestContext);
        }
    return doStartTagInternal();
    }
    catch (JspException ex) {
        logger.error(ex.getMessage(), ex);
        throw ex;
    }
    catch (RuntimeException ex) {
        logger.error(ex.getMessage(), ex);
        throw ex;
    }
    catch (Exception ex) {
        logger.error(ex.getMessage(), ex);
        throw new JspTagException(ex.getMessage());
    }
}

By extending the Spring RequestContextAwareTag and overriding the doStartTagInternal() method, your tag will have access to the RequestContext, needed to retrieve Spring profiles.

With this context, it’s easy to retrieve environment profiles:

Java
/**
 * Created by lopes_f on 6/3/2015.
 * <florian.lopes@outlook.com>
 * Custom tag evaluating current profile before rendering body
 */
public class ProfileConditionTag extends RequestContextAwareTag {

    private String profile;

    @Override
    protected int doStartTagInternal() throws Exception {
        final Environment environment = 
             this.getRequestContext().getWebApplicationContext().getEnvironment();
        if (environment != null) {
            final String[] profiles = environment.getActiveProfiles();
            if (ArrayUtils.contains(profiles, this.profile)) {
                return EVAL_BODY_INCLUDE;
            }
        }

        return SKIP_BODY;
    }

    public String getValue() {
        return profile;
    }

    public void setValue(String profile) {
        this.profile = profile;
    }
}

Usage

Create the taglib descriptor and place it into the WEB-INF/taglibs/ directory:

XML
<?xml version="1.0" encoding="UTF-8" ?>

<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
                            http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
        version="2.0">
    <description>Conditional profile Tag</description>
    <tlib-version>2.1</tlib-version>
    <short-name>ProfileConditionTag</short-name>
    <uri></uri>
    <tag>
        <name>profile</name>
        <tag-class>com.ingesup.java.qcm.taglib.ProfileConditionTag</tag-class>
        <body-content>scriptless</body-content>
        <attribute>
            <name>value</name>
            <required>true</required>
        </attribute>
    </tag>
</taglib>

Using this tag is pretty straightforward:

XML
<qcm:profile value="dev">
    <form action="login-as-admin" method="POST">
        <input type="submit" value="login as admin"/>
    </form>
</qcm:profile>

You can head to this url and see that the admin-login button doesn’t appear as the active profile for the application is “heroku”.

Application Code

You can see the whole application code in my GitHub project.

The post Access Spring profiles in JSP using custom tag appeared first on Florian Lopes's blog.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Engineer Florian Lopes
France France
Java / Spring freelancer

Comments and Discussions

 
GeneralMy vote of 2 Pin
johnjonny10-Oct-16 7:54
johnjonny10-Oct-16 7:54 
QuestionNice Pin
AnaelDujardin29-Sep-16 23:50
AnaelDujardin29-Sep-16 23:50 
PraiseRe: Nice Pin
Florian Lopes30-Sep-16 7:46
professionalFlorian Lopes30-Sep-16 7:46 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.