/*
* JBoss, a division of Red Hat
* Copyright 2011, Red Hat Middleware, LLC, and individual
* contributors as indicated by the @authors tag. See the
* copyright.txt in the distribution for a full listing of
* individual contributors.
*
* This 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 software 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 software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.gatein.wsrp.admin.ui;
import org.gatein.common.util.ParameterValidation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
/**
* Provides base behavior for WSRP admin UI beans.
*
* @author <a href="mailto:chris.laprun@jboss.com">Chris Laprun</a>
* @version $Revision: 13413 $
* @since 2.6
*/
public abstract class WSRPManagedBean implements Serializable
{
protected transient Logger log = LoggerFactory.getLogger(getClass());
/** The BeanContext associated with this bean */
protected BeanContext beanContext;
/** The name of the action associated with a cancel operation for this bean, i.e. which view should we redirect to if the user cancels the current view. */
private String cancelOutcome;
public static final String INVALID_NAME = "INVALID_NAME_ERROR";
public static final String INVALID_PATH = "INVALID_PATH_ERROR";
public static final String DUPLICATE = "DUPLICATE_ERROR";
static void bypassAndRedisplay()
{
// bypass the rest of the life cycle and re-display page
FacesContext.getCurrentInstance().renderResponse();
}
/** Provides an API to validate input properties. */
public interface PropertyValidator extends Serializable
{
/**
* Determines whether this PropertyValidator checks for duplicated values.
*
* @return <code>true</code> if this PropertyValidator checks for duplicated values, <code>false</code> otherwise
*/
boolean checkForDuplicates();
/**
* Retrieves the name of the type of objects this PropertyValidator can validate.
*
* @return
*/
String getTypeOfValidatedValues();
/**
* Determines whether the specified property value already exists.
*
* @param value
* @return
*/
boolean isAlreadyExisting(String value);
/**
* Performs a quick check that the specified value doesn't contain any invalid characters to be able to fail fast before performing a full regex validation.
*
* @param value the value we want to check
* @return the validated value or <code>null</code> if it doesn't conform to the expected format
*/
String checkForInvalidCharacters(String value);
/**
* Retrieves the {@link org.gatein.common.util.ParameterValidation.ValidationErrorHandler} associated with this PropertyValidator. Allows implementations to vary how error
* messages are presented to client code, depending on the context they run in.
*
* @param name the name of the property being validated
* @param targetForErrorMessage the UI component target for the potential error message
* @return
*/
ParameterValidation.ValidationErrorHandler getValidationErrorHandler(String name, String targetForErrorMessage);
/**
* Retrieves the regular expression this PropertyValidator uses to validate values.
*
* @return
*/
Pattern getValidationPattern();
/**
* Retrieves the key associated to the error message in the localization resource bundle.
*
* @return
*/
String getErrorKey();
}
private PropertyValidator validator = new DefaultPropertyValidator();
protected void setValidator(PropertyValidator validator)
{
this.validator = validator;
}
protected PropertyValidator getValidator()
{
return validator;
}
public void setBeanContext(BeanContext beanContext)
{
this.beanContext = beanContext;
}
public void setCancelOutcome(String cancelOutcome)
{
this.cancelOutcome = cancelOutcome;
}
public String checkAndReturnValueIfValid(String value, String targetForErrorMessage)
{
return checkAndReturnValueIfValid(value, targetForErrorMessage, validator);
}
/**
* Asks the specified PropertyValidator to check the validity of the specified value, using the specified target for error messages
*
* @param value the value to be validated
* @param targetForErrorMessage the name of the UI component being targeted when an error message needs to be displayed
* @param validator the PropertyValidator that will validate the values
* @return the validated value or <code>null</code> otherwise
*/
public String checkAndReturnValueIfValid(String value, String targetForErrorMessage, PropertyValidator validator)
{
ParameterValidation.throwIllegalArgExceptionIfNull(validator, "PropertyValidator");
final String objectTypeName = validator.getTypeOfValidatedValues();
if (ParameterValidation.isNullOrEmpty(value))
{
beanContext.createTargetedErrorMessage(targetForErrorMessage, validator.getErrorKey(), value, getLocalizedType(objectTypeName));
return null;
}
else
{
String original = value;
value = validator.checkForInvalidCharacters(value);
// we got an invalid value after quickly checking for invalid characters, fail!
if (value == null)
{
beanContext.createTargetedErrorMessage(targetForErrorMessage, validator.getErrorKey(), original, getLocalizedType(objectTypeName));
return null;
}
// Trim value
value = value.trim();
// "sanitize" value: if it's invalid, return null and output message
value = ParameterValidation.sanitizeFromPatternWithHandler(value, validator.getValidationPattern(), validator.getValidationErrorHandler(value, targetForErrorMessage));
// we got an invalid value, fail!
if (value == null)
{
return null;
}
// Check for duplicate
if (validator.checkForDuplicates() && validator.isAlreadyExisting(value))
{
getDuplicateErrorMessage(value, targetForErrorMessage, objectTypeName);
return null;
}
return value;
}
}
/**
* Retrieves the localized error message to display when the specified value is a duplicate of an already existing one.
*
* @param value
* @param targetForErrorMessage
* @param objectTypeName
*/
protected void getDuplicateErrorMessage(String value, String targetForErrorMessage, String objectTypeName)
{
beanContext.createTargetedErrorMessage(targetForErrorMessage, DUPLICATE, value, getLocalizedType(objectTypeName));
}
/**
* Retrieves the localized description for the specified object type name (type of validated values).
*
* @param objectTypeName
* @return
*/
private String getLocalizedType(String objectTypeName)
{
return beanContext.getMessageFromBundle(objectTypeName);
}
/**
* Retrieves the name of the type of objects this bean deals with.
*
* @return
*/
protected abstract String getObjectTypeName();
/**
* Checks whether the specified value (understood as an identifier for entities this bean manages) is already known to this bean and therefore would consist of a duplicated
* value.
*
* @param objectName
* @return
*/
public abstract boolean isAlreadyExisting(String objectName);
/**
* Determines whether the specified old and new values are different after being normalized.
* @param oldValue
* @param newValue
* @return
* @todo public for test cases
*/
public boolean isOldAndNewDifferent(Object oldValue, Object newValue)
{
oldValue = normalizeStringIfNeeded(oldValue);
newValue = normalizeStringIfNeeded(newValue);
return (oldValue != null && !oldValue.equals(newValue)) || (oldValue == null && newValue != null);
}
/**
* Normalizes String by considering empty String as null as JSF would give either and trim non-null Strings.
*
* @param value
* @return
* @todo public for test cases
*/
public Object normalizeStringIfNeeded(Object value)
{
if (value == null)
{
return null;
}
else
{
if (value instanceof String)
{
String stringValue = (String)value;
return stringValue.length() == 0 ? null : stringValue.trim();
}
else
{
return value;
}
}
}
protected class MessageValidationHandler extends ParameterValidation.ValidationErrorHandler
{
private String targetForErrorMessage;
private String validatedName;
private String objectTypeName;
private String errorMessageKey;
public MessageValidationHandler(String defaultValue, String targetForErrorMessage, String validatedName, String objectTypeName)
{
this(defaultValue, targetForErrorMessage, validatedName, objectTypeName, INVALID_NAME);
}
public MessageValidationHandler(String defaultValue, String targetForErrorMessage, String validatedName, String objectTypeName, String errorMessageKey)
{
super(defaultValue);
this.targetForErrorMessage = targetForErrorMessage;
this.validatedName = validatedName;
this.objectTypeName = objectTypeName;
this.errorMessageKey = errorMessageKey;
}
protected String internalValidationErrorHandling(String s)
{
beanContext.createTargetedErrorMessage(targetForErrorMessage, errorMessageKey, validatedName, getLocalizedType(objectTypeName));
return null;
}
}
protected class DefaultPropertyValidator implements PropertyValidator
{
public boolean checkForDuplicates()
{
return true;
}
public String getTypeOfValidatedValues()
{
// delegate to the enclosing bean
return WSRPManagedBean.this.getObjectTypeName();
}
public boolean isAlreadyExisting(String value)
{
// delegate to the enclosing bean
return WSRPManagedBean.this.isAlreadyExisting(value);
}
/**
* Checks whether the specified value contains '.' or '/'.
*
* @param value the value which format we want to check
* @return
*/
public String checkForInvalidCharacters(String value)
{
// if name contains . or /, it's invalid for a Portal object
return (value.indexOf('.') != -1 || value.indexOf('/') != -1) ? null : value;
}
public ParameterValidation.ValidationErrorHandler getValidationErrorHandler(String name, String targetForErrorMessage)
{
return new MessageValidationHandler(null, targetForErrorMessage, name, getTypeOfValidatedValues());
}
public Pattern getValidationPattern()
{
return ParameterValidation.XSS_CHECK;
}
@Override
public String getErrorKey()
{
return INVALID_NAME;
}
}
/**
* Default action: returns to outcome specified by {@link #setCancelOutcome(String)}
*
* @return
*/
public String cancel()
{
return cancelOutcome;
}
protected static List<SelectItem> getSelectItemsFrom(List<String> identifiers)
{
List<SelectItem> result = new ArrayList<SelectItem>(identifiers.size());
for (String pageIdentifier : identifiers)
{
result.add(new SelectItem(pageIdentifier));
}
return result;
}
}