Search code examples
springjsfjsf-2view-scope

Spring with JSF 2 view scope losing injected values


I have implemented the custom view scope for spring. The page was a redirect from another bean with prerenderView listener that loads a p:datatable. The datatable is loaded with the values and then there is a p:command button to submit the data to a backing bean that calls a service to use the dao implement to update a table. The daoImpl has none of the injected properties now. Datasource and jdbctemplate are now null. I believe I have everything configured to let Spring managed my beans but it seems to me a new instance of the bean is created and spring is not aware therefore doesn't inject the values. Code below has been simplified.

Page

<ui:define name="content">
    <h:form id="form1" >

       <p:messages id="msgs" autoUpdate="true"/> 

        <p:panel id="panel1" >
        <center>

            <p:dataTable id="campaignQTable" var="campaignQ" value="#{campaign.campaignQRows}"
                      style="text-align:right"  editable="true"  > 
                     <!-- paginator="true" rows="10"  
                     paginatorTemplate="{CurrentPageReport}  {FirstPageLink} {PreviousPageLink} 
                        {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"  
                     rowsPerPageTemplate="5,10,15" >  

                     widgetVar="scrollist">
                    scrollable="true"  scrollWidth="1100">  

                     -->  

            <f:facet name="header">  
                Update Campaign Qualifications
            </f:facet>  

             <p:column headerText="Contest Name" style="text-align:center">
                <h:outputText value="#{campaignQuals.contestName}" />
            </p:column>


        </p:dataTable>

         <p:commandButton value="Save Campaign Qualifications" action="#{campaign.saveCampaignQuals}" 
             process="@all" update="msgs,panel1" />

        </center>
        </p:panel>

    </h:form>

Bean

@Component("campaign")
@Scope("view")
public class CampaignQualBeans implements Serializable {

    private static final long serialVersionUID = 1L;

    @Autowired
    @Qualifier("CampaignServiceImpl")
    CampaignService campaignService;

    //  CampaignQualsTblId - all rows
    ArrayList<CampaignQualsTblId> campaignQNames = new ArrayList<CampaignQualsTblId>();
    CampaignQualsTblId selectedCampaignQ;

    String contestName;
    String contestYear;
    String conYearPlus1;

    //  CampaignQualsTbl - single Campaign all rows for a single year
    ArrayList<CampaignQualsTbl> campaignQRows = new ArrayList<CampaignQualsTbl>();

    // *************************************************************
    // CONSTRUCTORS ************************************************
    // *************************************************************

    public CampaignQualBeans() {
        super();        
    }       




    //  Upon listener being running, get all the rows for that contest year (campaignQualsUpdate.xhtml)
    public void loadCampaignQuals(ComponentSystemEvent event) {

        if (FacesContext.getCurrentInstance().getPartialViewContext().isAjaxRequest()) {
            return;

        }else{  
            System.out.println("Campaign Qualifications Loaded");
            campaignQRows = campaignService.getCampaignQualRows(contestName, contestYear);

            int year = Integer.parseInt(contestYear) + 1;
            conYearPlus1 = String.valueOf(year);

        }
    }

    //  Once button has been selected, save the data to the CampaignQualsTbl (campaignQualsUpdate.xhtml)
    public String saveCampaignQuals() {
        System.out.println("Contest Saved");

        boolean success = campaignService.saveCampaignQRows(contestName, contestYear, campaignQRows);

        if (success) {
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Campaign Qualifications were successfully entered for " 
                    + contestName + " - " + contestYear));
        }
        else {

            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR,"Campaign Qualifications were not saved.  Please check entry and save again.  " + 
             "If the problem continues, please contact the Help Desk.",""));
        }
        return null;
    }
}

view scope

    public class ViewScope implements Scope{

    public Object get(String name, ObjectFactory objectFactory) {
        Map<String,Object> viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();

        if(viewMap.containsKey(name)) {
            return viewMap.get(name);
        } else {
            Object object = objectFactory.getObject();
            viewMap.put(name, object);

            return object;
        }
    }

    public Object remove(String name) {
        return FacesContext.getCurrentInstance().getViewRoot().getViewMap().remove(name);
    }

    public String getConversationId() {
        return null;
    }

    public void registerDestructionCallback(String name, Runnable callback) {
        //Not supported
    }

    public Object resolveContextualObject(String key) {
        return null;
    }       
}

Service

    @Service("CampaignServiceImpl")
public class CampaignServiceImpl implements CampaignService,ServletContextAware,Serializable{

    private static final long serialVersionUID = 1L;

    @Autowired
    @Qualifier("campaignDao")
    CampaignDao campaignDAO;


    private transient ServletContext servletContext;

    //  call DAO to insert the campaign qualifications data into the Campaign_Quals_tbl table
    @Override
    public boolean saveCampaignQRows(String contestName, String contestYear,
            ArrayList<CampaignQualsTbl> campaignQRows) {

        boolean success = campaignDAO.insertCampaignQRows(contestName, contestYear, campaignQRows);

        return success;
    }

