Search code examples
javajsfnullpointerexceptioncdiinject

How to resolve @Inject that gives NullPointerException?


I have been looking for a possible solution why the @Inject object I'm using is returning null. I looked all over stackoverflow and I also bump into this site Solving @Inject and Null Pointer Exception and Im sure that Ive already taken those into consideration.

I have a jsf file, sector.xhtml. I'm doing a file upload and calling the method upload of fileUploadController. Then the stockUploadController calls the parse method that calls the SubSectorStockParser's parse method. This method parses the uploaded csv, looping through each readline call. subSectorToStockMapping[4] is the sub sector value found in the csv file and it is being used to do a query to the database to retrieve the Subsector object. This is where the problem lies ---- the line ---

Subsector subSector = stockUploadController.findSubsector(subSectorToStockMapping[4]);

The stockUploadController is null eventhough it was annotated as @Inject, so it throws a NullPointerException.

21:51:57,316 INFO  [stdout] (default task-20) stockUploadController:null
21:51:57,316 ERROR [stderr] (default task-20) java.lang.NullPointerException
21:51:57,317 ERROR [stderr] (default task-20)   at com.traderpau.app.util.SubSectorToStockParser.parse(SubSectorToStockParser.java:45)
21:51:57,317 ERROR [stderr] (default task-20)   at com.traderpau.app.controller.StockUploadController.parse(StockUploadController.java:42)
21:51:57,317 ERROR [stderr] (default task-20)   at com.traderpau.app.controller.StockUploadController$Proxy$_$$_WeldClientProxy.parse(Unknown Source)
21:51:57,318 ERROR [stderr] (default task-20)   at com.traderpau.app.controller.FileUploadController.upload(FileUploadController.java:84)
21:51:57,318 ERROR [stderr] (default task-20)   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
21:51:57,318 ERROR [stderr] (default task-20)   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
21:51:57,319 ERROR [stderr] (default task-20)   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
21:51:57,319 ERROR [stderr] (default task-20)   at java.lang.reflect.Method.invoke(Method.java:606)
21:51:57,319 ERROR [stderr] (default task-20)   at com.sun.el.parser.AstValue.invoke(AstValue.java:292)
21:51:57,320 ERROR [stderr] (default task-20)   at com.sun.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:304)
21:51:57,320 ERROR [stderr] (default task-20)   at org.jboss.weld.util.el.ForwardingMethodExpression.invoke(ForwardingMethodExpression.java:40)
21:51:57,320 ERROR [stderr] (default task-20)   at org.jboss.weld.el.WeldMethodExpression.invoke(WeldMethodExpression.java:50)
21:51:57,321 ERROR [stderr] (default task-20)   at org.jboss.weld.util.el.ForwardingMethodExpression.invoke(ForwardingMethodExpression.java:40)
21:51:57,321 ERROR [stderr] (default task-20)   at org.jboss.weld.el.WeldMethodExpression.invoke(WeldMethodExpression.java:50)
21:51:57,321 ERROR [stderr] (default task-20)   at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:105)
21:51:57,322 ERROR [stderr] (default task-20)   at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:87)

I have confirmed that other parts of my code that uses @Inject is working.

Can you offer any other things that I might have missed out or any things that I can investigate to resolve this problem?

WEB-INF/beans.xml

 <beans 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/beans_1_1.xsd" bean-discovery-mode="all">
    </beans>

sector.xhtml

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui"> 

<h:head>
<title>Sector Test Entry</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<h:outputStylesheet name="css/screen.css" />
</h:head> 
<h:body> 
    <ui:composition template="/template/common/commonLayout.xhtml">
        <ui:define name="content">
    <div id="content">
        <h:form id="transaction" enctype="multipart/form-data">
        <p:growl id="growl" life="2000" />
            <p:panelGrid columns="2">

                <p:outputLabel value="Update:"/>
                <h:selectOneRadio id="update" value="#{dataUpdateBean.updateOption}">
                    <f:selectItem noSelectionOption="true" itemLabel="Sector" itemValue="Sector"/>
                    <f:selectItem itemLabel="Sub to Stocks" itemValue="Stocks"/>                    
                </h:selectOneRadio>
                <h:outputText value="&#160;" />
                <p:fileUpload id="file" value="#{fileUploadController.file}" mode="simple" />

                <h:commandButton action="#{fileUploadController.upload}" 
                    value="Click Me" />


            </p:panelGrid>
        </h:form>

    </div>
    </ui:define>
    </ui:composition>
</h:body> 
</html>

FileUploadController.java

import java.text.SimpleDateFormat;

import javax.enterprise.inject.Model;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.servlet.http.Part;

import org.primefaces.event.FileUploadEvent;
import org.primefaces.model.UploadedFile;

import com.traderpau.app.data.DataUpdateBean;

@Model
public class FileUploadController {

    @Inject
    private SectorUploadController sectorUploadController;

    @Inject 
    private StockUploadController stockUploadController;

    @Inject 
    private DataUpdateBean updateBean;

    private UploadedFile file;

    private Part filePart;


    public Part getFilePart() {
        return filePart;
    }


    public void setFilePart(Part filePart) {
        this.filePart = filePart;
    }


    public UploadedFile getFile() {
        return file;
    }


    public void setFile(UploadedFile file) {
        this.file = file;
    }

    public void saveFile(){
        System.out.println("File:" + file);
        System.out.println("File part:" + 
        filePart);
    }


