Showing posts with label jsf-2. Show all posts
Showing posts with label jsf-2. Show all posts

Monday, May 29, 2017

JSF 2.0 Required Field Label Decorator for properties with @NotNull Constraint

Leave a Comment

I have a JSF 2.0 application which also uses Primefaces 3.3. Currently there is a nice feature where a label is decorated with an asterisk if the related <p:inputText> uses a required="true" attribute.

This field is bound to a bean property which is annotated with @NotNull validation constraint. It seems redundant and error prone to have to also add required="true" in the XHTML when the bean property is already annotated with @NotNull.

Is there a hook or some way to automatically decorate labels for components that are bound to properties with @NotNull?

Any thoughts or suggestions are greatly appreciated.

2 Answers

Answers 1

Note: This is a hack. It may have performance implications due to it's use of introspection

  1. At a basic level, what you need to know if the field is annotated with @NotNull. Perform this check in a sensible place like @PostConstruct for a view scoped bean. Declare a global variable to determine the required attribute

    boolean  requiredAttribute;          @PostConstruct public void init{  Field theField = this.getClass().getField("theField"); NotNull theAnnotation = theField.getAnnotation(NotNull.class); if(theAnnotation != null){    requiredAttribute = true;    } } 
  2. Bind the required attribute to the variable in the backing bean

    <p:inputText id="inputit" required="#{myBean.requiredAttribute}"/> 

Answers 2

This solution is based on PF 6.0, I don't remember if BeanValidationMetadataExtractor is available in previous versions. Anyway, creating a DIY extractor is a simple task.

