Java and JBoss related stuff
RSS icon Home icon
  • Envers 1.2.3-hibernate-3.3 – backport release

    Posted on August 21st, 2010 Adam Warski No comments

    Hernan Chanfreau backported some of the recent Envers changes and fixes from the 3.5 branch. The detailed list of the backported issues can be found in this forum post.

    I applied the patch and released a new Hibernate-3.3 compatible Envers version, it’s available in the JBoss Maven repository.

    Thanks a lot to Hernan for the work and providing the patch! :)

    Adam

  • New features in cdi-ext

    Posted on August 2nd, 2010 Adam Warski No comments

    Tomek SzymaƄski just commited two new features to cdi-ext.

    The first is the ability to secure JSF pages when using the Nav component to handle navigation (more on it here). If you want to make a page accessible only if a certain EL expression is true, when defining a page, you can write:

    
    private final Page adminPage = new ViewIdPageBuilder("/admin.xhtml")
        .setRequiresLogin(true)
        .setSecurityEL("#{currentUser.isAdmin)").b();
    

    If the condition is not met, the user will get 403 Forbidden. Also, Tomek extended handling of setRequiresLogin(true), so that if a user is redirected to the login page, because he was not logged in and the page is secured, the page which the user tried to access is stored and after logging in, he is redirected back to the original page.

    The second is the ability to specify the transaction timeout using a @TransactionTimeout(timeout = 5) annotation on a method.

    Moreover, we have moved our repository infrastructure to Nexus. The new address where cdi-ext artifacts are deployed is http://tools.softwaremill.pl/nexus/content/repositories/snapshots.

    Adam

  • Ruby on Rails + CDI? Why not! Enter TorqueBox + Weld

    Posted on July 27th, 2010 Adam Warski 1 comment

    I guess many people are often “unsatisfied” with how JSF works and how much time it sometimes takes to do a simple thing. That’s why we are trying out a new combination: RoR for the frontend and CDI for the backend. How?

    Deploying RoR applications to JBoss AS is really easy thanks to the TorqueBox project. You just deploy a .yml file using the provided rake tasks and you can develop the application “live” – no redeploys, instant changes, and so on.

    Using RoR as a frontend to a CDI/Weld based application requries two more steps, so that RoR can see the business logic classes and share the same http session with CDI (so it’s possible to access @SessionScoped beans from RoR and CDI code).

    First you need to deploy your application in the DefaultDomain (at least until TORQUE-85 is fixed). To do this, add a jboss-classloading.xml file to the META-INF directory with this content:

    
    <classloading xmlns="urn:jboss:classloading:1.0"
                  domain="DefaultDomain"
                  top-level-classloader="true"
                  export-all="NON_EMPTY"
                  import-all="true">
    </classloading>
    

    Secondly, you need to add a filter to RoR’s web application, so that Weld and RoR share the same session. Just edit config/web.xml in your RoR application (the magic in the RoR deployer will add it to the virtual .war deployment it creates) and add the following:

    
    <web-app>
        <listener>
            <listener-class>org.jboss.weld.servlet.WeldListener</listener-class>
        </listener>
    </web-app>
    

    Now RoR and CDI share the same session (so you can use @SessionScoped beans etc, probably also @ConversationScoped, but I haven’t tried that). You can lookup CDI beans from RoR code using e.g. the BeanInject class from cdi-ext, or just by writing a very simple utility method which lookups the BeanManager.

    Adam

  • Initial valid-time support in Envers

    Posted on July 2nd, 2010 Adam Warski No comments

    I just commited initial support for valid-time auditing in Envers, a feature that a lot of users has been (directly or indirectly) asking for. It’s joint work, as Stephanie Pau contributed a patch with a large portion of those changes – thanks!

    You can try it out by checking out Hibernate trunk source code.

    What is valid-time about? So far Envers only stored the revision at which a change was made. This information is enough to retrieve historical data, however the queries are quite complicated and in advanced use cases can be time-consuming. This can be improved when we store both the start and end revisions, that is information on when data was “valid”. For historic entities, both column are filled, and for “current” data, the end revision column is null.

    Using the valid-time audit strategy, it will be possible to:

    • speed up and simplify the queries to retrieve historical data
    • implement support for queries, which traverse relations
    • implement other types of queries, like latest changes

    To configure Envers to store the end-revision number, you have to specify a property in your configuration file:

    
    <property name="org.hibernate.envers.audit_strategy">
       org.hibernate.envers.strategy.ValidTimeAuditStrategy
    </property>
    

    Envers will then generate and additional REVEND column (next to the REV column) in every audit (_AUD) entity/table; however this column won’t be part of the primary key. You can change the name of the end-revision column by setting the org.hibernate.envers.audit_strategy_valid_time_end_name property value.

    The value of the end-revision column can be calculated basing only on the original revision-changed columns, so using a couple of queries it will be possible to easily migrate existing data to the new audit strategy.

    Please note that this feature is experimental, and can be changed in the future. The associated JIRA issue is HHH-3763.

    Adam

  • NEnvers

    Posted on July 1st, 2010 Adam Warski No comments

    If you are a .NET and NHibernate user, soon you’ll be able to use Envers in your project!

    Simon Duduica has been working on an Envers to .NET port. This is still work in progress, but you can check out the current source code here: https://nhcontrib.svn.sourceforge.net/svnroot/nhcontrib/trunk/sandbox/simondud/Envers.NET

    A large portion was ported, but there’s also quite a lot left, so any help is appreciated. Big thanks to Simon and looking forward to a release :)

    Adam

  • CDI & Weld Extensions in Git

    Posted on May 17th, 2010 Adam Warski No comments

    Hello,

    I’ve created a new cdiext project at github, initially with two extensions:

    1. Stackable Security Interceptors, about which I blogged here and here. Example usage:

    
    @SecureBinding
    @Secure("#{loggedInUser.administrator}")
    public @interface AdministratorOnly {
    }
    
    public class SecureBean {
        @AdministratorOnly
        @Secure("#{additionalSecurityCheck}")
        public void doSecret() { ... }
    }
    

    2. Injectable ELEvaluator, which works both during a faces request and outside of one (e.g. during invocation of an MDB). Example usage:

    
    @Inject
    private ELEvaluator elEvaluator;
    
    void someMethod() {
        // ...
        Integer result = elEvaluator.evaluate(
                "#{testParam1 + 10 + testParam2}", Integer.class, params);
        // ...
    }
    

    Thanks to Dan Allen for helping out with this one.

    There are also some tests done using Arquillian – looks like it’s going to be a great testing tool! :)

    Adam

  • JSR-308, checkers framework and static analysis on GeeCON

    Posted on April 26th, 2010 Adam Warski 1 comment

    I’ll be speaking about the JSR-308 specification (annotations on java types), which will be part of Java 7, on the GeeCON conference, which will take place from the 13th till the 14th of May 2010 in Poznan, Poland.

    Apart from an introduction to the new annotations, I will cover the checkers framework, and do a live demo of some of the bundled checkers (nullability, immutability), as well as of my typestate checker. I will also show how to implement a simple custom checker, using the framework.

    If you’ll be there, be sure not to miss it. If you’ve not yet registered, visit http://2010.geecon.org :).

    See you there,
    Adam

  • Hibernate 3.5-Final, Envers included, released!

    Posted on April 1st, 2010 Adam Warski No comments

    Hibernate 3.5 was just released; congratulations to Steve and the whole team!

    It’s the first final Hibernate release that includes Envers. You can find it both in the bundle downloadable from SF, as well as in the Maven repository.

    Happy Easter,
    Adam

  • Simple security interceptor in Weld/JSF2

    Posted on March 31st, 2010 Adam Warski 7 comments

    Waiting for the Seam3 security module, I wrote a simple security interceptor (inspired by the Seam2 security annotation). You can use it like this:

    
    @Secure("#{loggedInUser.name == arg0.name}")
    public List<Message> listMessages(User owner) { ... }
    

    Here, loggedInUser is a @Named Weld (CDI) bean, and arg0 is the first argument of the method. This is a slight modification to the Seam2 security annotation, where it wasn’t possible to reference arguments. If the EL expression doesn’t evaluate to true, an exception is thrown.

    The implementation is pretty straightforward, as adding an interceptor in Weld is really simple. First, we need the annotation:

    
    /**
     * @author Adam Warski (adam at warski dot org)
     */
    @Retention(RUNTIME)
    @Target({TYPE, METHOD})
    @InterceptorBinding
    public @interface Secure {
        /**
         * @return The EL expression that should be evaluated. If it evaluates to
         * {@code true}, access will be granted. The EL expression may reference
         * any objects that are in any context, as well as the arguments of the method,
         * under the names {@code arg0, arg1, arg2, ...}.
         */
        @Nonbinding
        String  value();
    }
    

    The key components here is the @InterceptorBinding meta-annotation, and specifying the value of to be @Nonbinding. If the value element was binding, then we would need to define an interceptor for each possible String value.

    Next, the interceptor itself:

    
    @Interceptor
    @Secure("")
    public class SecurityInterceptor {
        private String getArgName(int index) { return "arg" + index; }
    
        @AroundInvoke
        public Object invoke(InvocationContext ctx) throws Exception {
            FacesContext facesCtx = FacesContext.getCurrentInstance();
            ELContext elCtx = facesCtx.getELContext();
    
            Secure secure = getSecureAnnotation(ctx.getMethod());
            String expression = secure.value();
    
            // Populating the request map so that parameters are available (arg0, ...)
            Map<String, Object> requestMap = facesCtx.getExternalContext()
                    .getRequestMap();
            for (int i = 0; i < ctx.getParameters().length; i++) {
                Object parameter = ctx.getParameters()[i];
                requestMap.put(getArgName(i), parameter);
            }
    
            Boolean expressionValue = (Boolean) facesCtx.getApplication()
                    .getExpressionFactory()
                    .createValueExpression(elCtx, expression, Boolean.class)
                    .getValue(elCtx);
    
            // Removing the parameters (arg0, arg1, ...)
            for (int i = 0; i < ctx.getParameters().length; i++) {
                requestMap.remove(getArgName(i));
            }
    
            if (expressionValue == null || !expressionValue) {
                throw new SecurityException();
            }
    
            return ctx.proceed();
        }
    
        private Secure getSecureAnnotation(Method m) {
            for (Annotation a: m.getAnnotations()) {
                if (a instanceof Secure) { return (Secure) a; }
            }
            for (Annotation a: m.getDeclaringClass().getAnnotations()) {
                if (a instanceof Secure) { return (Secure) a; }
            }
    
            throw new RuntimeException("@Secure not found on method " + m.getName() +
                    " or its class " + m.getClass().getName());
        }
    }
    

    And finally, we need an entry in beans.xml, enabling the interceptor:

    
    <interceptors>
       <class>util.security.SecurityInterceptor</class>
    </interceptors>
    

    One shortcoming is that it’s not currently possible to place one @Secure annotation on the class, and another on the methods (see this thread on the forum). The idea is that then the class-level annotation expresses general security constraints, which can be later refined on the method level.

    Another missing feature, which could be easily added, is a message parameter to the annotation, which would be included in the exception in case the check fails.

    Adam

  • JSF2 navigation: post->redirect->get

    Posted on March 11th, 2010 Adam Warski 2 comments

    JSF2 improves a lot both how navigation can be done (you can now return a view id from an action method, no need to describe every navigation case in faces-config.xml) and how URLs are handled (finally, GET support). JSF2 introduces view parameters (for those who know Seam: standardized page parameters). Each page can define a metadata section, where the view parameters are described, bound to bean values, converted and validated.

    As an example, a blog-entry-viewing page would define the id of the entry to be displayed as follows:

    
    <f:metadata>
       <f:viewParam name="entry_id" value="#{blog.entry}" required="true">
          <f:converter converterId="blog-entry-converter" />
       </f:viewParam>
    </f:metadata>
    

    Unfortunately I had some trouble with one thing: how to redirect to a page, including the view parameters, after a POST? This post->redirect->get pattern is very common. E.g. when you post a new comment for a blog entry and press submit, the data is persisted and you want to be redirected back to view.jsf?entry_id=819 (using the default JSF command-button behavior, you would land on a plain, non-bookmarkable view.jsf).

    Dan Allen wrote a series of very good introductory articles to JSF2 on DZone. There, he writes that what I described above should be possible to achieve by adding a <redirect include-view-params="true"/> tag to the appropriate <navigation-case> in faces-config.xml. Unfortunately, the xsd doesn’t allow such an attribute and it doesn’t work – I suppose that this construct didn’t make it into the final version of the spec (although somebody may correct me if I’m wrong).

    Another solution, this time working, can be found on Ed Burns’s blog. The trick is to return a string containing the view id and some additional parameters from the action method or use them as the command button/link action, e.g.:

    
    public String action() {
         // business logic ...
         return "view.xhtml?faces-redirect=true&includeViewParams=true"
    }
    

    However this way you’ll have to repeat the combination of the “magical parameters” a lot in your code. And it’s pretty easy to do a spelling mistake in one of the strings you return. Furthermore, it’s not possible to easily include one view parameter, without repeating the value mapping.

    The way I solved this is by introducing a Nav component (I’m using Weld), which holds information about pages. It contains a nested Page class, which has a “fluent” interface for building a link. Navigation then looks as follows:

    
    @Inject
    private Nav nav;
    
    public String action() {
         // business logic ...
         return nav.getViewEntry().redirect().includeViewParams().s();
    }
    

    Or, if you want to include only one parameter:

    
    public String action() {
         // business logic ...
         return nav.getViewEntry().redirect().includeViewParam("name").s();
    }
    

    In xhtml pages, you can also use the nav component to generate links:

    
    <h:link outcome="#{nav.manageIndex.s}">Manage</h:link>
    

    Notice that you completely abstract away from the actual names of the xhtml views (pages) – they are stored centrally only in the nav component! This makes any refactorings really easy.

    Speaking of the nav component, here’s the code:

    
    /**
     * @author Adam Warski (adam at warski dot org)
     */
    @Named
    @ApplicationScoped
    public class Nav {
        public static class Page {
            private final String viewId;
            private final Map<String, String> params;
    
            private Page(String viewId) {
                this.viewId = viewId;
                this.params = new LinkedHashMap<String, String>();
            }
    
            private Page(String viewId, Map<String, String> params) {
                this.viewId = viewId;
                this.params = params;
            }
    
            public Page redirect() {
                return includeParam("faces-redirect", "true");
            }
    
            public Page includeViewParams() {
                return includeParam("includeViewParams", "true");
            }
    
            public Page includeViewParam(String name) {
                // Getting the metadata facet of the view
                FacesContext ctx = FacesContext.getCurrentInstance();
                ViewDeclarationLanguage vdl = ctx.getApplication().getViewHandler()
                      .getViewDeclarationLanguage(ctx, viewId);
                ViewMetadata viewMetadata = vdl.getViewMetadata(ctx, viewId);
                UIViewRoot viewRoot = viewMetadata.createMetadataView(ctx);
                UIComponent metadataFacet = viewRoot.getFacet(
                      UIViewRoot.METADATA_FACET_NAME);
    
                // Looking for a view parameter with the specified name
                UIViewParameter viewParam = null;
                for (UIComponent child : metadataFacet.getChildren()) {
                    if (child instanceof UIViewParameter) {
                        UIViewParameter tempViewParam = (UIViewParameter) child;
                        if (name.equals(tempViewParam.getName())) {
                            viewParam = tempViewParam;
                            break;
                        }
                    }
                }
    
                if (viewParam == null) {
                    throw new FacesException("Unknown parameter: '" + name +
                         "' for view: " + viewId);
                }
    
                // Getting the value
                String value = viewParam.getStringValue(ctx);
                return includeParam(name, value);
            }
    
            public Page includeParam(String name, String value) {
                Map<String, String> newParams = new LinkedHashMap<String, String>(params);
                newParams.put(name, value);
                return new Page(viewId, newParams);
            }
    
            public String s() {
                StringBuilder sb = new StringBuilder();
                sb.append(viewId);
    
                String paramSeparator = "?";
                for (Map.Entry<String, String> nameValue : params.entrySet()) {
                    sb.append(paramSeparator).append(nameValue.getKey())
                          .append("=").append(nameValue.getValue());
                    paramSeparator = "&amp;amp;";
                }
    
                return sb.toString();
            }
    
            public String getS() { return s(); }
        }
    
        private final Page manageIndex = new Page("/manage/index.xhtml");
        private final Page manageUsers = new Page("/manage/users.xhtml");
        private final Page home = new Page("/home.xhtml");
        private final Page thisPage = new Page("");
        // other pages ...
    
        public Page getManageIndex() {
            return manageIndex;
        }
    
        // other getters ...
    }
    

    Looking forward, the Page class may also include e.g. security management, however that would require some more JSF bindings.

    Adam