Entries Tagged 'Java' ↓

Using JCaptcha in CAS4

CAS 4, which isn’t out yet, or in any condition to be used in production, now supports using JCaptcha in order to throttle attempts to discern passwords.

Complete details on enabling it are in the JASIG Wiki. We’ve enabled it by default in the demonstration WAR file.

Essentially, we use an algorithm (rather simple, feel free to enhance it and contribute it back) to determine if you’ve had too many failed attempts in a a certain period of time based on your IP address. We don’t use sessions because obviously you could just get a new session ;-)

This is just one of the many enhancements based on community feedback that we’re looking to include in CAS 4.

Using JaValid with Spring to Validate Beans during Post Processing

JaValid is an annotation-based framework for validating fields or methods (i.e. NotNull, NotEmpty, etc.). We initially leveraged it in CAS to provide domain-validation to credential objects. This would allow the domain class to contain all the information about itself (similar to what Grails does).
CAS previously used some custom code in the Inspektr library to validate Spring beans via a BeanPostProcessor. Some of the code in Inspektr and JaValid was similar (i.e. the annotations) such that we figured it might be better to leverage more of JaValid (less code for us to maintain). To that end, we’ve managed to construct a BeanPostProcessor that leverages JaValid for validating the properties of Spring beans after they are created. I’ve pasted it below for anyone who’s interested in doing something similar:

/**
 * Copyright 2008 JA-SIG, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0

 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jasig.cas.server.util;

import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.binding.message.MessageResolver;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.javalid.core.AnnotationValidator;
import org.javalid.core.AnnotationValidatorImpl;
import org.javalid.core.ValidationMessage;

import java.util.List;
import java.util.Locale;

/**
 * Implementation of a {@link org.springframework.beans.factory.config.BeanPostProcessor} that uses Javalid to validate
 * properties of POJOs managed by Spring.
 *
 * @author Scott Battaglia
 * @version $Revision$ $Date$
 * @since 4.0.0
 */
public final class JavalidAnnotationValidatorBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {

    private final AnnotationValidatorImpl annotationValidator;

    private String pathPrefix = "";

    private String group = null;

    private int recursiveLevels = 2;

    private final ResourceBundleMessageSource resourceBundleMessageSource;

    public JavalidAnnotationValidatorBeanPostProcessor(final String configLocation) {
        if (configLocation != null) {
            this.annotationValidator = new AnnotationValidatorImpl(configLocation);
        } else {
            this.annotationValidator = new AnnotationValidatorImpl();
        }

        this.resourceBundleMessageSource = new ResourceBundleMessageSource();
        this.resourceBundleMessageSource.setBasename("org/javalid/core/validator/jv_messages");
    }

    public JavalidAnnotationValidatorBeanPostProcessor() {
        this(null);
    }

    public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {
        final List<ValidationMessage> messages = this.annotationValidator.validateObject(bean, this.group, this.pathPrefix, true, this.recursiveLevels);
        final StringBuilder builder = new StringBuilder();

        if (messages.isEmpty()) {
            return bean;
        }

        builder.append("bean '");
        builder.append(beanName);
        builder.append("' encountered errors on properties: '");
        for (final ValidationMessage message : messages) {
            builder.append("[property=");
            builder.append(message.getPath());
            builder.append(";error='");
            builder.append(this.resourceBundleMessageSource.getMessage(message.getMessage(), message.getValues(), Locale.getDefault()));
            builder.append("'] ");
        }

        throw new FatalBeanException(builder.toString());
    }
}

Updating Your CurrentJDK on the Mac

If, like me, you do Java development on the Mac, at some point you’ll need to change the JDK that the Mac uses by default.  Tools such as Eclipse and IntelliJ make life easier as they can point to specific JDKs (and you can tell it which one), but certain tools like Maven determine the correct JDK based on directories, etc.

The Mac makes this extremely frustrating with the way it handles JDKs (weird directories, many symbolic links, etc.) and their Console doesn’t change the current JDK just the Runtime.

