NOTE: This is outdated information that applies only to Tapestry 4. For current information see

http://tapestry.apache.org/integrating-with-spring-framework.html

Prerequisite

Please read the following Wiki Tapestry4Spring to get Spring implemented into your application.

Description

In my applicationContext.xml I have a bean called profileManager that loads the user profile information from the username, so I was a little lost when I downloaded Tapestry4-beta9 and found out that the Global and Visit object are going to be deprecated and that injecting your objects was the vogue way of doing things now. After reviewing the online documentation and java docs and having a limited knowledge of HiveMind I figured out what this injecting was all about. Now I had to figure out how to get my user profile loaded into the application with a user that has already been authenticated from the container (Tomcat+JOSSO). The following documentation is what I did to make that happen, I don't know if it's the most correct way of doing things but I learned a little and I wanted to share this with you.

Take 1:

I created a session Application State Object called user.credentials and injected that into my Home.page specification. At this point I'm not interfacing with Spring.

UserCredentials.java (Replaces the Visit object)

public class UserCredentials implements Serializable
{
    static final long serialVersionUID = 8527372709128066158L;

    private String _userName;

    public String getUser( IRequestCycle cycle )
    {
        if( _userName == null )
        {
            _userName = cycle.getInfrastructure().getRequest().getRemoteUser();
        }

        return _userName;
    }
}

hivemodule.xml

<module id="etechstudios.app" version="1.0.0">
    <contribution configuration-id="tapestry.state.ApplicationObjects">
        <state-object name="user.credentials" scope="session">
            <create-instance class="com.etechstudios.app.security.UserCredentials"/>
        </state-object>
    </contribution>
</module>

Home.java

public abstract class HomePage extends BasePage implements PageBeginRenderListener
{
    public abstract UserCredentials getUserCredentials();

    public abstract String getUsername();
    public abstract void setUsername(String username);

    public void pageBeginRender(PageEvent event)
    {
        setUsername(getUserCredentials().getUser( event.getRequestCycle() ));
    }
}

Home.page

<page-specification class="com.etechstudios.app.html.HomePage">
    <description>App Home Page</description>
    <inject property="userCredentials" type="state" object="user.credentials"/>
</page-specification>

Home.html

<html>
    <body>
        <table>
            <tr>
                <td>
                    Welcome&nbsp;<span jwcid="@Insert" value="ognl:username"/>
                </td>
            </tr>
        </table>
    </body>
</html>

I know that this is quite a bit of code to do something that would only take this in Home.page and Home.html,

I'm sure there about 1 million ways to do this but this is what I figured out.

-- Home.page 
    <inject property="request" object="service:tapestry.globals.WebRequest"/>

-- Home.html
    Welcome&nbsp;<span jwcid="@Insert" value="ognl:infastructure.request.remoteUser"/>

but I'm laying the foundation for communicating with the spring framework and I like to type.

Take 2:

Now I want to add a user validator that validates the user and loads the profile from the database. This will use the Spring framework to get that information.

I needed a bridge between my session and the spring engine without injecting the service every where so this is what I did. I created an application Application State Object factory that creates a global UserValidator object.

UserValidatorFactory.java

This factory takes the Spring bean "profileManager" and passes it to the global UserValidator object when its created.

public class UserValidatorFactory implements StateObjectFactory
{
    private ProfileManager _profileManager;

    public UserValidatorFactory() {}

    public void setProfileManager(ProfileManager profileManager)
    {
        _profileManager = profileManager;
    }

    public Object createStateObject()
    {
        return new UserValidator(_profileManager);
    }
} 

UserValidator.java (Replaces the Global object)

public class UserValidator
{
    public static final String USER_VALIDATOR = "user.validator";

    private ProfileManager _profileManager;

    public UserValidator( ProfileManager profileManager )
    {
        _profileManager = profileManager;
    }

    public UserProfile validateUser( String user )
    {
        return _profileManager.getUser(user);
    }
}

hivemodule.xml

<module id="etechstudios.app" version="1.0.0">
    <contribution configuration-id="tapestry.state.ApplicationObjects">
        <state-object name="user.credentials" scope="session">
            <create-instance class="com.etechstudios.app.security.UserCredentials"/>
        </state-object>
        <state-object name="user.validator" scope="application">
            <invoke-factory object="service:UserValidatorFactory"/>
        </state-object>
    </contribution>

    <service-point id="UserValidatorFactory" interface="org.apache.tapestry.engine.state.StateObjectFactory">
        <invoke-factory>
            <construct class="com.etechstudios.app.security.UserValidatorFactory">
                <set-object property="profileManager" value="spring:profileManager"/>
            </construct>
        </invoke-factory>
    </service-point>
</module>

UserCredentials.java

In this class I use the infrastructure to gain access to the ApplicationStateManager to retreive the global UserValidator object and if you are the first person then you will be creating the UserValidator object.

public class UserCredentials implements Serializable
{
    static final long serialVersionUID = 8527372709128066158L;

    private transient UserProfile _userProfile;
    private String _userName;

    public String getUsername()
    {
        return _userName;
    }

    public UserProfile getUser( IRequestCycle cycle )
    {
        if( _userProfile != null )
        {
            return _userProfile;
        }

        if( _userName == null )
        {
            _userName = cycle.getInfrastructure().getRequest().getRemoteUser();
        }

        ApplicationStateManager asm = cycle.getInfrastructure().getApplicationStateManager();
        UserValidator validator = (UserValidator) asm.get( UserValidator.USER_VALIDATOR );
        _userProfile = validator.validateUser( _userName );

        // Not sure if this is nessary
        if( !asm.exists( UserValidator.USER_VALIDATOR ) )
        {
            asm.store( UserValidator.USER_VALIDATOR, validator );
        }

        return _userProfile;
    }
}

Home.java

public abstract class HomePage extends BasePage implements PageBeginRenderListener
{
    public abstract UserCredentials getUserCredentials();

    public abstract UserProfile getUser();
    public abstract void setUser(UserProfile user);

    public void pageBeginRender(PageEvent event)
    {
        setUser(getUserCredentials().getUser( event.getRequestCycle() ));
    }
}

No modifications are required in the Home.page specification

Home.html

<html>
    <body>
        <table>
            <tr>
                <td>
                    Welcome&nbsp;<span jwcid="@Insert" value="ognl:user.firstName"/>&nbsp;<span jwcid="@Insert" value="ognl:user.lastUpdateTime"/>
                </td>
            </tr>
        </table>
    </body>
</html>

Conclusion

Sorry I didn't comment more but I like code examples.

  • No labels