I had a similar problem. In my specific case:

  • User should be informed that a certain field (read UIInput) is required
  • I don't want to repeat required="true" on the comp since it's already bound to a @NotNull/@NotBlank property/field
  • In my case, a label component may not be present (and I don't like asterisked-labels)

So, here is what I have done:

import java.util.Set; import javax.el.ValueExpression; import javax.faces.component.UIInput; import javax.faces.context.FacesContext; import javax.faces.event.AbortProcessingException; import javax.faces.event.PreRenderComponentEvent; import javax.faces.event.SystemEvent; import javax.faces.event.SystemEventListener; import javax.validation.constraints.NotNull; import javax.validation.metadata.ConstraintDescriptor; import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.constraints.NotEmpty; import org.omnifaces.util.Faces; import org.primefaces.context.RequestContext; import org.primefaces.metadata.BeanValidationMetadataExtractor; import one.util.streamex.StreamEx;   public class InputValidatorConstraintListener implements SystemEventListener {     @Override     public boolean isListenerForSource(Object source)     {         return source instanceof UIInput;     }      @Override     public void processEvent(SystemEvent event) throws AbortProcessingException     {         if(event instanceof PreRenderComponentEvent)         {             UIInput component = (UIInput) event.getSource();              boolean required = component.isRequired();             if(!required)             {                 FacesContext context = Faces.getContext();                 ValueExpression valueExpression = component.getValueExpression("value");                 RequestContext requestContext = RequestContext.getCurrentInstance();                  Set<ConstraintDescriptor<?>> constraints = null;                 try                 {                     constraints = BeanValidationMetadataExtractor.extractAllConstraintDescriptors(context, requestContext, valueExpression);                 }                 catch(Exception e)                 {                     // ignore                 }                  if(constraints != null && !constraints.isEmpty())                 {                     required = StreamEx.of(constraints)                         .map(ConstraintDescriptor::getAnnotation)                         .anyMatch(x -> x instanceof NotNull || x instanceof NotBlank || x instanceof NotEmpty);                 }             }              component.getPassThroughAttributes().put("data-required", required);              return;         }     } } 

and declare it in faces-config.xml:

<?xml version="1.0" encoding="utf-8"?> <faces-config version="2.2" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">      <application>         <system-event-listener>             <system-event-listener-class>it.shape.core.jsf.listener.InputValidatorConstraintListener</system-event-listener-class>             <system-event-class>javax.faces.event.PreRenderComponentEvent</system-event-class>         </system-event-listener>     </application>  </faces-config> 

With this listener UIInputs are rendered with a data-required passthrough attribute:

<input      id="form:editPanelMain:j_idt253"      name="form:editPanelMain:j_idt253"      type="text"      value="Rack Assemply"      size="80"      data-required="true"    <============================ NOTE THIS!!     data-widget="widget_form_editPanelMain_j_idt253"      class="ui-inputfield ui-inputtext ui-widget ui-state-default ui-corner-all"      role="textbox"      aria-disabled="false"      aria-readonly="false"> 

Now, I use a css rule to highlight these fields:

input[data-required='true'],  .ui-inputfield[data-required='true'],  *[data-required='true']  .ui-inputfield {     box-shadow: inset 0px 2px 2px #bf8f8f; } 

You can adapt this listener to either set the component as required or use another approach that suits your specific needs.

Another approach could be:

  • listen for UILabels instead of UIInputs
  • get the UIInput associated with the for/forValue ValueExpression of the label
  • check the UIInput for validation constraint
  • eventually invoke UIInput.setRequired(true)

The performance impact is negligible, as I have tested complex pages with ~3000 components.

Read More

Wednesday, May 17, 2017

Fileupload does not work in Primefaces

Leave a Comment

I have some issues with the <p:fileUpload> in Primefaces. At first I tried the snippets in the Documentation of Fileupload starting on page 2018. The first issue was that if I use the snippet, the upload function is not called:

public void upload() {     System.out.println("This is never shown");      if(file != null) {             System.out.println("This is never shown");     } } 

html:

<h:form  enctype="multipart/form-data">     <p:fileUpload value="#{fileUploadView.file}" mode="simple" />     <p:commandButton value="Submit" action="#{fileUploadView.upload}" ajax="false"/> </h:form> 

I have tried many other snippets and solutions. Advanced mode, with fileUploadListener, deleted the enctype in h:form, ....

If I choose mode="simple" and combine it with ajax="true" the function FileUploadView.upload() is called, but the file is always NULL.

If I delete the enctype from the <h:form>, the function FileUploadView.upload() is called, but the file is always NULL.

I mention the issues above, because this should not work either, because I read all possible solutions on this forum. I also added all suggested dependencies and context-params and filter, but it does not work.

Here I will post my pom.xml, web.xml and the full .java code: pom.xml

    <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">     <modelVersion>4.0.0</modelVersion>      <groupId>at.qe</groupId>     <artifactId>ASN_Application</artifactId>     <version>1.0.0</version>     <packaging>war</packaging>       <name>ASN_Application</name>     <description>     </description>      <repositories>         <repository>             <id>prime-repo</id>             <name>Prime Repo</name>             <url>http://repository.primefaces.org</url>         </repository>           <repository>             <id>spring-releases</id>             <url>https://repo.spring.io/libs-release</url>         </repository>      </repositories>     <pluginRepositories>         <pluginRepository>             <id>spring-releases</id>             <url>https://repo.spring.io/libs-release</url>         </pluginRepository>     </pluginRepositories>      <parent>         <groupId>org.springframework.boot</groupId>         <artifactId>spring-boot-starter-parent</artifactId>         <version>1.4.1.RELEASE</version>     </parent>      <properties>         <start-class>at.qe.sepm.asn_app.Main</start-class>         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>     </properties>      <dependencies>         <dependency>             <groupId>commons-io</groupId>             <artifactId>commons-io</artifactId>             <version>2.5</version>          </dependency>         <dependency>             <groupId>commons-fileupload</groupId>             <artifactId>commons-fileupload</artifactId>             <version>1.3.2</version>         </dependency>         <dependency>             <groupId>org.primefaces.themes</groupId>             <artifactId>all-themes</artifactId>             <version>1.0.10</version>         </dependency>          <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-data-jpa</artifactId>         </dependency>         <dependency>             <groupId>com.h2database</groupId>             <artifactId>h2</artifactId>             <scope>runtime</scope>         </dependency>          <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-web</artifactId>         </dependency>         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-actuator</artifactId>         </dependency>         <dependency>             <groupId>org.glassfish.web</groupId>             <artifactId>el-impl</artifactId>             <version>2.2</version>         </dependency>         <dependency>             <groupId>javax.enterprise</groupId>             <artifactId>cdi-api</artifactId>             <version>2.0-PFD2</version>         </dependency>         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-security</artifactId>         </dependency>         <dependency>             <groupId>com.sun.faces</groupId>             <artifactId>jsf-api</artifactId>             <version>2.2.14</version>             <scope>compile</scope>         </dependency>         <dependency>             <groupId>com.sun.faces</groupId>             <artifactId>jsf-impl</artifactId>             <version>2.2.13</version>             <scope>compile</scope>             <optional>true</optional>         </dependency>         <dependency>             <groupId>org.apache.tomcat.embed</groupId>             <artifactId>tomcat-embed-core</artifactId>             <scope>compile</scope>         </dependency>         <dependency>             <groupId>org.apache.tomcat.embed</groupId>             <artifactId>tomcat-embed-jasper</artifactId>             <scope>compile</scope>         </dependency>         <dependency>             <groupId>org.primefaces</groupId>             <artifactId>primefaces</artifactId>             <version>6.1</version>         </dependency>          <dependency>             <groupId>org.apache.poi</groupId>             <artifactId>poi</artifactId>             <version>3.16</version>         </dependency>         <dependency>             <groupId>com.lowagie</groupId>             <artifactId>itext</artifactId>             <version>2.1.7</version>         </dependency>          <!-- special test dependencies -->         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-test</artifactId>             <scope>test</scope>         </dependency>         <dependency>             <groupId>org.springframework.security</groupId>             <artifactId>spring-security-test</artifactId>             <scope>test</scope>         </dependency>         <dependency>             <groupId>nl.jqno.equalsverifier</groupId>             <artifactId>equalsverifier</artifactId>             <version>2.1.6</version>             <scope>test</scope>         </dependency>         <dependency>             <groupId>mysql</groupId>             <artifactId>mysql-connector-java</artifactId>             <version>6.0.6</version>         </dependency>      </dependencies>      <build>         <plugins>             <plugin>                 <artifactId>maven-compiler-plugin</artifactId>                 <configuration>                     <source>1.8</source>                     <target>1.8</target>                 </configuration>             </plugin>             <!-- Enable jacoco analysis -->             <plugin>                 <groupId>org.apache.maven.plugins</groupId>                 <artifactId>maven-surefire-plugin</artifactId>                 <configuration>                     <forkMode>once</forkMode>                     <argLine>                         ${coverageAgent}                     </argLine>                     <useSystemClassLoader>true</useSystemClassLoader>                 </configuration>             </plugin>             <plugin>                 <groupId>org.jacoco</groupId>                 <artifactId>jacoco-maven-plugin</artifactId>                 <version>0.7.6.201602180812</version>                 <configuration>                     <propertyName>coverageAgent</propertyName>                 </configuration>                 <executions>                     <execution>                         <goals>                             <goal>prepare-agent</goal>                         </goals>                     </execution>                     <execution>                         <id>report</id>                         <phase>prepare-package</phase>                         <goals>                             <goal>report</goal>                         </goals>                     </execution>                 </executions>             </plugin>         </plugins>     </build> </project> 

web.xml

<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"          xsi:schemaLocation="http://java.sun.com/xml/ns/javaee           http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"          version="2.5">      <servlet>         <servlet-name>Faces Servlet</servlet-name>         <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>         <load-on-startup>1</load-on-startup>     </servlet>      <error-page>         <exception-type>org.springframework.security.access.AccessDeniedException</exception-type>         <location>/error/access_denied.xhtml</location>     </error-page>       <context-param>         <param-name>javax.faces.STATE_SAVING_METHOD</param-name>         <param-value>server</param-value>     </context-param>     <context-param>         <param-name>javax.faces.FACELETS_SKIP_COMMENTS</param-name>         <param-value>true</param-value>     </context-param>  <context-param>     <param-name>primefaces.FONT_AWESOME</param-name>     <param-value>true</param-value> </context-param>  <context-param>     <param-name>primefaces.UPLOADER</param-name>     <param-value>commons</param-value>   </context-param>   <filter>     <filter-name>PrimeFaces FileUpload Filter</filter-name>     <filter-class>org.primefaces.webapp.filter.FileUploadFilter</filter-class> </filter> <filter-mapping>     <filter-name>PrimeFaces FileUpload Filter</filter-name>     <servlet-name>Faces Servlet</servlet-name> </filter-mapping>     <mime-mapping>     <extension>eot</extension>     <mime-type>application/vnd.ms-fontobject</mime-type> </mime-mapping> <mime-mapping>       <extension>otf</extension>       <mime-type>font/opentype</mime-type>   </mime-mapping>       <mime-mapping>       <extension>ttf</extension>       <mime-type>application/x-font-ttf</mime-type>   </mime-mapping>       <mime-mapping>       <extension>woff</extension>       <mime-type>application/x-font-woff</mime-type>   </mime-mapping> <mime-mapping>       <extension>svg</extension>       <mime-type>image/svg+xml</mime-type>   </mime-mapping>      <servlet-mapping>         <servlet-name>Faces Servlet</servlet-name>         <url-pattern>*.xhtml</url-pattern>     </servlet-mapping>      <welcome-file-list>         <welcome-file>login.xhtml</welcome-file>     </welcome-file-list> </web-app> 

FileUploadView.java

@Component @Scope("view")     public class FileUploadView{         @Autowired         private PictureService pictureService;         @Autowired         private UserRepository userRepository;         @Autowired         private AuditLogRepository auditLogRepository;         private Picture picture;         private UploadedFile file;          public Picture getPicture(){             return picture;         }          public void setPicture(Picture picture){             this.picture = picture;         }           public UploadedFile getFile() {             return file;         }          public void setFile(UploadedFile file) {             this.file = file;         }         @PostConstruct         public void init(){             file= new DefaultUploadedFile();         }          public void upload() {              if(file != null) {                 try{                     System.out.println("This is never shown");                      AuditLog log = new AuditLog(getAuthenticatedUser().getUsername(),"PICTURE UPLOADED: " + getAuthenticatedUser().getUsername() + " [" + getAuthenticatedUser().getUserRole() + "] ", new Date());                     auditLogRepository.save(log);                     System.out.println(file.getFileName());                     picture = new Picture(file.getFileName(), userRepository.findFirstByUsername(auth.getName()), new Date(), file.getFileName());                     pictureService.savePicture(picture);                 FacesMessage message = new FacesMessage("Succesful", file.getFileName() + " is uploaded.");                 FacesContext.getCurrentInstance().addMessage(null, message);                 String filename = FilenameUtils.getName(file.getFileName());                 InputStream input = file.getInputstream();                 OutputStream output = new FileOutputStream(new File( filename));                 try {                     IOUtils.copy(input, output);                 } finally {                     IOUtils.closeQuietly(input);                     IOUtils.closeQuietly(output);                 }                 }catch(Exception e)                 {                  }             }         } 

I would appreciated if someone could help me finding the problem.

Thanks in advance

[EDIT]: I have tried to make a clean Spring project and tried all of the possibilities to make a <p:fileUpload>, but none of them work. But I looked on the traffic of my browser, and I found out something interesting: Traffic after I uploaded the File with advanced. The POST statement appears immediately after I pressed the uploadbutton, the other GETs appear 1-2 seconds after.

1 Answers

Answers 1

The documentation says

Advanced File Upload

FileUploadListener is the way to access the uploaded files in this mode, when a file is uploaded defined fileUploadListener is processed with a FileUploadEvent as the parameter.

Add the fileUploadListener with a FileUploadEvent parameter. Make the button AJAXical. Lose the enctype. Don't bother with fileUpload's value.

Read More

Saturday, April 16, 2016

Filter for JSF table

Leave a Comment

I would like to add filter for JSF table and limit the values based on filter value.

<h:inputText id="search" value="#{accounts.searchString}"></h:inputText>  <h:commandButton value="Search" action="#{accounts.search()}">     <f:ajax execute="search" render="output"></f:ajax> </h:commandButton> 

I suppose that the best way will be to add the filter value into the SQL query:

SELECT * FROM CUSTOMERS ORDER BY %S %S offset ? limit ? 

Full code: http://pastebin.com/eEeTWEqK

How I can implement this for the code into the link?

PS. I modified the code this way:

<div class="div_input_style">     <h:inputText id="search" class="input_style" value="#{accounts.searchString}"></h:inputText>      <h:commandButton class="theSpan" value="Search by title">         <f:ajax execute="search" render="output"></f:ajax>     </h:commandButton> </div>  public List<AccountsObj> list(int firstRow, int rowCount, String sortField, boolean sortAscending) throws SQLException     {         String SqlStatement = null;          if (ds == null)         {             throw new SQLException();         }          Connection conn = ds.getConnection();         if (conn == null)         {             throw new SQLException();         }          String sortDirection = sortAscending ? "ASC" : "DESC";          SqlStatement = "SELECT * FROM ACCOUNT "             + " WHERE ? IS NULL OR ? IN (USER_NAME, FIRST_NAME, LAST_NAME)"             + " ORDER BY %S %S offset ? limit ? ";          String sql = String.format(SqlStatement, sortField, sortDirection);          PreparedStatement ps = null;         ResultSet resultSet = null;         List<AccountsObj> resultList = new ArrayList<>();          try         {             conn.setAutoCommit(false);             boolean committed = false;              ps = conn.prepareStatement(sql);             ps.setString(1, searchString);             ps.setString(2, searchString);             ps.setInt(3, firstRow);             ps.setInt(4, rowCount);              resultSet = ps.executeQuery();             resultList = ProcessorArrayList(resultSet);              conn.commit();             committed = true;          }         finally         {             ps.close();             conn.close();         }          return resultList;     } 

0 Answers

Read More