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:
<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(“…”)
:
@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:
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:
This class extends the TagSupport
Servlet class and override the main
method doStartTag()
to inject the RequestContext
:
@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:
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:
="1.0"="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:
<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.
CodeProject
The post Access Spring profiles in JSP using custom tag appeared first on Florian Lopes's blog.