Как сделать редирект в Jboss Seam фреймворке

Если вы еще не знаете, как сделать редирект в приложении, написанном на базе Seam фреймворка — прочитайте эту статью. В ней содержится простой пример реализации редиректа на основе Tuckey Url Rewrite фильтра.

Описание задачи

Допустим, у нас есть база данных товаров магазина отечественной сантехники. Мы знаем JBoss и Seam достаточно хорошо, чтобы спроектировать, спрограммировать и развернуть online магазин. Что мы и сделали. Все прекрасно работает. Вот так будет выглядеть адрес стиральной машинки: http://www.super-best-sanitary.ru/household/washer/producer/ct1243.html

Покупатели покупают, веб сайт процветает, база данных обновляется, склады с товаром опустошаются. Прошло время. Поставщик этих прекрасных стиральных машинок запустил собственный веб сайт с каталогом и решил сделать с каждой страницы с описанием девайса сделать ссылку на страницу нашего магазина. К сожалению, он не захотел вникать в логику формирования наших ссылок, а сделал просто вот так: http://www.super-best-sanitary.ru/?id=987251. И встала перед нами задача сделать перенаправление на человеческую страницу нашего сайта.

Есть у нас таблица, которая связывает идентификационный номер девайса производителя с самим девайсом и производителем и категорией товара в нашем магазине. Чтобы было проще - изобразим ее так:

Вот такой Entity bean получится из этой таблицы:

package com.shop;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Goods {
	
	@Id
	@GeneratedValue
	private Integer id;
	private String producer_link_name;
	private String category_link_name;
	private String device_link_name;
	private Integer device_producer_id;
	
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getProducer_link_name() {
		return producer_link_name;
	}
	public void setProducer_link_name(String producer_link_name) {
		this.producer_link_name = producer_link_name;
	}
	public String getCategory_link_name() {
		return category_link_name;
	}
	public void setCategory_link_name(String category_link_name) {
		this.category_link_name = category_link_name;
	}
	public String getDevice_link_name() {
		return device_link_name;
	}
	public void setDevice_link_name(String device_link_name) {
		this.device_link_name = device_link_name;
	}
	public Integer getDevice_producer_id() {
		return device_producer_id;
	}
	public void setDevice_producer_id(Integer device_producer_id) {
		this.device_producer_id = device_producer_id;
	}
	
		
}

Теперь идем на http://tuckey.org/urlrewrite/ и скачиваем последнюю версию Url Rewrite фильтра. Как написано на этой странице – распаковываем Jar библиотеку фильтра в web-inf, добавляем следующую запись в web.inf:

  <filter-name>UrlRewriteFilter</filter-name>
  <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
  <!-- set the amount of seconds the conf file will be checked for reload
          can be a valid integer (0 denotes check every time,
          empty/not set denotes no reload check) -->
  <init-param>
   <param-name>confReloadCheckInterval</param-name>
   <param-value>60</param-value>
  </init-param>
  <!-- sets up log level (will be logged to context log) -->
  <init-param>
   <param-name>logLevel</param-name>
   <param-value>log4j</param-value>
  </init-param>
  <!-- you can disable status page if desired
          can be: true, false (default true) -->
  <init-param>
   <param-name>statusEnabled</param-name>
   <param-value>true</param-value>
  </init-param>
  <!-- you can change status path so that it does not
          conflict with your installed apps (note, defaults
          to /rewrite-status) note, must start with / -->
  <init-param>
   <param-name>statusPath</param-name>
   <param-value>/my_rewrite_status</param-value>
  </init-param>
 </filter>

Страница my_rewrite_status покажет статус текущих настроек фильтра и массу другой полезной отладочной информации.

Готово. Осталось написать свой настроечный файл и бин, который будет обрабатывать запрос. В настроечный файл фильтра добавим такую запись:

<rule>
	<from>^/?id=([0-9+])$</from>
	<set name="producer_id">$1</set>
	<run class="com.shop.ProducerRewriteAction" method="doDevice" neweachtime="false"/>
	<to type="permanent-redirect">%{attribute:url}</to>
</rule>

Как видно из настроек мы будем делать редирект со статусом 301. Класс обработчик называется com.shop.ProducerRewriteAction. Осталось его создать и имплементировать.

package com.shop;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.shop.bean.Device;


public class ProducerRewriteAction {
	private static final String DEVICE_JNDI_NAME = "super-best-sanitary/DeviceBean/local";
	public void doDevice(HttpServletRequest request, HttpServletResponse response){
		
		int producer_id = (Integer)request.getAttribute("producer_id");
		String url = "/";
		try {
			Device device = (Device) (new InitialContext()).lookup(DEVICE_JNDI_NAME);
			url = device.getURLByProducerId(producer_id);
		} catch (NamingException e) {
			return;
		}
		request.setAttribute("url", url);		
		
	}
}

Интерфейс бина:

package com.shop.bean;

public interface Device {

	public String getURLByProducerId(int producer_id);

}

Имплементация:

package com.shop.bean;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.jboss.seam.annotations.Name;

import com.shop.Goods;

@Name("Device")
@Stateless
public class DeviceBean implements Device {

    @PersistenceContext
    private EntityManager em;
    
	public String getURLByProducerId(int producer_id) {
		Goods selectedDevice = (Goods)em.createQuery("select n from Goods n where n.device_producer_id = :id").setParameter("id", producer_id).getSingleResult();
		return "/household/" + selectedDevice.getCategory_link_name() + "/" + selectedDevice.getProducer_link_name() + "/"+selectedDevice.getDevice_link_name() + ".html";
	}

}

Единственный комментарий – чтобы использовать таким образом dependency injection для EntityManager соединение с базой данных должно быть настроено заранее.

Таким образом, все входящие запросы вида /?id=987251 будут перенаправлены на вполне приличный URL: /household/washer/producer/ct1243.html

Александр Смелков
Санкт-Петербург Зима 2008