    @Override
    public void setServletContext(ServletContext arg0) {
        // TODO Auto-generated method stub

    }

}

Dao

@Repository("campaignDao")
public class CampaignDaoImpl implements CampaignDao,Serializable{

    private static final long serialVersionUID = 1L;

    @Autowired
    @Qualifier("myDataSource")
    private transient DataSource dataSource;

    @Autowired
    transient ServletContext context;

    private transient JdbcTemplate JDBCTemplate;
    private SimpleJdbcCall jdbcCall;  

    //setup JDBC sources/connections
    public void setUserJDBCTemplate(JdbcTemplate userJDBCTemplate) {
        this.JDBCTemplate = new JdbcTemplate(dataSource);
    }

    // *************************************************************
    // SQL Statements for the Campaign_Quals_tbl********************
    // *************************************************************    
    private static final String INSERT_CAMPAIGN_Q_ROW =
            "Insert into Campaign_Quals_tbl (ContestName, ContestYear, Agent_Tenure, Torunament_Level, Placed_Applications " +
            "Life_Premium, Contest_Premium, Min_Life_Premium" +
            "Values (?, ?, ?, ?, ?, ?, ?, ?)";



    //  execute the query that inserts the new rows into the Campaign_Quals_tbl table
    @Override
    public boolean insertCampaignQRows(String contestName, String contestYear, 
            ArrayList<CampaignQualsTbl> campaignQRows) {

        ArrayList<CampaignQualsTbl> rows = campaignQRows;

        try {
            JDBCTemplate = new JdbcTemplate(dataSource);

            for (CampaignQualsTbl row : rows) {

                Object[] parameters = new Object[] {contestName, contestYear, row.getAgentTenure(), row.getTournamentLevel(), 
                    row.getPlacedApplications(), row.getLifePremium(), row.getContestPremium(), row.getMinLifePremium()};

                JDBCTemplate.update(INSERT_CAMPAIGN_Q_ROW, parameters);
            }

        } catch (DataAccessException e){
                e.printStackTrace();                
                return false;
        }   
        return true;
    }   
}

Logs

   START PHASE RESTORE_VIEW(1)
