I am using Spring to manage my controller instances using setControllerFactory (see below), to enable the controller to care for the persistence of the entities by autowiring a EntityManagerFactory and handling of database transactions. This approach works fine with a couple of other controllers I developed so far. However, lately I wanted to extend the functionality and copied an existing working controller scaffold, adopted it to a different Entity with the according FXML UI. For whatever reason I can’t make it work. What happens is that the controller gets instantiated and the controllers initialize method is called without any of the declared FMXL members being injected upfront – I debugged it, they are all null. Therefore initialize throws a NullPointerException. If I comment the setControllerFactory line out and all persistent related commands, it works fine, but I have no persitence. I spent an hour to compare the working examples with the new one but can’t find the root cause.
Please find following the code snippets
The entity class
@Entity
@Inheritance
@Access(AccessType.FIELD)
public abstract class Term implements Serializable {
@Transient
private transient final Logger logger = LoggerFactory.getLogger(this.getClass());
private static final long serialVersionUID = -5585030750290575696L;
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String identifier;
private String goal = "tbd."; // what shall be achieved in the given time frame
@OneToOne
private Term predecessor;
@OneToOne
private Term successor;
private int myNumber; // order within release
private int version;
private LocalDateTime publishingDate;
private String owner;
/*
* Note: Use Period.between(begin, end).getUnits() to determine the years, months, days, hours between begin and end
*/
private LocalDate begin;
private LocalDate end;
@Transient
private transient DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withLocale(Locale.GERMAN);
/*
* Terms are structured in hierarchical levels as defined by the following finals
*/
public final static String RELEASE = "Release"; // top level
public final static String PHASE = "Phase"; //
public final static String BASELINE = "Baseline";
public final static String ITERATION = "Iteration";
public final static String TASK = "Task"; // bottom level
@Transient
public final static transient ArrayList<String> levels =
new ArrayList<String>(Arrays.asList(RELEASE, PHASE, BASELINE, ITERATION, TASK));
@Transient
public transient static ObservableList<String> chronoUnits = FXCollections.observableArrayList(
EffortEstimate.YEARS, EffortEstimate.MONTHS, EffortEstimate.WEEKS, EffortEstimate.DAYS, EffortEstimate.HOURS);
public abstract String getLevel(); // return one of the above values indicating the hierarchical level
protected String defaultTimeUnit;
/*
* A Term may be broken down into sub-Terms, called children. A child can not be a Term from a higher hierarchical level.
* Between parent and child is a bi-directional relationship
*/
@ManyToOne
private Term parent;
@Transient
private List<Term> children;
@Transient
private transient ObservableList<Term> observableChildren = FXCollections.observableArrayList(); // an observable mirror of field children
/*
* If a requirement should be implemented during a certain time, but it is unclear by which team, then the
* requirement may be temporarily assigned to a term.
*/
@Transient
private List<UserRequirement> userRequirements;
@Transient
private transient final ListProperty<UserRequirement> observableRequirements = new SimpleListProperty<>(FXCollections.observableArrayList());
public enum Type {
Development, Hardening
}
@Enumerated(EnumType.STRING)
protected Type myType; // see enum Type
public enum Status {
Setup, Upcoming, Running, Complete
}
@Enumerated(EnumType.STRING)
protected Status myState; // see enum Status
protected Term() {
children = FXCollections.observableArrayList();
userRequirements = FXCollections.observableArrayList();
publishingDate = LocalDateTime.now();
myState = Status.Setup;
myType = Type.Development;
}
public Term(Term parent, String identifier) {
this();
setIdentifier(identifier);
setParent(parent);
}
private Long getId() {
return id;
}
private void setId(Long id) {
this.id = id;
}
public Term getParent() {
return parent;
}
public void setParent(Term parent) {
this.parent = parent;
}
public ObservableList<Term> getObservableChildren() {
return observableChildren;
}
@Access(AccessType.PROPERTY)
@OneToMany(mappedBy="parent", orphanRemoval = true, cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
private List<Term> getChildren() {
return children;
}
@SuppressWarnings("unused") // Used by JPA
private void setChildren(List<Term> children) {
this.children = children;
observableChildren.setAll(FXCollections.observableArrayList(children));
}
public void addChild(Term child) {
children.add(child);
observableChildren.add(child);
Term currentParent = child.getParent();
if(currentParent != null) {
currentParent.delChild(child);
}
child.setParent(this);
}
public boolean delChild(Term child) {
child.setParent(null);
observableChildren.remove(child);
return children.remove(child);
}
public String getIdentifier() {
return identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
public String getGoal() {
return goal;
}
public void setGoal(String goal) {
this.goal = goal;
}
public Term getPredecessor() {
return predecessor;
}
public void setPredecessor(Term predecessor) {
this.predecessor = predecessor;
}
public Term getSuccessor() {
return successor;
}
public void setSuccessor(Term successor) {
this.successor = successor;
}
public LocalDate getBegin() {
return begin;
}
public void setBegin(LocalDate begin) {
this.begin = begin;
}
public LocalDate getEnd() {
return end;
}
public void setEnd(LocalDate end) {
this.end = end;
}
public String getDefaultTimeUnit() {
return defaultTimeUnit;
}
public void setDefaultTimeUnit(String defaultTimeUnit) {
this.defaultTimeUnit = defaultTimeUnit;
}
public ObservableList<UserRequirement> getObservableRequirements() {
return observableRequirements;
}
/*
public ListProperty<UserRequirement> observableRequirementsProperty() {
return observableRequirements;
}
*/
@Access(AccessType.PROPERTY)
@OneToMany(mappedBy="term", fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
private List<UserRequirement> getRequirements() {
return userRequirements;
}
@SuppressWarnings("unused") // Used by JPA
private void setRequirements(List<UserRequirement> userRequirements) {
//logger.debug(userRequirements.size() + " reqs to " + this.debug());
this.userRequirements = userRequirements;
if(userRequirements != null) {
observableRequirements.clear();
for(UserRequirement req: userRequirements) {
observableRequirements.add(req);
}
}
observableRequirements.setAll(FXCollections.observableList(userRequirements));
}
public void addRequirement(UserRequirement userRequirement) {
logger.debug(userRequirement.debug() + " to " + this.debug());
userRequirements.add(userRequirement);
observableRequirements.add(userRequirement);
userRequirement.setTerm(this);
}
public void delRequirement(UserRequirement userRequirement) {
logger.debug(userRequirement.debug() + " from " + this.debug());
if(userRequirements.contains(userRequirement)) {
if(!userRequirements.remove(userRequirement)) {
logger.error("Removal of requirement from list failed! Term: " + this.toString());
}
if(!observableRequirements.remove(userRequirement)) {
logger.error("Removal of requirement from observable list failed! Term: " + this.toString());
}
userRequirement.setTerm(null);
}
}
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
public LocalDateTime getPublishingDate() {
return publishingDate;
}
public void setPublishingDate(LocalDateTime publishingDate) {
this.publishingDate = publishingDate;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public List<String> getAllowedChildClasses() {
List<String> list = new ArrayList<String>(levels.subList(levels.indexOf(getLevel())+1, levels.size()));
//list.set(0, "Sub-" + list.get(0));
return list;
}
public int getMyNumber() {
return myNumber;
}
public void setMyNumber(int myNumber) {
this.myNumber = myNumber;
}
public Type getMyType() {
return myType;
}
public void setMyType(Type myType) {
this.myType = myType;
}
public Status getMyState() {
return myState;
}
public void setMyState(Status myState) {
this.myState = myState;
}
@Override
public String toString() {
String beginString = begin == null? "tbd." : begin.format(formatter);
String endString = end == null? "tbd." : end.format(formatter);
return String.format("%s-%s: %s > %s", beginString, endString, identifier, goal);
}
public String debug() {
String tmp = getRequirements() == null ? "0" : Integer.toString(getRequirements().size());
String txt = getId() + "@" + getLevel() + ": " + toString() + "; " + tmp + " userRequirements; " + getChildren().size() + " children";
if(getParent() == null) {
txt += " as root element";
} else {
txt += " owned by " + getParent().getId();
}
return txt;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
//result = prime * result + version;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Term other = (Term) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
/*
if (version != other.version)
return false;
*/
return true;
}
}
The FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.DatePicker?>
<?import javafx.scene.control.Hyperlink?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.Separator?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<GridPane fx:id="gridPane" hgap="10.0" maxHeight="1.7976931348623157E308"
maxWidth="1.7976931348623157E308" stylesheets="@../stylesheets/controller.css"
vgap="10.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.agiletunes.controllers.terms.TermEditorCtrl">
<columnConstraints>
<ColumnConstraints maxWidth="1.7976931348623157E308" />
<ColumnConstraints maxWidth="1.7976931348623157E308"
minWidth="10.0" />
<ColumnConstraints minWidth="10.0" />
<ColumnConstraints maxWidth="1.7976931348623157E308"
minWidth="10.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" />
<RowConstraints minHeight="10.0" />
<RowConstraints minHeight="10.0" />
<RowConstraints minHeight="10.0" />
<RowConstraints />
<RowConstraints minHeight="10.0" />
<RowConstraints minHeight="10.0" />
<RowConstraints minHeight="10.0" />
<RowConstraints minHeight="10.0" />
<RowConstraints minHeight="10.0" />
</rowConstraints>
<children>
<DatePicker fx:id="beginDatePicker" onAction="#beginChanged"
GridPane.columnIndex="1" GridPane.rowIndex="4">
<GridPane.margin>
<Insets />
</GridPane.margin>
</DatePicker>
<DatePicker fx:id="endDatePicker" onAction="#endChanged"
GridPane.columnIndex="3" GridPane.rowIndex="4">
<GridPane.margin>
<Insets right="24.0" />
</GridPane.margin>
</DatePicker>
<Label text="Begin" GridPane.halignment="LEFT"
GridPane.rowIndex="4">
<GridPane.margin>
<Insets left="24.0" />
</GridPane.margin>
</Label>
<Label text="End" GridPane.columnIndex="2" GridPane.halignment="LEFT"
GridPane.rowIndex="4">
<GridPane.margin>
<Insets left="12.0" right="8.0" />
</GridPane.margin>
</Label>
<Label maxWidth="1.7976931348623157E308" text="Identifier"
GridPane.hgrow="ALWAYS" GridPane.rowIndex="2">
<GridPane.margin>
<Insets left="24.0" right="12.0" top="24.0" />
</GridPane.margin>
</Label>
<TextField fx:id="identifierTextField" onAction="#identifierChanged"
onMouseExited="#identifierChanged" GridPane.columnIndex="1"
GridPane.columnSpan="2147483647" GridPane.rowIndex="2">
<GridPane.margin>
<Insets right="24.0" top="24.0" />
</GridPane.margin>
</TextField>
<Label layoutX="34.0" layoutY="80.0" maxWidth="1.7976931348623157E308"
text="Level" GridPane.rowIndex="5">
<GridPane.margin>
<Insets left="24.0" />
</GridPane.margin>
</Label>
<ComboBox fx:id="timeUnitComboBox" maxWidth="1.7976931348623157E308"
onAction="#timeUnitChanged" GridPane.columnIndex="3" GridPane.hgrow="ALWAYS"
GridPane.rowIndex="5">
<GridPane.margin>
<Insets right="24.0" />
</GridPane.margin>
</ComboBox>
<Separator prefWidth="200.0" GridPane.columnSpan="2147483647"
GridPane.rowIndex="7" />
<Label layoutX="34.0" layoutY="39.0" text="Goal"
GridPane.rowIndex="3">
<GridPane.margin>
<Insets left="24.0" />
</GridPane.margin>
</Label>
<TextField fx:id="goalTextField" layoutX="116.0" layoutY="34.0"
onAction="#goalChanged" onKeyReleased="#goalChanged" onMouseExited="#goalChanged"
GridPane.columnIndex="1" GridPane.columnSpan="2147483647"
GridPane.rowIndex="3">
<GridPane.margin>
<Insets right="24.0" />
</GridPane.margin>
</TextField>
<Label layoutX="34.0" layoutY="162.0" text="Time unit"
GridPane.columnIndex="2" GridPane.halignment="RIGHT"
GridPane.rowIndex="5">
<GridPane.margin>
<Insets left="12.0" right="8.0" />
</GridPane.margin>
</Label>
<Label layoutX="34.0" layoutY="162.0" maxWidth="1.7976931348623157E308"
text="Type" GridPane.rowIndex="6">
<GridPane.margin>
<Insets left="24.0" />
</GridPane.margin>
</Label>
<ComboBox fx:id="typeComboBox" layoutX="116.0" layoutY="157.0"
maxWidth="1.7976931348623157E308" onAction="#typeChanged"
GridPane.columnIndex="1" GridPane.rowIndex="6" />
<Label layoutX="391.0" layoutY="162.0" text="State"
GridPane.columnIndex="2" GridPane.halignment="LEFT"
GridPane.rowIndex="6">
<GridPane.margin>
<Insets left="12.0" right="8.0" />
</GridPane.margin>
</Label>
<ComboBox fx:id="stateComboBox" layoutX="464.0" layoutY="157.0"
maxWidth="1.7976931348623157E308" onAction="#stateChanged"
GridPane.columnIndex="3" GridPane.rowIndex="6">
<GridPane.margin>
<Insets right="24.0" />
</GridPane.margin>
</ComboBox>
<Separator prefWidth="200.0" GridPane.columnSpan="2147483647"
GridPane.rowIndex="1" />
<Label layoutX="34.0" layoutY="99.0" maxWidth="1.7976931348623157E308"
text="Parent">
<GridPane.margin>
<Insets left="24.0" top="24.0" />
</GridPane.margin>
</Label>
<Hyperlink fx:id="parentHyperlink" onMouseClicked="#parentSelected"
text="Hyperlink" GridPane.columnIndex="1" GridPane.columnSpan="2147483647">
<GridPane.margin>
<Insets right="24.0" top="24.0" />
</GridPane.margin>
</Hyperlink>
<ListView fx:id="subTermListView" onMouseClicked="#childSelected"
prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1"
GridPane.columnSpan="2147483647" GridPane.rowIndex="8">
<GridPane.margin>
<Insets bottom="24.0" right="24.0" />
</GridPane.margin>
</ListView>
<Label layoutX="34.0" layoutY="38.0" maxWidth="1.7976931348623157E308"
text="Sub-terms" GridPane.hgrow="ALWAYS" GridPane.rowIndex="8"
GridPane.valignment="TOP">
<GridPane.margin>
<Insets left="24.0" />
</GridPane.margin>
</Label>
<Label fx:id="levelLabel" text="Label" GridPane.columnIndex="1"
GridPane.rowIndex="5" />
</children>
</GridPane>
The Controller class:
..
@Controller
public class TermEditorCtrl implements Initializable {
@FXML private DatePicker beginDatePicker;
@FXML private DatePicker endDatePicker;
@FXML private TextField identifierTextField;
@FXML private ComboBox<String> timeUnitComboBox;
@FXML private TextField goalTextField;
@FXML private ComboBox<Term.Type> typeComboBox;
@FXML private ComboBox<Term.Status> stateComboBox;
@FXML private ListView<Term> subTermListView;
@FXML private Label levelLabel;
@FXML private Hyperlink parentHyperlink;
@Autowired private TermService termService;
@Autowired private EntityManagerFactory emf;
protected EntityManager entityManager;
protected EntityTransaction transaction;
protected boolean isDirty;
protected Term myTerm;
public TermEditorCtrl() {
}
@Override
public void initialize(URL location, ResourceBundle resources) {
typeComboBox.setItems(FXCollections.observableArrayList(Term.Type.values()));
stateComboBox.setItems(FXCollections.observableArrayList(Term.Status.values()));
timeUnitComboBox.setItems(FXCollections.observableArrayList(Term.chronoUnits));
}
@Transactional(propagation=Propagation.REQUIRED)
protected void saveIfNeeded() {
if(transaction != null) {
if(isDirty) {
Alert alert = new Alert(AlertType.CONFIRMATION, "Otherwise all changes will be lost\n\n", ButtonType.YES, ButtonType.NO);
alert.setTitle("Requirement changed");
alert.setHeaderText("Save changes?");
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == ButtonType.YES){
termService.save(myTerm);
transaction.commit();
}
} else {
transaction.rollback();
transaction = null;
}
}
}
@Transactional(propagation=Propagation.REQUIRED)
public void setTerm(Term aTerm) {
saveIfNeeded();
myTerm = aTerm;
entityManager = emf.createEntityManager();
transaction = entityManager.getTransaction();
transaction.begin();
identifierTextField.setText(aTerm.getIdentifier());
beginDatePicker.setValue(aTerm.getBegin());
endDatePicker.setValue(aTerm.getEnd());
goalTextField.setText(aTerm.getGoal());
stateComboBox.getSelectionModel().select(aTerm.getMyState());
typeComboBox.getSelectionModel().select(aTerm.getMyType());
levelLabel.setText(aTerm.getLevel());
subTermListView.setItems(aTerm.getObservableChildren());
timeUnitComboBox.getSelectionModel().select(aTerm.getDefaultTimeUnit());
Term parent = aTerm.getParent();
if (parent != null) {
parentHyperlink.setText(parent.getIdentifier());
} else {
parentHyperlink.setText("");
}
isDirty = false;
}
@FXML
void beginChanged(ActionEvent event) {
myTerm.setBegin(beginDatePicker.getValue());
isDirty = true;
}
@FXML
void endChanged(ActionEvent event) {
myTerm.setEnd(endDatePicker.getValue());
isDirty = true;
}
@FXML
void goalChanged(KeyEvent event) {
myTerm.setGoal(goalTextField.getText());
isDirty = true;
}
@FXML
void identifierChanged(ActionEvent event) {
myTerm.setIdentifier(identifierTextField.getText());
isDirty = true;
}
@FXML
void stateChanged(ActionEvent event) {
myTerm.setMyState(stateComboBox.getSelectionModel().getSelectedItem());
isDirty = true;
}
@FXML
void childSelected(MouseEvent event) {
setTerm(subTermListView.getSelectionModel().getSelectedItem());
}
@FXML
void parentSelected(MouseEvent event) {
setTerm(myTerm.getParent());
}
@FXML
void timeUnitChanged(ActionEvent event) {
myTerm.setDefaultTimeUnit(timeUnitComboBox.getSelectionModel().getSelectedItem());
isDirty = true;
}
@FXML
void typeChanged(ActionEvent event) {
myTerm.setMyType(typeComboBox.getSelectionModel().getSelectedItem());
isDirty = true;
}
public void setStage(Stage aStage) {
aStage.setOnCloseRequest(event -> {
saveIfNeeded();
});
}
}
The code that loads the FXML UI and the controller when a Term object is selected in a ListView
private ContextMenu getContextMenu(Term term) {
ContextMenu cm = new ContextMenu();
MenuItem editItem = new MenuItem("Edit");
cm.getItems().add(editItem);
editItem.setOnAction(event -> {
try{
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/TermEditor.fxml"));
fxmlLoader.setControllerFactory(c -> App.springContext.getBean(c)); // lookup the controller from the spring application context
Parent parent = fxmlLoader.load();
TermEditorCtrl termCtrl = fxmlLoader.getController();
Stage stage = new Stage();
Scene scene = new Scene(parent);
stage.setScene(scene);
stage.show();
termCtrl.setStage(stage);
termCtrl.setTerm(term);
}
catch(IOException e){
logger.debug(e.getLocalizedMessage());
e.printStackTrace();
}
});
The error message
javafx.fxml.LoadException:
/C:/Users/Alexander/Documents/agiletunes-codespace/agileTunes-implementation/target/classes/fxml/TermEditor.fxml
at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2601)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2579)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2441)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2409)
at com.agiletunes.controllers.requirement.PlanningPaneCtrl$TermTreeCell.lambda$0(PlanningPaneCtrl.java:674)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.control.MenuItem.fire(MenuItem.java:462)
at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.doSelect(ContextMenuContent.java:1405)
at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.lambda$createChildren$343(ContextMenuContent.java:1358)
at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.Scene$MouseHandler.process(Scene.java:3757)
at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3485)
at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)
at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2494)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:381)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$354(GlassViewEventHandler.java:417)
at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:416)
at com.sun.glass.ui.View.handleMouseEvent(View.java:555)
at com.sun.glass.ui.View.notifyMouse(View.java:937)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NullPointerException
at com.agiletunes.controllers.terms.TermEditorCtrl.initialize(TermEditorCtrl.java:68)
at com.agiletunes.controllers.terms.TermEditorCtrl$$FastClassBySpringCGLIB$$1861d384.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:669)
at com.agiletunes.controllers.terms.TermEditorCtrl$$EnhancerBySpringCGLIB$$bd8c26eb.initialize(<generated>)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2548)
... 45 more
Please let me know should you need additional information, code or alike
Thank you very much in advance for any help.
I debuged this further. Eventually I found the root cause in canApply(Pointcut pc, Class targetClass, boolean hasIntroductions) in AopUtils. The problem is that the model of the controller is set, calling the setTerm method. The parameter of setTerm is a Term object and Term is an abstract class. Spring has obviously a problem handling abstract parameters. I replaced the setTerm method with 5 different setter methods, for each concrete Term extensions one. Now it works.
I wonder whether this is a bug or a feature, since the intercept method in CglibAopProxy gets also a proxy object with all FXML variables set?!