Help

Inactive Bloggers
07. Dec 2007, 05:48 CET, by Pete Muir

A quick update for those who like living on the edge ;-)

Firstly, I've published a snapshot of Embedded JBoss beta3 - this is a pretty major update to Embedded JBoss (and its components like EJB3, Hibernate etc.) and pretty close to the beta3 release - so it would be great to get some feedback. It's looking good (but there is a known issue with JMS).

If you want to give it a go:

  • Tomcat: Follow these instructions to install into a /fresh/ copy of Tomcat.
  • SeamTest: Replace your current bootstrap directory and lib/test directory

Secondly, if you've made the move to Java 6 you need a couple of tweaks to Embedded JBoss for the beta3 snapshot:

  1. Start java with -Dsun.lang.ClassLoader.allowArraySyntax=true
  2. Java 6 SE includes JAXB 2.0, but Embedded JBoss needs JAXB 2.1. The easiest way to do this is to put the jaxb-api.jar (for JAXB 2.1) into the endorsed library directory, then start java with -Djava.endorsed.dirs=/path/to/endorsed/directoy

Your testng launcher would look something like this:

<testng outputdir="/path/to/test/report/directory">
   <jvmarg line="-Djava.endorsed.dirs=/path/to/endorsed/directory" />
   <jvmarg line="-Dsun.lang.ClassLoader.allowArraySyntax=true" />
   <classpath refid="test.classpath" />
   <xmlfileset dir="/path/to/src/directory" includes="**/testng.xml" />
</testng>
07. Dec 2007, 01:55 CET, by Christian Bauer

Seam offers some basic infrastructure for CAPTCHA creation and validation, so all you have to do if you want to add CAPTCHA validation to a form is add a single form field and show the picture with <h:graphicImage/>. The only built-in implementation we shipped with Seam 1.x and 2.0 was based on JCaptcha, but you could easily extend it and do your own question/answer thing. This is actually what I did and you can see my simplified math question CAPTCHA if you try to post a comment to this entry.

I'm not the only Seam user who had problems with JCaptcha (search the Seam forum if you want to know more, basically: it's over-engineered, needs seconds to startup, sometimes needs seconds to render an image, the default image generators are hard to read but CAPTCHAs are still easy to break, etc). So Gavin wrote a new Captcha implementation we could ship with Seam 2.0.1. This is in Seam CVS now and not released, so ignore this blog entry if you are not a Seam CVS user and come back to it when we release 2.0.1.

The usage scenario is still the same, add an input field and a picture to your form:

<s:validateAll>
    <h:inputText size="6" maxlength="6" required="true" id="verifyCaptcha" value="#{captcha.response}"/>
    <h:graphicImage value="/seam/resource/captcha"/>
</s:validateAll>

By default Gavins implementation only renders a simple math question as an image with no obfuscation at all. So I extended the built-in classes. This is how my generated CAPTCHA questions look like:

The trick is to tell the user to ignore any circles - and to never use any characters that look like circles (zero, o, O). I don't think people will have issues deciphering these characters, I've been trying myself a few dozen times during testing and failed only once or twice. I think that this CAPTCHA is quite difficult to break automatically though, the grey shades used for obfuscation and real text are the same and circles really destroy the original shape of the characters. And if it's broken, we can simply add more aggressive circles in small incremental steps or increase the rotation range of the characters.

Another thing that makes CAPTCHAs less painful is to store the data in the HTTP session, so that if a user entered a captcha once on your site, you don't require it to be entered a second time. But that's all built-in with the new Seam Captcha stuff.

Here is my custom code for the image generation:

package org.jboss.seam.wiki.core.captcha;

import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Create;
import org.jboss.seam.annotations.Install;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.captcha.Captcha;
import org.jboss.seam.captcha.CaptchaResponse;

import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;

@Name("org.jboss.seam.captcha.captcha")
@Scope(ScopeType.SESSION)
@Install(precedence = Install.APPLICATION)
public class WikiCaptcha extends Captcha {

    Color backgroundColor = new Color(0xf5,0xf5, 0xf5);
    Font textFont = new Font("Arial", Font.PLAIN, 25);
    int charsToPrint = 6;
    int width = 120;
    int height = 25;
    int circlesToDraw = 4;
    float horizMargin = 20.0f;
    double rotationRange = 0.2;
    String elegibleChars = "ABDEFGHJKLMRSTUVWXYabdefhjkmnrstuvwxy23456789";
    char[] chars = elegibleChars.toCharArray();

    @Override
    @Create
    public void init() {
        super.init();

        StringBuffer finalString = new StringBuffer();
        for (int i = 0; i < charsToPrint; i++) {
            double randomValue = Math.random();
            int randomIndex = (int) Math.round(randomValue * (chars.length - 1));
            char characterToShow = chars[randomIndex];
            finalString.append(characterToShow);
        }

        setChallenge(finalString.toString());
        setCorrectResponse(finalString.toString());
    }

