Java tutorial
/*- * Copyright (c) * * 2012-2014, Facultad Politcnica, Universidad Nacional de Asuncin. * 2012-2014, Facultad de Ciencias Mdicas, Universidad Nacional de Asuncin. * 2012-2013, Centro Nacional de Computacin, Universidad Nacional de Asuncin. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA */ package py.una.pol.karaku.controller; import java.io.IOException; import java.io.Serializable; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import javax.faces.application.FacesMessage; import javax.faces.context.FacesContext; import org.ajax4jsf.javascript.JSObject; import org.hibernate.exception.ConstraintViolationException; import org.richfaces.application.ServiceTracker; import org.richfaces.javascript.JavaScriptService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataIntegrityViolationException; import py.una.pol.karaku.dao.restrictions.Where; import py.una.pol.karaku.dao.search.SearchHelper; import py.una.pol.karaku.exception.UniqueConstraintException; import py.una.pol.karaku.model.DisplayName; import py.una.pol.karaku.security.HasRole; import py.una.pol.karaku.security.KarakuSecurity; import py.una.pol.karaku.util.ControllerHelper; import py.una.pol.karaku.util.I18nHelper; import py.una.pol.karaku.util.KarakuConverter; import py.una.pol.karaku.util.StringUtils; /** * Controler que sirve de soporte para vistas avanzadas o mas genericas. * * @author Arturo Volpe * * @param <T> * entidad * @param <ID> * clave primaria de la entidad * @since 1.1 * @version 1.4 07/02/2013 */ public abstract class KarakuAdvancedController<T, K extends Serializable> extends KarakuBaseController<T, K> implements IKarakuAdvancedController<T, K> { /** * Cadenas estticas para los mensajes mostrados en las vistas. */ private static final String JSFUNCTION = "KarakuExtendedDataTableFix.fixColumns()"; private static final String DELETE_FAILURE = "BASE_ABM_DELETE_FAILURE"; private static final String EDIT_FAILURE = "BASE_ABM_EDIT_FAILURE"; private static final String CREATE_FAILURE = "BASE_ABM_CREATE_FAILURE"; private static final String CREATE_SUCCESS = "BASE_ABM_CREATE_SUCCESS"; private static final String EDIT_SUCCESS = "BASE_ABM_EDIT_SUCCESS"; private static final String DELETE_SUCCESS = "BASE_ABM_DELETE_SUCCESS"; @Autowired private ControllerHelper helper; @Autowired private SearchHelper searchHelper; @Override public List<String> getBaseSearchItems() { List<String> aRet = new ArrayList<String>(); Class<T> clazz = getClazz(); for (Field f : clazz.getDeclaredFields()) { DisplayName displayName = f.getAnnotation(DisplayName.class); if (displayName != null) { aRet.add(I18nHelper.getName(displayName)); } } return aRet; } /** * Retorna la {@link Class} a la que este controller representa. * * @return {@link Class} */ protected Class<T> getClazz() { return getBaseLogic().getDao().getClassOfT(); } private String getField(final String value) { for (Field f : getClazz().getDeclaredFields()) { DisplayName displayName = f.getAnnotation(DisplayName.class); if (displayName == null) { continue; } if (I18nHelper.getName(displayName).equals(value)) { if ("".equals(displayName.path().trim())) { return f.getName(); } else { return f.getName() + "." + displayName.path(); } } } return null; } /** * Delega la responsabilidad da {@link SearchHelper} para realizar bsquedas * genericas. * * @return {@link Where} configurado, utilizar este {@link Where} como punto * de partida. */ @Override public Where<T> getSimpleFilters() { Where<T> where = new Where<T>(); if (!StringUtils.isValid(getFilterOption(), getFilterValue())) { return where; } return searchHelper.buildWhere(getClazz(), getField(getFilterOption()), getFilterValue()); } /** * Retorna un converter general para ser utilizado por cualquier combo * * @return Converter universal */ public KarakuConverter getConverter() { return KarakuConverter.getInstance(); } /** * Retorna una entidad base a ser usada como ejemplo base * * @return Entidad recien creada */ public T getBaseEntity() { return getBaseLogic().getNewInstance(); } @Override @HasRole(KarakuSecurity.DEFAULT_CREATE) public String preCreate() { setMode(Mode.NEW); setBean(getBaseEntity()); return goNew(); } @Override @HasRole(KarakuSecurity.DEFAULT_DELETE) public String doDelete() { try { delete(getBean()); reloadEntities(); helper.createGlobalFacesMessageSimple(FacesMessage.SEVERITY_INFO, getMessageDeleteSuccess()); return postDelete(); } catch (Exception e) { if (!handleException(helper.convertException(e, getClazz()))) { log.warn("doCreate failed", e); helper.createGlobalFacesMessage(FacesMessage.SEVERITY_WARN, getMessageDeleteFailure(), e.getMessage()); } return ""; } } /** * Mtodo que se encarga de invocar a la lgica y de eliminar el bean * actual. * <p> * Es un mtodo de conveniencia para reescribir solamente las partes * relevantes a la eliminaci de la entidad y omitir detalles como la * generacin de mensajes y manejo de excepciones. * </p> * <p> * Cualquier excepcin lanzada aqu ser manejada en * {@link #handleException(Exception)}. * </p> * <p> * Si el caso de uso dicta que se deben eliminar mas de una entidad, se * recomienda que en este mtodo se llame a la lgica y que sea ella la * encargada de realizar la operacin, retornando el elemento relevante para * mostrar al usuario. Se recomienda el uso de la lgica pues aqu no se * pueden realizar transacciones. * </p> * * @param entity * entidad a actualizar * @return T entidad actualizada. */ protected void delete(final T entity) { log.info("Delete llamado"); getBaseLogic().remove(entity); } @Override @HasRole(KarakuSecurity.DEFAULT_EDIT) public String doEdit() { try { edit(getBean()); reloadEntities(); helper.createGlobalFacesMessageSimple(FacesMessage.SEVERITY_INFO, getMessageEditSuccess()); return postEdit(); } catch (Exception e) { if (!handleException(helper.convertException(e, getClazz()))) { log.warn("doEdit failed", e); helper.createGlobalFacesMessage(FacesMessage.SEVERITY_WARN, getMessageEditFailure(), e.getMessage()); } return ""; } } /*** * Retorna el mensaje a ser visualizado cuando un registro es creado con * xito. * * @return Mensaje internacionalizado. */ protected String getMessageCreateSuccess() { return getMessage(CREATE_SUCCESS); } /*** * Retorna el mensaje a ser visualizado cuando un registro no es creado con * xito. * * @return Mensaje internacionalizado. */ protected String getMessageCreateFailure() { return getMessage(CREATE_FAILURE); } /*** * Retorna el mensaje a ser visualizado cuando un registro es editado con * xito. * * @return Mensaje internacionalizado. */ protected String getMessageEditSuccess() { return getMessage(EDIT_SUCCESS); } /*** * Retorna el mensaje a ser visualizado cuando un registro no es editado con * xito. * * @return Mensaje internacionalizado. */ protected String getMessageEditFailure() { return getMessage(EDIT_FAILURE); } /*** * Retorna el mensaje a ser visualizado cuando un registro es eliminado con * xito. * * @return Mensaje internacionalizado. */ protected String getMessageDeleteSuccess() { return getMessage(DELETE_SUCCESS); } /*** * Retorna el mensaje a ser visualizado cuando un registro no es eliminado * con xito. * * @return Mensaje internacionalizado. */ protected String getMessageDeleteFailure() { return getMessage(DELETE_FAILURE); } /** * Mtodo que se encarga de invocar a la lgica y de actualizar el bean * actual. * <p> * Es un mtodo de conveniencia para reescribir solamente las partes * relevantes a la actualizacin de la entidad y omitir detalles como la * generacin de mensajes y manejo de excepciones. * </p> * <p> * Cualquier excepcin lanzada aqu ser manejada en * {@link #handleException(Exception)}. * </p> * <p> * Si el caso de uso dicta que se deben modificar mas de una entidad, se * recomienda que en este mtodo se llame a la lgica y que sea ella la * encargada de realizar la operacin, retornando el elemento relevante para * mostrar al usuario. Se recomienda el uso de la lgica pues aqu no se * pueden realizar transacciones. * </p> * * @param entity * entidad a actualizar * @return T entidad actualizada. */ protected T edit(final T entity) { log.info("Edit llamado"); return getBaseLogic().update(entity); } /** * {@inheritDoc} * <p> * Crea el actual bean, este mtodo no lanza ninguna excepcin, si desea * capturar la excepcin que lanza la creacin utilize la funcin * {@link #handleException(Exception)}, si desea modificar el comportamiento * al crear un objeto, utilice la funcin {@link #create(Object)}. * </p> * * @see #create(Object) * @see #handleException(Exception) */ @Override @HasRole(KarakuSecurity.DEFAULT_CREATE) public String doCreate() { try { setBean(create(getBean())); reloadEntities(); helper.createGlobalFacesMessageSimple(FacesMessage.SEVERITY_INFO, getMessageCreateSuccess()); return postCreate(); } catch (Exception e) { if (!handleException(helper.convertException(e, getClazz()))) { log.warn("doCreate failed", e); helper.createGlobalFacesMessage(FacesMessage.SEVERITY_WARN, getMessageCreateFailure(), e.getMessage()); } return ""; } } /** * {@inheritDoc} * <p> * Esta implementacin captura las excepciones del tipo * {@link UniqueConstraintException} y las excepciones del tipo * {@link DataIntegrityViolationException} y intenta generar un mensaje de * error pertinente. * </p> * */ @Override public boolean handleException(final Exception e) { if (e instanceof UniqueConstraintException) { UniqueConstraintException unique = (UniqueConstraintException) e; if (unique.getFields().size() == 1) { String field = unique.getFields().get(0); controllerHelper.addWarnMessage(field, getMessage("FIELD_DUPLICATE"), getMessage("FIELD_DUPLICATE_DETAIL")); } if (unique.getFields().size() > 1) { controllerHelper.addGlobalWarnMessage(getMessage("FIELDS_DUPLICATED"), getMessage("FIELDS_DUPLICATED_DETAIL", unique.getFields().toArray())); } return true; } else if (e instanceof DataIntegrityViolationException) { // TODO: buscar una solucion mas generica para este caso, de // identificar excepciones causadas por constraints de foreign keys DataIntegrityViolationException integrityException = (DataIntegrityViolationException) e; /* controlar que su causa sea del tipo constraintException */ if (integrityException.getCause() instanceof ConstraintViolationException) { /* controlar que el constraint sea de fk */ ConstraintViolationException constraintException = (ConstraintViolationException) integrityException .getCause(); if (constraintException.getConstraintName().startsWith("fk")) { controllerHelper.createGlobalFacesMessage(FacesMessage.SEVERITY_WARN, "ENTITY_REQUIRED_DETAIL"); return true; } } } return false; } /** * Mtodo que se encarga de invocar a la lgica y de persistir el bean * actual. * <p> * Es un mtodo de conveniencia para reescribir solamente las partes * relevantes a la creacin de la entidad y omitir detalles como la * generacin de mensajes y manejo de excepciones. * </p> * <p> * Cualquier excepcin lanzada aqu ser manejada en * {@link #handleException(Exception)}. * </p> * <p> * Si el caso de uso dicta que se deben crear mas de una entidad, se * recomienda que en este mtodo se llame a la lgica y que sea ella la * encargada de guardar la lista, retornando el elemento relevante para * mostrar al usuario. Se recomienda el uso de la lgica pues aqu no se * pueden realizar transacciones. * </p> * * @param entity * entidad a crear * @return T entidad creada. */ protected T create(T entity) { log.info("Create llamado"); return getBaseLogic().add(entity); } /** * Agrega la llamada a la funcin js "equalColumnWidth()" dentro del * listener "ready" de JQuery. Desgraciadamente esta es la nica forma que * proporciona RichFaces para agregar mtodos al ready que crea l mismo. * * Este mtodo se debera llamar desde la vistas donde exista alguna lista * con extendedDataTable. */ public void columnFix() { JavaScriptService jsService = ServiceTracker.getService(JavaScriptService.class); JSObject js = new JSObject("name") { @Override public void appendScript(Appendable target) throws IOException { CharSequence cs = JSFUNCTION; target.append(cs); } @Override public void appendScriptToStringBuilder(StringBuilder stringBuilder) { stringBuilder.append(JSFUNCTION); } }; jsService.addPageReadyScript(FacesContext.getCurrentInstance(), js); } }