Help

Built with Seam

You can find the full source code for this website in the Seam package in the directory /examples/wiki. It is licensed under the LGPL.

In some instances you need to do a redirect to a URL outside your application, for example when a given page is widely linked by external sites and is moved to another application, or on SEO situations that require a 301.

package com.boulevardr.ui;

import java.io.IOException;

import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletResponse;

import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;

@Name("permanentRedirector") public class PermanentRedirector
{
        @In FacesContext facesContext;
        
        public void toUrl(String location)
        {
                HttpServletResponse res = (HttpServletResponse)facesContext.getExternalContext().getResponse();
                res.setContentType("text/html");
                res.setHeader("Location", location);
                res.setStatus(301);
                facesContext.responseComplete();
        }
}

on pages.xml:

<page view-id="/page.seam" action="#{permanentRedirector.toUrl('http://site.to.redirect')}" />

It was suggested that this kind of redirect should be done in a Servlet. However, I prefer to keep all flow-related rules on a single location (i. e.: pages.xml), and that was my main motivation behind this solution.

1 comment:
 
10. Dec 2010, 09:53 America/New_York | Link

The above solution does require you to construct absolute URLs and prevents you from using the Seam Redirect component.

Here's my solution. To enable a 301 redirect call the following:

PermenantRedirectFilter.redirect301();

before the code you use your normal code to redirect

Here is the redirector class:

package com.example;

import org.jboss.seam.annotations.Install;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.jboss.seam.annotations.web.Filter;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.web.AbstractFilter;

import javax.faces.context.FacesContext;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

import static org.jboss.seam.ScopeType.APPLICATION;
import static org.jboss.seam.annotations.Install.BUILT_IN;

@Scope(APPLICATION)
@Name("permenantRedirectFilter")
@Install(precedence = BUILT_IN, classDependencies = "javax.faces.context.FacesContext")
@BypassInterceptors
@Filter(within = "org.jboss.seam.web.ajax4jsfFilter", around = "org.jboss.seam.web.redirectFilter")
public class PermenantRedirectFilter extends AbstractFilter {
    public static final String REDIRECT_301 = "REDIRECT_301";

    public static void redirect301() {
        if (Contexts.isEventContextActive()) {
            Contexts.getEventContext().set(REDIRECT_301, true);
        }
    }


    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        chain.doFilter(request, wrapResponse((HttpServletResponse) response));
    }


    private static ServletResponse wrapResponse(HttpServletResponse response) {
        return new HttpServletResponseWrapper(response) {

            @Override
            public void sendRedirect(String location) throws IOException {
                if (FacesContext.getCurrentInstance() != null
                        && Contexts.isEventContextActive()
                        && Contexts.getEventContext().isSet(REDIRECT_301)) {

                    setHeader("Location", toAbsolute(location));
                    setStatus(SC_MOVED_PERMANENTLY);

                } else {
                    super.sendRedirect(location);
                }
            }

            protected String toAbsolute(String location) {
                if (location == null) location = "/";
                if (!location.startsWith("http://") && !location.startsWith("https://")) {
                    try {
                        StringBuilder sb = new StringBuilder();
                        URL reqUrl = new URL(getRequestUrl(FacesContext.getCurrentInstance()));
                        sb.append(reqUrl.getProtocol());
                        sb.append("://");
                        sb.append(reqUrl.getHost());
                        if (("http".equals(reqUrl.getProtocol()) && (reqUrl.getPort() != -1 && reqUrl.getPort() != 80))
                            || ("https".equals(reqUrl.getProtocol()) && (reqUrl.getPort() != -1 && reqUrl.getPort() != 443))){
                            sb.append(':').append(reqUrl.getPort());
                        }
                        if (!location.startsWith("/")){
                            sb.append('/');
                        }
                        sb.append(location);
                        location = sb.toString();
                    } catch (MalformedURLException e){
                    }
                }
                return location;
            }

            protected String getRequestUrl(FacesContext facesContext) {
                Object request = facesContext.getExternalContext().getRequest();
                if (request instanceof HttpServletRequest) {
                    return ((HttpServletRequest) request).getRequestURL().toString();
                } else {
                    return null;
                }
            }

        };

    }
}

The only caveat is that I took a shortcut and ignored the httpPort and httpsPort from Pages. This could be integrated fairly easily. Could probably be tidied a little too!