    @Override
    public BufferedImage renderChallenge() {

        // Background
        BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = (Graphics2D) bufferedImage.getGraphics();
        g.setColor(backgroundColor);
        g.fillRect(0, 0, width, height);

        // Some obfuscation circles
        for (int i = 0; i < circlesToDraw; i++) {
            int circleColor = 80 + (int)(Math.random() * 70);
            float circleLinewidth = 0.3f + (float)(Math.random());
            g.setColor(new Color(circleColor, circleColor, circleColor));
            g.setStroke(new BasicStroke(circleLinewidth));
            int circleRadius = (int) (Math.random() * height / 2.0);
            int circleX = (int) (Math.random() * width - circleRadius);
            int circleY = (int) (Math.random() * height - circleRadius);
            g.drawOval(circleX, circleY, circleRadius * 2, circleRadius * 2);
        }

        // Text
        g.setFont(textFont);
        FontMetrics fontMetrics = g.getFontMetrics();
        int maxAdvance = fontMetrics.getMaxAdvance();
        int fontHeight = fontMetrics.getHeight();
        float spaceForLetters = -horizMargin * 2 + width;
        float spacePerChar = spaceForLetters / (charsToPrint - 1.0f);

        char[] allChars = getChallenge().toCharArray();
        for (int i = 0; i < allChars.length; i++ ) {
            char charToPrint = allChars[i];
            int charWidth = fontMetrics.charWidth(charToPrint);
            int charDim = Math.max(maxAdvance, fontHeight);
            int halfCharDim = (charDim / 2);
            BufferedImage charImage = new BufferedImage(charDim, charDim, BufferedImage.TYPE_INT_ARGB);
            Graphics2D charGraphics = charImage.createGraphics();
            charGraphics.translate(halfCharDim, halfCharDim);
            double angle = (Math.random() - 0.5) * rotationRange;
            charGraphics.transform(AffineTransform.getRotateInstance(angle));
            charGraphics.translate(-halfCharDim, -halfCharDim);
            int charColor = 60 + (int)(Math.random() * 90);
            charGraphics.setColor(new Color(charColor, charColor, charColor));
            charGraphics.setFont(textFont);
            int charX = (int) (0.5 * charDim - 0.5 * charWidth);
            charGraphics.drawString("" + charToPrint, charX, ((charDim - fontMetrics.getAscent())/2 + fontMetrics.getAscent()));
            float x = horizMargin + spacePerChar * (i) - charDim / 2.0f;
            int y = ((height - charDim) / 2);
            g.drawImage(charImage, (int) x, y, charDim, charDim, null, null);

            charGraphics.dispose();
        }
        g.dispose();

        return bufferedImage;
    }

    @CaptchaResponse(message = "#{messages['lacewiki.label.VerificationError']}")
    public String getResponse() {
        return super.getResponse();
    }
}

(If some of the code looks familiar, you have maybe seen it here before.)

I still need to finish a few other things before I can upgrade the software running this site to the new Captcha though. So if you want to try it out, get Seam CVS and put this class into your codebase, no other configuration necessary.

05. Dec 2007, 22:55 CET, by Emmanuel Bernard

Max and I will be at JavaPolis next week. I don't know what Max is doing there but I will talk about Hibernate Search and JSR-303 Bean Validation, both talks on Thursday the 13th. Speaking of JSR-303, I have done a quick interview with Mark Newton on the topic: in a nutshell, it's shaping well and we hope to have a draft out in a month or so for you to review :)

Max should have some exciting news on the tooling side for Seam, JSF and Hibernate.

Speaking of Seam, Pete will be there as well for a university around JBoss Seam

Come by the booth, I'm sure we will have some beers for you.

One of my favorite feature in JBoss Tools is the Project Archives which provides automatic and incremental archiving of arbitrary directories into a zipped archive (jar,zip,war,etc) or into an exploded directory. Usable for any development that wants hot and/or exploded deployment.

A screen cast is available at docs.jboss.org, but if you want the details read the following...

Project Archives View

To start using it you open the Project archives view, select a project and right click in the view to add the type of jar you want (each project can have multiple archives defined).

In the dialog that comes up you specify what the name of the archive should be, where it should be located and whether the folder should be archived/compressed or exploded.

The tools will dependent on the archive type and project files give a best-guess on which files you would like to include in the archive. You can of course customize this freely afterwards.

When you have pressed Ok, you will be able to see the resulting archive or folder in Eclipse or if you configured it to go the file system use your favorite file browser to see the result.

Automatic Incremental packaging

When first defined through the Project archives view JBoss Tools will automatically monitor changes and keep the archive or folder in sync. Since the changes and archiving is done incrementally there are no need to wait for Ant or Maven to collect the various artifacts and do a full archiving.

I suggest watching the screen cast to get a sense of how easy and fast this works.

Exploded jars

The exploded archive support allows you to use this with any server that supports what is called exploded deployment (commonly used with many Java and non-java servers) - thus this is in no way limited to JBoss servers or even Java technology; any technology that needs to copy a set of files to a directory or archive will be able to benefit from this feature.

