A basic Spring with Z2 modularity sample

The sample contained in the repository z2-samples.spring-basic is a clean room example on how to use the Spring integration features described in how_to_spring.

Z2 has the following Java Version requirements

Z2 Version Min. Java version required Max Java version supported
2.1 - 2.3.1 Java 6 Java 7
2.4 - master Java 8 Java 8

Note: Most samples suggest to use the master branch. You may choose another version branch (please check the respective repository).
Make sure you have a corresponding Java Development Kit (JDK) or Java Runtime Environment (JRE) installed. If in doubt, go to Download Java SE.

Note: Running v2.1-v2.3.1 on Java 8 is supported by specifying

com.zfabrik.java.level=7

(or 6, if that is your desired compilation language level) in <home>/run/bin/runtime.properties. By this the Java compiler version detection does not fall back to a lower level.

There is no further pre-requisite to running this sample, and you may proceed as described in How to run a sample. Here's the really fast version:

mkdir install
cd install 
git clone -b master http://git.z2-environment.net/z2-base.core
git clone -b master http://git.z2-environment.net/z2-samples.spring-basic

# on Linux / Mac OS:
cd z2-base.core/run/bin
./gui.sh

# on Windows:
cd z2-base.core\run\bin
gui.bat

Notes:

  • If you run into read timeouts due to weak internet connection, simply keep repeating to start z2.
  • If you are located behind an internet proxy, please check How_to_proxy_settings on how to configure your installation.

If you want to inspect the code using Eclipse, please create a workspace in install (i.e. install/workspace) and import the Git repositories and projects (see also First steps).

There are three modules contained in this sample. For the moment only consider the following two:

com.zfabrik.samples.spring-basic.services

This module has a classpath defined applicationContext and exposes an annotation defined bean ComputationServiceImpl bean from it as a service to be consumed from another module: The computations bean.

The application context enables discovery of Spring beans that are marked by annotations such as @Service, @Component, @Repository:

<beans ...>
    <!-- Turn on annotation based config -->
    <context:annotation-config/>
    <!-- Turn on auto discovery -->
    <context:component-scan base-package="com.zfabrik.samples.impl"/>
</beans>

The *computations" bean implementation implements the interface IComputationService and is itself not visible to consumers. It's structure is roughly like this:

@Service("computations")
public class ComputationServiceImpl implements IComputationService {

...

}

The computations bean is exposed via the Z2 component com.zfabrik.samples.spring-basic.services/computations:

com.zfabrik.component.type=org.springframework.bean

#
# the context that defines the bean (more than one
# bean can be exposed like this)
#
bean.context=com.zfabrik.samples.spring-basic.services/applicationContext

#
# the bean name
#
bean.name=computations

Note that the Z2 Bean component names its application context. At runtime this means that an attempt to retrieve the bean will make sure the application context is loaded - and not any earlier.

com.zfabrik.samples.spring-basic.frontend

This module has a Web application with an application context defined in WEB-INF/applicationContext. It uses Spring AspectJ based annotation driven configuration to inject dependencies into instances of ControllerServlet.

Note the Java component descriptor that, apart from referencing the service module, holds the minimum declaration to make sure of Spring with AspectJ supported, compile-time-woven annotation based configuration:

com.zfabrik.component.type=com.zfabrik.java

java.privateReferences=\
    com.zfabrik.springframework.web,\
    org.springframework.tx,\
    org.springframework.orm,\
    com.zfabrik.samples.spring-basic.services

java.privateIncludes=\
    org.springframework.security:spring-security-core,\
    org.springframework:spring-aspects

java.compile.order=java,spring_aspectj

It's application context imports the computations service from the other module above and enables the use of Spring configuration (note: Unlike above it does not enable discovery of Spring beans):

<beans ... >
    <!-- turn on @Configurable support -->
    <context:spring-configured/>
    <!-- Turn on annotation based config -->
    <context:annotation-config/>
     <!-- application config: Bind the bean at samples.spring.simplemodules.services/computations --> 
    <bean id="computations" class="com.zfabrik.springframework.ComponentFactoryBean">
        <property name="componentName" value="com.zfabrik.samples.spring-basic.services/computations"/>
        <property name="className" value="com.zfabrik.samples.services.IComputationService"/>
    </bean>
</beans>

Finally

Open a browser and navigate to http://localhost:8080/spring-basic to verify you get this:

An extended Spring with Z2 modularity and some Spring Security sample