    @SuppressWarnings("unused")
    public void upload() throws Exception{
        String fileName = file.getFileName();

        if(file != null) {
            FacesMessage message = new FacesMessage("Succesful", fileName + " is uploaded.");
            FacesContext.getCurrentInstance().addMessage(null, message);
        }else{
            FacesMessage message = new FacesMessage("Error in upload of " + fileName );
            FacesContext.getCurrentInstance().addMessage(null, message);
        }
        String path = FacesContext.getCurrentInstance().getExternalContext().getRealPath("/");
        SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMddHHmmss");
        if(updateBean.getUpdateOption().equalsIgnoreCase("Sector")){
            file.write(
                    String.format("%s%s%s", path, "_TRADE_MONITOR_UPLOAD/SECTOR_TO_SUB/",fileName
                            ));
            sectorUploadController.parse(String.format("%s%s%s", path, "_TRADE_MONITOR_UPLOAD/SECTOR_TO_SUB/",fileName
                    ));
            FacesMessage message = new FacesMessage("Sector to Sub successdul.");
            FacesContext.getCurrentInstance().addMessage(null, message);
        }else if(updateBean.getUpdateOption().equalsIgnoreCase("Stocks")){
            file.write(
                    String.format("%s%s%s", path, "_TRADE_MONITOR_UPLOAD/SUB_TO_STOCK/",fileName
                            ));
            stockUploadController.parse(String.format("%s%s%s", path, "_TRADE_MONITOR_UPLOAD/SUB_TO_STOCK/",fileName));
            FacesMessage message = new FacesMessage("Sub to Stocks successful.");
            FacesContext.getCurrentInstance().addMessage(null, message);
        }
    }
}

StockUploadController.java

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.enterprise.inject.Model;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.inject.Inject;

import com.traderpau.app.data.DataUpdateBean;
import com.traderpau.app.model.Stock;
import com.traderpau.app.model.Subsector;
import com.traderpau.app.service.StockRegistration;
import com.traderpau.app.util.SubSectorToStockParser;

@Model
public class StockUploadController {
    @Inject
    private StockRegistration stockRegistration;

    @Inject
    private DataUpdateBean bean;

    public void findSubsectorName(ActionEvent actionEvent){
        addMessage("Search query:" + bean.getSubSectorName());
        System.out.println("StockUploadController:findSubsectorName");
        stockRegistration.findSubSectorName(bean.getSubSectorName());
    }

    public void addMessage(String summary) {
        FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, summary,  null);
        FacesContext.getCurrentInstance().addMessage(null, message);
    }

    public void parse(String filename){

        Map<String, List<String>> subSectorToStockMap = new HashMap<>();
        SubSectorToStockParser parser = new SubSectorToStockParser(subSectorToStockMap);
        parser.parse(filename);

    }

    public Subsector findSubsector(String name){
        System.out.println("StockRegistration:" + stockRegistration);
        return stockRegistration.findSubSectorName(name);
    }

    public void registerStock(Stock stock) throws Exception{
        stockRegistration.register(stock);
    }

}

SubSectorToStockParser.java

    import java.io.BufferedReader;
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;

    import javax.inject.Inject;

    import com.traderpau.app.controller.StockUploadController;
    import com.traderpau.app.model.Stock;
    import com.traderpau.app.model.Subsector;
    import com.traderpau.app.service.StockRegistration;

     public class SubSectorToStockParser implements Parser {

    private Map<String, List<String>> subSectorToStockMap = new HashMap<>();

    @Inject
    private StockRegistration stockRegistration;

    @Inject
    private StockUploadController stockUploadController;

    public SubSectorToStockParser(Map<String, List<String>> subSectorToStockMap) {
        super();
        this.subSectorToStockMap = subSectorToStockMap;
    }

    @Override
    public void parse(String filename) {
        System.out.println("Sub sector filename:" + filename);
        BufferedReader br = null;
        try{
            br = new BufferedReader(new FileReader(filename));
            String line;
            String csvSplitBy = ",";
            System.out.println("stockUploadController:" + stockUploadController );

            while((line = br.readLine()) != null){
                String[] subSectorToStockMapping   = line.split(csvSplitBy);
                Subsector subSector = stockUploadController.findSubsector(subSectorToStockMapping[4]);
                Stock stock = new Stock();
                stock.setSymbol(subSectorToStockMapping[0]);
                stock.setStockName(subSectorToStockMapping[1]);
                stock.setOutstandingShares( Double.parseDouble(subSectorToStockMapping[2]) );
                stock.setSubSector(subSector != null? subSector: null);
                stockUploadController.registerStock(stock);
            }

        }catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

Solution

  • The problem is using the 'new' keyword to instantiate SubSectorToStockParser

    @Model
    public class StockUploadController {
     ...
        public void parse(String filename){
    
            Map<String, List<String>> subSectorToStockMap = new HashMap<>();
            SubSectorToStockParser parser = new SubSectorToStockParser(subSectorToStockMap);  //SubSectorToStockParser should be injected
            parser.parse(filename);
    
        }
    ...
    

    By using the 'new' keyword, CDI has no control over the lifecycle of the resulting object and thus cannot perform injections, causing stockUploadController to be null.

    To solve this, mark SubSectorToStockParser with the appropriate scope (e.g: @Model), then inject it into StockUploadController.