What about JBossIDE packaging ?

JBossIDE 1.x had a similar feature which relied on generating an ant script and execute Ant to do the actual packaging.

The advantages of the new Project Archives is that it does not wait for a launch of Ant and instead of doing a full build/repackaging of a compressed archive every time, we can now do it incrementally and with the option of exploded form.

To migrate from JBossIDE packaging to JBoss Tools archiving you can use the conversion tool which we made available at download.jboss.org.

The conversion is as simple as:

java -jar packaging-converter.jar /path/to/.packaging

Ant and Maven

To avoid specifying how packaging works twice, once in JBoss Tools and once in your favorite build system we added an ant task to allow you to use it externally from Eclipse.

The following is an example of how that is used:

<property name="studio.home" location="${eclipse.home}/../studio/eclipse"/>

 <path id="generate-archives-classpath">
  <fileset dir="${eclipse.home}/plugins">
   <include name="org.eclipse.equinox.common_*.jar"/>                                    
  </fileset>
  <fileset dir="${studio.home}">
   <include name="plugins/org.jboss.ide.eclipse.archives.core**/archivescore.jar"/>
   <include name="plugins/org.jboss.ide.eclipse.archives.core**/lib/*.jar"/>         
  </fileset>
</path>
                 
 <taskdef name="generate-archives" classpathref="generate-archives-classpath"       
classname="org.jboss.ide.eclipse.archives.core.ant.GenerateArchivesTask"/>
         
<target name="run-packaging">
 <!-- each project needs to have their root dir defined when running outside eclipse -->
 <property name="pojp.dir" value="C:/Users/max/workspace/pojp"/>
 <generate-archives projectPath="C:/Users/max/workspace/pojp"/>
</target>

Final words

Thanks goes out to Rob for showing me how the Ant task works and for Marshall to get the conversion script ready. We hope to integrate that more directly into JBoss Tools in an upcoming release.

If you want to see this in live action look me up at JavaPolis next week (in the JBoss/Red Hat booth).

Gavin pointed out that he did not get the separation between business logic and UI. Which leads to the question of why are we layering in the first place? I have heard many many bad reasons to justify layering. Actually, this is one of my favorite questions when I give a training. It's always fun to see the people sweating over a justification.

What are the problems layering is trying to solve:

  • separation of concerns
  • easier maintainability
  • loose coupling
  • reusability
  • justifying yet another additional level of management in the organization

So what could be the problems of such static horizontal layers:

  • the distinction between them could be fuzzy and lead to endless hair splitting discussions at the cafeteria
  • they naturally force state conversion and state transfer across layers for the sake of isolation. State conversion and state transfer are hard to solve, slow and lead to duplication and maintainability nightmares: where did my loose coupling go?
  • they have no real purpose half the time but to please the God of Architecture, tens of thousands of empty and meaningless classes are written to fill up layers implementing delegation patterns over delegation patterns.

Horizontal (hence static) layering in the standard sense of it (presentation, service, component, dao) is a notion inherited of the dark days where:

  • the component model was bloated and unusable
  • stateless applications were the golden goal because it scales better

We need a way to define dynamic reusable concerns, interactions between them, avoid unnecessary state copy and duplication, and find more natural boundaries between concerns. A component model could achieve that if it were shared by the presentation, the business and the data access layer technology with no blind butcher axed boundaries.

Now, bare with me a second and imagine that you had at your disposal a lightweight component model, where components could depend on each other in a strongly and decoupled way, where components could interact with each other by sending events, where components could hold state and whose lifecycle would be handled automatically.

A component would describe and implement a business need

@Component
public class Checkout {

It would depend on other business components

@Component @Asynchronous @PayByCheque 
public class ChequePaymentProcessor implements PaymentProcessor {

describing the dependency and its requirements with natural adjectives in a loosed coupled and type safe way

    @In @Asynchronous @PayByCheque PaymentProcessor

It could have declarative behavior

@Transactional @Component
public class Checkout { ... }

It could notify other business components

    @In @Notifier @New Event<Order> order;

And other components could listen to those events in a decoupled way

@Component 
public class AuditAgent {
    public void onOrder(@Observes @New Order order) { ... }
}

Oh, and finally it would be concise (check the previous examples :) )

Of course some components could be more technical than business oriented, some could be more DAO-ish others more Service-ish. But the point is that this component model and the declarative loose coupled type safe way to describe interactions with other components is very flexible and adjusts to your needs. It fits your needs, you have to fit in it.

So what do we have?

  • separation of concern: loose coopling and declarative interations and behaviors
  • easier maintainability: type safety and no state transfer
  • loose coupling: interface / implementation runtime and type safe binding
  • reusability: a component represents a use case, a story of your application
  • justifying yet another additional level of management in the organization: if you really insist, we can find a way

Seam has paved the way of components describing a story shared across all the technologies in my system and Web Beans will bring it the elegant type safe declarative touch of Guice.

Showing 1026 to 1030 of 1245 blog entries