The third module com.zfabrik.samples.spring-basic.secured contained in the sample repository implements a very similar basic frontend to the one above but illustrating in addition:

  • How to use Spring Security to secure the access to a Web application
  • How to use Spring Security to secure methods of a bean
  • How to use Spring Security with Spring AspectJ weaving

More specifically, the contained Web application knows of two users "user" (password "user") and "admin" (password "admin") that have roles ROLE_USER or ROLE_USER and ROLE_ADMIN respectively.

The controller servlet delegates operations to a session facade implemented by the class SomeFacadeImpl that offers two methods, one requiring ROLE_USER, one requiring ROLE_ADMIN.

But let's rather have a step-by-step overview: The Java component declaration com.zfabrik.samples.spring-basic.secured/java, as compared to the one of the other frontend module has some noteworthy additions:

com.zfabrik.component.type=com.zfabrik.java

java.privateReferences=\
    com.zfabrik.samples.spring-basic.services,\
    org.springframework.tx,\
    org.springframework.orm,\
    com.zfabrik.springframework.web

java.privateIncludes=\
    org.springframework:spring-aspects,\
    org.springframework.security:spring-security-core,\
    org.springframework.security:spring-security-web,\
    org.springframework.security:spring-security-config,\
    org.springframework.security:spring-security-aspects

#
# this enables the processing of @Secured annotations
#
aspectj.privateAspectPathByClass=\
    org.springframework.security.access.intercept.aspectj.aspect.AnnotationSecurityAspect

java.compile.order=java,spring_aspectj

One the one it references the Spring Security module and it includes some Spring Security elements (that unfortunately must be included). Notably the support for Web apps, the config support, and finally the aspect, which is the pre-requisite for AspectJ support with Spring Security.

We prefer AspectJ compile-time weaving over Spring AOP as it is much more consistent (non-proxy-based AOP, see also how_to_spring and elsewhere) and, as the compilation part is automatic with Z2, no extra burden to configure.

However, to let the Spring AspectJ compiler, supported by Z2 know about the handling of Spring Security annotations used in the implementation, the corresponding Aspect Implementation must be indicated. Hence the additional property "aspectj.privateAspectPathByClass". See also AspectJCompiler for more details.

Fortunately this was the hardest part of our configuration tour.

The Web apps application context has a lot of similarities to the one of the other frontend but adds some Spring Security related configuration:

...

    <!-- web security config: require basic authentication, hard-coded users "user" and "admin" -->
    <security:http auto-config='true' disable-url-rewriting="true">
        <security:intercept-url pattern="/**" access="ROLE_ADMIN,ROLE_USER" />
        <security:http-basic />
    </security:http>

    <security:authentication-manager>
        <security:authentication-provider>
            <security:user-service>
                <security:user name="admin" password="admin" authorities="ROLE_ADMIN,ROLE_USER" />
                <security:user name="user" password="user" authorities="ROLE_USER" />
            </security:user-service>
        </security:authentication-provider>
    </security:authentication-manager>

    <!-- method security config (in this case, only Secured annotations enabled)-->
    <security:global-method-security
        secured-annotations="enabled" 
        mode="aspectj" 
    />

...

Having users and passwords declared in the application context is is a sacrifice due to the sample nature. Other than that these declarations say that

  1. the Web app uses Basic Authentication (being simpler than a form-based login for the sample),
  2. that all requests require either ROLE_USER or ROLE_ADMIN, and
  3. that it makes use of Method Security, expecting the support of @Secured annotations with AspectJ weaving.

The controller servlet (ControllerServlet) delegates all calls to the facade implementation SomeFacadeImpl that has secured methods:

@Component
public class SomeFacadeImpl {
    private final static Logger LOG = Logger.getLogger(SomeFacadeImpl.class.getName());

    public SomeFacadeImpl() {
        LOG.info("Init of "+this);
    }

    @Autowired
    private IComputationService computations;

    @Secured("ROLE_USER")
    public String doSomethingWithAString(String in) {
        return computations.doSomethingWithAString(in);
    }

    @Secured("ROLE_ADMIN")
    public String doSomethingThatRequiresAdmin() {
        return "Yes, you can!";
    }    
}

It does use the re-use service computations from the other module and gets it injected as above.

Now, if you open a Web browser at http://localhost:8080/spring-basic-secured you will first be asked to log in (best try "user" with password "user" first) and next offered the following options:

Pressing the button beneath "Try as admin" should give you an "Access denied" response, if you did not log on with the "admin" user, while it should give some affirmative response in case you did not log with admin permissions.

frontend.png (50.4 KB) Henning Blohm, 09.11.2013 09:23

secured.png (57.5 KB) Henning Blohm, 09.11.2013 14:42