I found this link that makes updating your current jdk rather easy.  Its a short command-line script that I can only describe as magic:
Setting Current JDK Programmatically on the Mac.

Spring Web Flow 2 and JUEL Integration

Its a tale of romance and tragedy. Okay, not really. But that’s certainly a better starting sentence than, “hey I got Spring Web Flow 2 to work with JUEL instead of JBoss EL.” Anyway, because CAS 4 is utilizing JaValid for its annotation-based validation logic, we’re required to have JUEL on our classpath (JaValid has some compile-time dependencies on JUEL). Unfortunately, the EL specification states that only one provider can be on the path at a time (at the very least, it complains when I have two). Damn them! Can’t we all just get along? Unfortunately, out of the box Spring Web Flow can either work with JBoss EL, or OGNL. OGNL was causing its own set of problems. Since I couldn’t get JaValid to compile against JBoss EL, that meant getting Spring Web Flow 2 to work with JUEL. You think that’d be easy. Not so much.

Its not exceedingly difficult. It just requires writing some custom code. First, we need a ELResolver for JUEL that can process methods (JUEL doesn’t include one). I “borrowed” the one from Camel and modified it a little bit:

/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jasig.cas.server.web.util;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
import java.beans.FeatureDescriptor;

import javax.el.BeanELResolver;
import javax.el.ELContext;
import javax.el.ELResolver;

/**
 * Allows JUEL to resolve methods.
 *
 * @author Scott Battaglia
 * @version $Revision$ $Date$
 * @since 4.0.0
 */
public class MethodELResolver extends ELResolver {

    @Override
    public Object getValue(final ELContext elContext, final Object base, final Object property) {
        Method method = findMethod(elContext, base, property);
        if (method != null) {
            elContext.setPropertyResolved(true);
            return method;
        }

        return null;
    }

    public boolean isReadOnly(final ELContext elContext, final Object o,final  Object o1) {
        return false;
    }

    public Class<?> getType(ELContext elContext, Object o, Object o1) {
        return null;
    }

    public void setValue(ELContext elContext, Object o, Object o1, Object o2) {
        // nothing to do
    }

    public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext elContext, Object o) {
        return null;
    }

    public Class<?> getCommonPropertyType(ELContext elContext, Object o) {
        return null;
    }

    protected Method findMethod(final ELContext elContext, final Object base, final Object property) {
        if (base != null &amp;amp;&amp;amp; property instanceof String) {
            final Method[] methods = base.getClass().getMethods();
            final List<Method> matching = new ArrayList<Method>();
            for (final Method method : methods) {
                if (method.getName().equals(property) &amp;amp;&amp;amp; Modifier.isPublic(method.getModifiers())) {
                    matching.add(method);
                }
            }
            int size = matching.size();
            if (!matching.isEmpty()) {
                if (size > 1) {
                    // TODO there's currently no way for JUEL to tell us how many parameters there are
                    // so lets just pick the first one that has a single param by default
                    for (final Method method : matching) {
                        final Class<?>[] paramTypes = method.getParameterTypes();
                        if (paramTypes.length == 1) {
                            return method;
                        }
                    }
                }
                // lets default to the first one
                return matching.get(0);
            }
        }
        return null;
    }
}

Since JUEL doesn’t have a ELResolver that handles methods you can’t just give the ExpressionFactory to the existing WebFlowELParser as a construction parameter (methods still won’t work then). You need to construct your own parser based off the existing parser:

/**
 * Copyright 2008 JA-SIG, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0

 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jasig.cas.server.web.util;

import java.util.ArrayList;
import java.util.List;

import javax.el.ELContext;
import javax.el.ELResolver;
import javax.el.FunctionMapper;
import javax.el.VariableMapper;

import org.springframework.binding.expression.el.DefaultELResolver;
import org.springframework.binding.expression.el.ELContextFactory;
import org.springframework.binding.expression.el.ELExpressionParser;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.expression.el.*;
import de.odysseus.el.ExpressionFactoryImpl;
import de.odysseus.el.tree.impl.Builder;
import de.odysseus.el.tree.impl.Cache;
import de.odysseus.el.tree.TreeStore;

public final class JuelWebFlowELExpressionParser extends ELExpressionParser {

	public JuelWebFlowELExpressionParser() {
		super(new ExpressionFactoryImpl(new TreeStore(new Builder(Builder.Feature.METHOD_INVOCATIONS), new Cache(1000))));
		putContextFactory(RequestContext.class, new RequestContextELContextFactory());
	}

	private static class RequestContextELContextFactory implements ELContextFactory {
		public ELContext getELContext(final Object target) {
			final RequestContext context = (RequestContext) target;
			final List<ELResolver> customResolvers = new ArrayList<ELResolver>();
			customResolvers.add(new RequestContextELResolver(context));
			customResolvers.add(new FlowResourceELResolver(context));
			customResolvers.add(new ImplicitFlowVariableELResolver(context));
			customResolvers.add(new ScopeSearchingELResolver(context));
			customResolvers.add(new SpringBeanWebFlowELResolver(context));
			customResolvers.add(new ActionMethodELResolver());
			customResolvers.add(new MethodELResolver());
			ELResolver resolver = new DefaultELResolver(customResolvers);
			return new WebFlowELContext(resolver);
		}
	}

	private static class WebFlowELContext extends ELContext {

		private final ELResolver resolver;

		public WebFlowELContext(ELResolver resolver) {
			this.resolver = resolver;
		}

		public ELResolver getELResolver() {
			return resolver;
		}

		public FunctionMapper getFunctionMapper() {
			return null;
		}

		public VariableMapper getVariableMapper() {
			return null;
		}
	}
}

Finally, all of this needs to be wired up in your Spring XML configuration file. I’ve included our example here:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:webflow="http://www.springframework.org/schema/webflow-config"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd">

    <webflow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices">
        <webflow:flow-location path="/WEB-INF/login/login.xml" id="login"/>
    </webflow:flow-registry>

    <webflow:flow-builder-services expression-parser="expressionParser" id="flowBuilderServices" />

    <bean id="expressionParser" class="org.jasig.cas.server.web.util.JuelWebFlowELExpressionParser" />

    <webflow:flow-executor id="flowExecutor"/>

    <bean class="org.springframework.webflow.mvc.servlet.FlowHandlerAdapter"
          p:flowExecutor-ref="flowExecutor"/>

    <bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping"
          p:flowRegistry-ref="flowRegistry"
          p:order="0"/>
</beans>

Be sure to include JUEL in your Web application and that should be all you need.

Accessing enumerations in JSP

I’m posting this here just because it took me a little while to find. Let’s say you have an enumeration:
enum OperationType {REDIRECT, POST, NONE}

and you need to make some decision in your JSP page based on the chosen OperationType. How do you refer to the enumeration? Apparently you do this:

<c:if test="${myClass.myOperationType == 'REDIRECT'}">

You need to refer to the enumerations not by their fully qualified class name but by the String version of them. Note: I believe this might only work in Unified EL.

el-api.jar Fun!

If you’re developing a new Servlet project and you’re using say Spring Web Flow with oh say, jboss-el (which I recommend), and you build using Maven2, watch out for the fact that the jboss-el pom definition pulls in the el-api jar. You’ll want to exclude that:

<dependency>
<groupId>org.jboss.seam</groupId>
<artifactId>jboss-el</artifactId>
<version>2.0.0.GA</version>
<scope>runtime</scope>
<exclusions>
<exclusion>
<groupId>javax.el</groupId>
<artifactId>el-api</artifactId>
</exclusion>
</exclusions>
</dependency>

If you don’t exclude it, when you deploy to the container (i.e. Tomcat 6), you’ll see a fun message like this when you load up your JSP page:

java.lang.LinkageError: loader constraints violated when linking javax/el/ExpressionFactory class

It would have been nice if the jar file was ignored like the servlet-api is ignored if its pulled into the WAR file deployment.

Upgrading to Spring Web Flow 2 for CAS4

For  better or worse, CAS3 has always used Spring Web Flow 1 even after Spring Web Flow 2 came out. As part of CAS4, we’re looking at upgrading to Spring Web Flow 2.  Overall, Spring Web Flow has simplified a lot of stuff.  Calling our service methods directly (for the most part) instead of creating an action to do the same thing is a huge time and codesaver.

I’ve encountered a few weird things along the way that I’m still working the kinks out on:

1. Configuring an External Validator - Spring Web Flow 2 makes it easy to use validators, as long as you follow their conventions (either a method on the model object or a validator that follows particular naming conventions).  I’m looking at leveraging the Javalid framework, which uses annotations on the model objects and one validator.  This should make it easier for deployers to use their own custom credentials objects and keep their validation logic within their class (reducing maintenance).  One problem, its not clear how to register a custom validator that doesn’t conform to the Spring Web Flow 2 conventions.  I’ve asked for information on the forum for anyone interested: http://forum.springframework.org/showthread.php?t=64418

2. Using Simple Domain/Model Objects - In Spring Web Flow 1 you called setupForm on a FormAction and magical stuff happened to instantiate the appropriate objects, etc.  In Spring Web Flow 2 you can merely call evaluate on a method and put the resulting object in the flow or request scope and use that as the model object.  Powerful!  However, its not clear to me how to merely use a simple object (i.e. an instantiation of UsernamePasswordCredentialsImpl).

For those interested in learning more, I’ve found the following resources useful:

Watch out for the IntelliJ 8.0.1 Upgrade

Not sure if its something I did or not, but I recently upgraded from IntelliJ IDEA 8.0 to IntelliJ IDEA 8.0.1 and it no longer properly imports my projects that are Maven2 based (i.e. CAS!). Reverting back to 8 didn’t solve the problem either.

I checked on my notebook, which I didn’t upgrade and the project still loads fine. Just warning everyone to watch out so it doesn’t happen to them (I’m still not sure why it happened to me).

Overall, I’m happy with IDEA, though currently less happy since its not working for me ;-)

[UPDATE]: Based on a recommendation from the comments, I’ve submitted a support request for this. I’ll update if/when I hear back. For now I’ll be living in the Eclipse world *shudder*

[UPDATE 2]: Apparently JetBrains support is ABSURDLY FAST. Recommended I erase the cache directory and restart the application. That appears to have done the trick. Waiting to hear back on whether the cache just got corrupted during the upgrade. Back in business though.

Think of the cookies!!

As part of the CAS4 development, I’m working with the new Spring MVC Controller annotations.  Overall, the attributes are great, but I’ve found that they can be lacking.

While you can easily specify an annotation to bind the parameter from a request to a parameter on the method, you cannot bind a cookie value to  a parameter on a method!  You have to use the cumbersome WebUtils method.

I’ve opened a JIRA request for a new annotation: SPR-5323.

I’m hoping to either convince them to implement this or I’ll try doing it myself over the weekend.

Obtaining Metadata about a user’s credentials

One of the things that was missing in CAS3, and hopefully rectified in CAS4, is our inability to display more information to the user.  This includes warnings such as “Your password will expire in 10 days,” “You last logged in from IP address 128.76.544.33″, etc.

The CAS4 architecture will support this notion of informational messages (we currently call them Warnings, which may be slightly misleading).  The question, since we don’t store this information currently at Rutgers, is where do people normally store this information?  Is it in LDAP with the credentials, is it in a separate meta information repository, is it stored in Kerberos, or is it encoded in the credentials themselves (i.e. a certificate is going to expire in 10 days).

For us to design the architecture correctly, it would help us know where this information is commonly stored so we can make it easy to retrieve (and possibly provide some common ones).

Please leave your comments below, or on the CAS mailing list.