From seam-commits at lists.jboss.org Wed Jun 16 16:00:41 2010 Content-Type: multipart/mixed; boundary="===============2086257053680347583==" MIME-Version: 1.0 From: seam-commits at lists.jboss.org To: seam-commits at lists.jboss.org Subject: [seam-commits] Seam SVN: r13193 - in examples/trunk/booking-simplified/src/main: java/org/jboss/seam/examples/booking/account and 3 other directories. Date: Wed, 16 Jun 2010 16:00:41 -0400 Message-ID: <201006162000.o5GK0fx8012999@svn01.web.mwc.hst.phx2.redhat.com> --===============2086257053680347583== Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Author: lincolnthree Date: 2010-06-16 16:00:40 -0400 (Wed, 16 Jun 2010) New Revision: 13193 Added: examples/trunk/booking-simplified/src/main/java/org/jboss/seam/examples/= booking/account/PasswordConfirmValidator.java examples/trunk/booking-simplified/src/main/java/org/jboss/seam/faces/ examples/trunk/booking-simplified/src/main/java/org/jboss/seam/faces/com= ponent/ examples/trunk/booking-simplified/src/main/java/org/jboss/seam/faces/com= ponent/UIInputContainer.java Modified: examples/trunk/booking-simplified/src/main/java/org/jboss/seam/examples/= booking/account/PasswordManagerBean.java examples/trunk/booking-simplified/src/main/webapp/password.xhtml Log: XVal for passwords Added: examples/trunk/booking-simplified/src/main/java/org/jboss/seam/examp= les/booking/account/PasswordConfirmValidator.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- examples/trunk/booking-simplified/src/main/java/org/jboss/seam/examples= /booking/account/PasswordConfirmValidator.java (rev= 0) +++ examples/trunk/booking-simplified/src/main/java/org/jboss/seam/examples= /booking/account/PasswordConfirmValidator.java 2010-06-16 20:00:40 UTC (rev= 13193) @@ -0,0 +1,71 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * 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.jboss.seam.examples.booking.account; + +import javax.faces.application.FacesMessage; +import javax.faces.component.UIComponent; +import javax.faces.context.FacesContext; +import javax.faces.validator.FacesValidator; +import javax.faces.validator.Validator; +import javax.faces.validator.ValidatorException; +import javax.inject.Inject; + +import org.jboss.seam.examples.booking.model.User; +import org.jboss.seam.faces.validation.InputField; + +/** + * @author Lincoln Baxter, II= I + * = + */ +(a)FacesValidator(value =3D "passwordConfirmValidator") +public class PasswordConfirmValidator implements Validator +{ + @Inject + @Authenticated + User currentUser; + + @Inject + @InputField + private String oldPassword; + + @Inject + @InputField + private String newPassword; + + @Inject + @InputField + private String confirmNewPassword; + + public void validate(final FacesContext context, final UIComponent comp= , final Object components) throws ValidatorException + { + if ((currentUser.getPassword() !=3D null) && currentUser.getPassword= ().equals(oldPassword)) + { + throw new ValidatorException(new FacesMessage("Your original pass= word was incorrect.")); + } + + if ((newPassword !=3D null) && !newPassword.equals(confirmNewPasswor= d)) + { + throw new ValidatorException(new FacesMessage("New passwords did = not match.")); + } + } + +} Modified: examples/trunk/booking-simplified/src/main/java/org/jboss/seam/ex= amples/booking/account/PasswordManagerBean.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- examples/trunk/booking-simplified/src/main/java/org/jboss/seam/examples= /booking/account/PasswordManagerBean.java 2010-06-16 19:32:21 UTC (rev 1319= 2) +++ examples/trunk/booking-simplified/src/main/java/org/jboss/seam/examples= /booking/account/PasswordManagerBean.java 2010-06-16 20:00:40 UTC (rev 1319= 3) @@ -39,19 +39,11 @@ = public void changePassword() { - if (user.getPassword().equals(confirmPassword)) - { - em.merge(user); - messages.info(new BundleKey("messages.properties", "account.passw= ordChanged")).textDefault("Password successfully updated."); - changed =3D true; - } - else - { - // FIXME reverting isn't going to work here - // revertUser(); - confirmPassword =3D null; - messages.error(new BundleKey("messages.properties", "account.pass= wordsDoNotMatch")).textDefault("Passwords do not match. Please re-type the = new password."); - } + em.merge(user); + messages.info(new BundleKey("messages.properties", "account.password= Changed")).textDefault("Password successfully updated."); + changed =3D true; + // messages.error(new BundleKey("messages.properties", + // "account.passwordsDoNotMatch")).textDefault("Passwords do not mat= ch. Please re-type the new password."); } = public boolean isChanged() Added: examples/trunk/booking-simplified/src/main/java/org/jboss/seam/faces= /component/UIInputContainer.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- examples/trunk/booking-simplified/src/main/java/org/jboss/seam/faces/co= mponent/UIInputContainer.java (rev 0) +++ examples/trunk/booking-simplified/src/main/java/org/jboss/seam/faces/co= mponent/UIInputContainer.java 2010-06-16 20:00:40 UTC (rev 13193) @@ -0,0 +1,548 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * 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.jboss.seam.faces.component; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.el.ValueReference; +import javax.faces.FacesException; +import javax.faces.application.FacesMessage; +import javax.faces.component.EditableValueHolder; +import javax.faces.component.FacesComponent; +import javax.faces.component.NamingContainer; +import javax.faces.component.UIComponent; +import javax.faces.component.UIComponentBase; +import javax.faces.component.UIMessage; +import javax.faces.component.UINamingContainer; +import javax.faces.component.UIViewRoot; +import javax.faces.component.html.HtmlOutputLabel; +import javax.faces.context.FacesContext; +import javax.faces.validator.BeanValidator; +import javax.validation.Validation; +import javax.validation.ValidationException; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import javax.validation.metadata.PropertyDescriptor; + +/** + * UIInputContainer is a supplemental component for a JSF= 2.0 + * composite component encapsulating one or more input components + * (EditableValueHolder), their corresponding message + * components (UIMessage) and a label + * (HtmlOutputLabel). This component takes care of wiring= the + * label to the first input and the messages to each input in sequence. It= also + * assigns two implicit attribute values, "required" and "invalid" to indi= cate + * that a required input field is present and whether there are any valida= tion + * errors, respectively. To determine if a input field is required, both t= he + * required attribute is consulted and whether the property has Bean Valid= ation + * constraints. Finally, if the "label" attribute is not provided on the + * composite component, the label value will be derived from the id of the + * composite component, for convenience. + * = + *

+ * Composite component definition example (minus layout): + *

+ * = + *
+ * <cc:interface componentType=3D"org.jboss.seam.faces.InputContainer"/>
+ * <cc:implementation>
+ *   <h:outputLabel id=3D"label" value=3D"#{cc.attrs.label}:" styleClas=
s=3D"#{cc.attrs.invalid ? 'invalid' : ''}">
+ *     <h:ouputText styleClass=3D"required" rendered=3D"#{cc.attrs.requ=
ired}" value=3D"*"/>
+ *   </h:outputLabel>
+ *   <cc:insertChildren/>
+ *   <h:message id=3D"message" errorClass=3D"invalid message" rendered=
=3D"#{cc.attrs.invalid}"/>
+ * </cc:implementation>
+ * 
+ * = + *

+ * Composite component usage example: + *

+ * = + *
+ * <example:inputContainer id=3D"name">
+ *   <h:inputText id=3D"input" value=3D"#{person.name}"/>
+ * </example:inputContainer>
+ * 
+ * = + *

+ * Possible enhancements: + *

+ * + * = + *

+ * NOTE: Firefox does not properly associate a label with the target input= if + * the input id contains a colon (:), the default separator character in J= SF. + * JSF 2 allows developers to set the value via an initialization parameter + * (context-param in web.xml) keyed to javax.faces.SEPARATOR_CHAR. We reco= mmend + * that you override this setting to make the separator an underscore (_). + *

+ * = + * @author Dan Allen + */ +(a)FacesComponent(UIInputContainer.COMPONENT_TYPE) +public class UIInputContainer extends UIComponentBase implements NamingCon= tainer +{ + /** + * The standard component type for this component. + */ + public static final String COMPONENT_TYPE =3D "org.jboss.seam.faces.Inp= utContainer"; + + protected static final String HTML_ID_ATTR_NAME =3D "id"; + protected static final String HTML_CLASS_ATTR_NAME =3D "class"; + protected static final String HTML_STYLE_ATTR_NAME =3D "style"; + + private boolean beanValidationPresent =3D false; + + public UIInputContainer() + { + beanValidationPresent =3D isClassPresent("javax.validation.Validator= "); + } + + @Override + public String getFamily() + { + return UINamingContainer.COMPONENT_FAMILY; + } + + /** + * The name of the auto-generated composite component attribute that ho= lds a + * boolean indicating whether the the template contains an invalid inpu= t. + */ + public String getInvalidAttributeName() + { + return "invalid"; + } + + /** + * The name of the auto-generated composite component attribute that ho= lds a + * boolean indicating whether the template contains a required input. + */ + public String getRequiredAttributeName() + { + return "required"; + } + + /** + * The name of the composite component attribute that holds the string = label + * for this set of inputs. If the label attribute is not provided, one = will + * be generated from the id of the composite component or, if the id is + * defaulted, the name of the property bound to the first input. + */ + public String getLabelAttributeName() + { + return "label"; + } + + /** + * The name of the auto-generated composite component attribute that ho= lds + * the elements in this input container. The elements include the label= , a + * list of inputs and a cooresponding list of messages. + */ + public String getElementsAttributeName() + { + return "elements"; + } + + /** + * The name of the composite component attribute that holds a boolean + * indicating whether the component template should be enclosed in an H= TML + * element, so that it be referenced from JavaScript. + */ + public String getEncloseAttributeName() + { + return "enclose"; + } + + public String getContainerElementName() + { + return "div"; + } + + public String getDefaultLabelId() + { + return "label"; + } + + public String getDefaultInputId() + { + return "input"; + } + + public String getDefaultMessageId() + { + return "message"; + } + + @Override + public void encodeBegin(final FacesContext context) throws IOException + { + if (!isRendered()) + { + return; + } + + super.encodeBegin(context); + + InputContainerElements elements =3D scan(getFacet(UIComponent.COMPOS= ITE_FACET_NAME), null, context); + // assignIds(elements, context); + wire(elements, context); + + getAttributes().put(getElementsAttributeName(), elements); + + if (elements.hasValidationError()) + { + getAttributes().put(getInvalidAttributeName(), true); + } + + // set the required attribute, but only if the user didn't already a= ssign + // it + if (!getAttributes().containsKey(getRequiredAttributeName()) && elem= ents.hasRequiredInput()) + { + getAttributes().put(getRequiredAttributeName(), true); + } + + if (!getAttributes().containsKey(getLabelAttributeName())) + { + getAttributes().put(getLabelAttributeName(), generateLabel(elemen= ts, context)); + } + + if (Boolean.TRUE.equals(getAttributes().get(getEncloseAttributeName(= )))) + { + startContainerElement(context); + } + } + + @Override + public void encodeEnd(final FacesContext context) throws IOException + { + if (!isRendered()) + { + return; + } + + super.encodeEnd(context); + + if (Boolean.TRUE.equals(getAttributes().get(getEncloseAttributeName(= )))) + { + endContainerElement(context); + } + } + + protected void startContainerElement(final FacesContext context) throws= IOException + { + context.getResponseWriter().startElement(getContainerElementName(), = this); + String style =3D (getAttributes().get("style") !=3D null ? getAttrib= utes().get("style").toString().trim() : null); + if (style.length() > 0) + { + context.getResponseWriter().writeAttribute(HTML_STYLE_ATTR_NAME, = style, HTML_STYLE_ATTR_NAME); + } + String styleClass =3D (getAttributes().get("styleClass") !=3D null ?= getAttributes().get("styleClass").toString().trim() : null); + if (styleClass.length() > 0) + { + context.getResponseWriter().writeAttribute(HTML_CLASS_ATTR_NAME, = styleClass, HTML_CLASS_ATTR_NAME); + } + context.getResponseWriter().writeAttribute(HTML_ID_ATTR_NAME, getCli= entId(context), HTML_ID_ATTR_NAME); + } + + protected void endContainerElement(final FacesContext context) throws I= OException + { + context.getResponseWriter().endElement(getContainerElementName()); + } + + protected String generateLabel(final InputContainerElements elements, f= inal FacesContext context) + { + String name =3D getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX) ? el= ements.getPropertyName(context) : getId(); + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + + /** + * Walk the component tree branch built by the composite component and = locate + * the input container elements. + * = + * @return a composite object of the input container elements + */ + protected InputContainerElements scan(final UIComponent component, Inpu= tContainerElements elements, final FacesContext context) + { + if (elements =3D=3D null) + { + elements =3D new InputContainerElements(); + } + + // NOTE we need to walk the tree ignoring rendered attribute because= it's + // condition + // could be based on what we discover + if ((elements.getLabel() =3D=3D null) && (component instanceof HtmlO= utputLabel)) + { + elements.setLabel((HtmlOutputLabel) component); + } + else if (component instanceof EditableValueHolder) + { + elements.registerInput((EditableValueHolder) component, getDefaul= tValidator(context), context); + } + else if (component instanceof UIMessage) + { + elements.registerMessage((UIMessage) component); + } + // may need to walk smarter to ensure "element of least suprise" + for (UIComponent child : component.getChildren()) + { + scan(child, elements, context); + } + + return elements; + } + + // assigning ids seems to break form submissions, but I don't know why + public void assignIds(final InputContainerElements elements, final Face= sContext context) + { + boolean refreshIds =3D false; + if (getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX)) + { + setId(elements.getPropertyName(context)); + refreshIds =3D true; + } + UIComponent label =3D elements.getLabel(); + if (label !=3D null) + { + if (label.getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX)) + { + label.setId(getDefaultLabelId()); + } + else if (refreshIds) + { + label.setId(label.getId()); + } + } + for (int i =3D 0, len =3D elements.getInputs().size(); i < len; i++) + { + UIComponent input =3D (UIComponent) elements.getInputs().get(i); + if (input.getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX)) + { + input.setId(getDefaultInputId() + (i =3D=3D 0 ? "" : (i + 1))); + } + else if (refreshIds) + { + input.setId(input.getId()); + } + } + for (int i =3D 0, len =3D elements.getMessages().size(); i < len; i+= +) + { + UIComponent msg =3D elements.getMessages().get(i); + if (msg.getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX)) + { + msg.setId(getDefaultMessageId() + (i =3D=3D 0 ? "" : (i + 1))); + } + else if (refreshIds) + { + msg.setId(msg.getId()); + } + } + } + + /** + * Wire the label and messages to the input(s) + */ + protected void wire(final InputContainerElements elements, final FacesC= ontext context) + { + elements.wire(context); + } + + /** + * Get the default Bean Validation Validator to read the contraints for= a + * property. + */ + private Validator getDefaultValidator(final FacesContext context) throw= s FacesException + { + if (!beanValidationPresent) + { + return null; + } + + ValidatorFactory validatorFactory; + Object cachedObject =3D context.getExternalContext().getApplicationM= ap().get(BeanValidator.VALIDATOR_FACTORY_KEY); + if (cachedObject instanceof ValidatorFactory) + { + validatorFactory =3D (ValidatorFactory) cachedObject; + } + else + { + try + { + validatorFactory =3D Validation.buildDefaultValidatorFactory(); + } + catch (ValidationException e) + { + throw new FacesException("Could not build a default Bean Valid= ator factory", e); + } + context.getExternalContext().getApplicationMap().put(BeanValidato= r.VALIDATOR_FACTORY_KEY, validatorFactory); + } + return validatorFactory.getValidator(); + } + + private boolean isClassPresent(final String fqcn) + { + try + { + if (Thread.currentThread().getContextClassLoader() !=3D null) + { + return Thread.currentThread().getContextClassLoader().loadClas= s(fqcn) !=3D null; + } + else + { + return Class.forName(fqcn) !=3D null; + } + } + catch (ClassNotFoundException e) + { + return false; + } + catch (NoClassDefFoundError e) + { + return false; + } + } + + public static class InputContainerElements + { + private String propertyName; + private HtmlOutputLabel label; + private final List inputs =3D new ArrayList(); + private final List messages =3D new ArrayList(= ); + private boolean validationError =3D false; + private boolean requiredInput =3D false; + + public HtmlOutputLabel getLabel() + { + return label; + } + + public void setLabel(final HtmlOutputLabel label) + { + this.label =3D label; + } + + public List getInputs() + { + return inputs; + } + + public void registerInput(final EditableValueHolder input, final Val= idator validator, final FacesContext context) + { + inputs.add(input); + if (input.isRequired() || isRequiredByConstraint(input, validator= , context)) + { + requiredInput =3D true; + } + if (!input.isValid()) + { + validationError =3D true; + } + // optimization to avoid loop if already flagged + else if (!validationError) + { + Iterator it =3D context.getMessages(((UIComponen= t) input).getClientId(context)); + while (it.hasNext()) + { + if (it.next().getSeverity().compareTo(FacesMessage.SEVERITY= _WARN) >=3D 0) + { + validationError =3D true; + break; + } + } + } + } + + public List getMessages() + { + return messages; + } + + public void registerMessage(final UIMessage message) + { + messages.add(message); + } + + public boolean hasValidationError() + { + return validationError; + } + + public boolean hasRequiredInput() + { + return requiredInput; + } + + private boolean isRequiredByConstraint(final EditableValueHolder inp= ut, final Validator validator, final FacesContext context) + { + if (validator =3D=3D null) + { + return false; + } + + // NOTE believe it or not, getValueReference on ValueExpression is + // broken, so we have to do it ourselves + ValueReference vref =3D new ValueExpressionAnalyzer(((UIComponent= ) input).getValueExpression("value")).getValueReference(context.getELContex= t()); + PropertyDescriptor d =3D validator.getConstraintsForClass(vref.ge= tBase().getClass()).getConstraintsForProperty((String) vref.getProperty()); + return (d !=3D null) && d.hasConstraints(); + } + + public String getPropertyName(final FacesContext context) + { + if (propertyName !=3D null) + { + return propertyName; + } + + if (inputs.size() =3D=3D 0) + { + return null; + } + + propertyName =3D (String) new ValueExpressionAnalyzer(((UICompone= nt) inputs.get(0)).getValueExpression("value")).getValueReference(context.g= etELContext()).getProperty(); + return propertyName; + } + + public void wire(final FacesContext context) + { + int numInputs =3D inputs.size(); + if (numInputs > 0) + { + if (label !=3D null) + { + label.setFor(((UIComponent) inputs.get(0)).getClientId(cont= ext)); + } + for (int i =3D 0, len =3D messages.size(); i < len; i++) + { + if (i < numInputs) + { + messages.get(i).setFor(((UIComponent) inputs.get(i)).get= ClientId(context)); + } + } + } + } + } +} Modified: examples/trunk/booking-simplified/src/main/webapp/password.xhtml =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- examples/trunk/booking-simplified/src/main/webapp/password.xhtml 2010-0= 6-16 19:32:21 UTC (rev 13192) +++ examples/trunk/booking-simplified/src/main/webapp/password.xhtml 2010-0= 6-16 20:00:40 UTC (rev 13193) @@ -4,6 +4,7 @@ xmlns:ui=3D"http://java.sun.com/jsf/facelets" xmlns:f=3D"http://java.sun.com/jsf/core" xmlns:h=3D"http://java.sun.com/jsf/html" + xmlns:s=3D"http://jboss.org/seam/faces" xmlns:p=3D"http://http://java.sun.com/jsf/composite/components/property" template=3D"/WEB-INF/layout/template.xhtml"> = @@ -23,13 +24,16 @@ =
= - - + + + = + + + = - - + + =
@@ -40,6 +44,8 @@ =
= + = --===============2086257053680347583==--