END PHASE RESTORE_VIEW(1)
START PHASE APPLY_REQUEST_VALUES(2)
[DEBUG] - org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory -   - Creating instance of bean 'campaignQuals'
[DEBUG] - org.springframework.beans.factory.annotation.InjectionMetadata -   - Processing injected method of bean 'campaignQuals': AutowiredFieldElement for tfic.com.Campaign.service.CampaignService tfic.com.Campaign.beans.CampaignQualBean.campaignService
[DEBUG] - org.springframework.beans.factory.support.AbstractBeanFactory -   - Returning cached instance of singleton bean 'CampaignServiceImpl'
[DEBUG] - org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata -   - Invoking init method on bean 'campaignQuals': public void tfic.com.Campaign.beans.CampaignQualBean.loadCampaignQualNames()
[DEBUG] - org.springframework.jdbc.core.JdbcTemplate -   - Executing SQL query [Select ContestName, Max(ContestYear) as ContestYear from Campaign_Quals_tbl group by ContestName]
[DEBUG] - org.springframework.jdbc.datasource.DataSourceUtils -   - Fetching JDBC Connection from DataSource
[DEBUG] - org.springframework.jdbc.datasource.DataSourceUtils -   - Returning JDBC Connection to DataSource
[DEBUG] - org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory -   - Finished creating instance of bean 'campaignQuals'
END PHASE APPLY_REQUEST_VALUES(2)
START PHASE PROCESS_VALIDATIONS(3)
END PHASE PROCESS_VALIDATIONS(3)
START PHASE UPDATE_MODEL_VALUES(4)
END PHASE UPDATE_MODEL_VALUES(4)
START PHASE INVOKE_APPLICATION(5)
tfic.com.Campaign.model.CampaignQualsTblId@11d93d05
END PHASE INVOKE_APPLICATION(5)
START PHASE RESTORE_VIEW(1)
END PHASE RESTORE_VIEW(1)
START PHASE APPLY_REQUEST_VALUES(2)
END PHASE APPLY_REQUEST_VALUES(2)
START PHASE PROCESS_VALIDATIONS(3)
[DEBUG] - org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory -   - Creating instance of bean 'campaign'
[DEBUG] - org.springframework.beans.factory.annotation.InjectionMetadata -   - Processing injected method of bean 'campaign': AutowiredFieldElement for tfic.com.Campaign.service.CampaignService tfic.com.Campaign.beans.CampaignQualBeans.campaignService
[DEBUG] - org.springframework.beans.factory.support.AbstractBeanFactory -   - Returning cached instance of singleton bean 'CampaignServiceImpl'
[DEBUG] - org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory -   - Finished creating instance of bean 'campaign'
END PHASE PROCESS_VALIDATIONS(3)
START PHASE UPDATE_MODEL_VALUES(4)
END PHASE UPDATE_MODEL_VALUES(4)
START PHASE INVOKE_APPLICATION(5)
END PHASE INVOKE_APPLICATION(5)
START PHASE RENDER_RESPONSE(6)
Campaign Qualifications Loaded
[DEBUG] - org.springframework.jdbc.core.JdbcTemplate -   - Executing prepared SQL query
[DEBUG] - org.springframework.jdbc.core.JdbcTemplate -   - Executing prepared SQL statement [Select Agent_Tenure, Tournament_Level, Placed_Applications,Life_Premium, Contest_Premium, Min_Life_Premium from Campaign_Quals_tbl where ContestName = ? and ContestYear = ?]
[DEBUG] - org.springframework.jdbc.datasource.DataSourceUtils -   - Fetching JDBC Connection from DataSource
[DEBUG] - org.springframework.jdbc.datasource.DataSourceUtils -   - Returning JDBC Connection to DataSource
END PHASE RENDER_RESPONSE(6)
START PHASE RESTORE_VIEW(1)
END PHASE RESTORE_VIEW(1)
START PHASE APPLY_REQUEST_VALUES(2)
END PHASE APPLY_REQUEST_VALUES(2)
START PHASE PROCESS_VALIDATIONS(3)
END PHASE PROCESS_VALIDATIONS(3)
START PHASE UPDATE_MODEL_VALUES(4)
END PHASE UPDATE_MODEL_VALUES(4)
START PHASE INVOKE_APPLICATION(5)
Lost injection datasource and jdbctemplate
END PHASE INVOKE_APPLICATION(5)

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_3_0.xsd" version="3.0">
    <display-name>SalesCampaignAdmin</display-name>
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
        <enabled>true</enabled>
        <async-supported>false</async-supported>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>
        *.xhtml</url-pattern>
    </servlet-mapping>
    <listener>
        <listener-class>
            org.springframework.web.context.request.RequestContextListener
        </listener-class>
    </listener>

    <servlet>
        <servlet-name>mvc-dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>mvc-dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/application-context.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

      <context-param>
    <param-name>javax.faces.PARTIAL_STATE_SAVING</param-name>
    <param-value>false</param-value>
  </context-param>

  <context-param>
    <param-name>javax.faces.FACELETS_REFRESH_PERIOD</param-name>
    <param-value>1</param-value>
  </context-param>
  <context-param>
    <param-name>org.apache.myfaces.ALLOW_JAVASCRIPT</param-name>
    <param-value>true</param-value>
  </context-param>
  <context-param>
    <param-name>org.apache.myfaces.PRETTY_HTML</param-name>
    <param-value>true</param-value>
  </context-param>
  <context-param>
    <param-name>org.apache.myfaces.DETECT_JAVASCRIPT</param-name>
    <param-value>false</param-value>
  </context-param>
  <context-param>
    <param-name>org.apache.myfaces.AUTO_SCROLL</param-name>
    <param-value>true</param-value>
  </context-param>
  <context-param>
    <param-name>com.sun.faces.expressionFactory</param-name>
    <param-value>com.sun.el.ExpressionFactoryImpl</param-value>
  </context-param>
  <context-param>
    <param-name>org.apache.myfaces.EXPRESSION_FACTORY</param-name>
    <param-value>com.sun.el.ExpressionFactoryImpl</param-value>
  </context-param> 

Faces-config

 <?xml version="1.0" encoding="UTF-8"?>
<faces-config
    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"
    version="2.2">

    <application>
        <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
    </application>


</faces-config>

app context

<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="
            http://www.springframework.org/schema/beans     
            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/context 
            http://www.springframework.org/schema/context/spring-context-3.0.xsd
            http://www.springframework.org/schema/mvc
            http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
            http://www.springframework.org/schema/tx
            http://www.springframework.org/schema/mvc/spring-tx-3.0.xsd
            http://www.springframework.org/schema/jdbc
            http://www.springframework.org/schema/mvc/spring-jdbc-3.0.xsd">

            <context:component-scan base-package="tfic.*" /> 
        <mvc:annotation-driven/>
    <import resource="mvc-dispatcher-servlet.xml"/>
    <import resource="spring-datasource.xml"/>

    <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
            <property name="scopes">
                <map>
                    <entry key="view">
                        <bean class="tfic.com.Campaign.helpers.ViewScope"/>
                    </entry>
                </map>
            </property>
        </bean>


    </beans>

Solution

  • Adding this to web.xml and the datasource is getting injected everytime now. Not sure of the repercussions yet.

    <context-param>
        <param-name>org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</param-name>
        <param-value>false</param-value>
    </context-param>
    

    Although I didn't get this exception, I still had the break of injections that the answer describes.

    ViewScoped Bean cause NotSerializableException