Own implementation of DefaultFXMLControllerBuilder for FXMLController creation via spring

42 views
Skip to first unread message

Andreas

unread,
Aug 19, 2015, 12:22:14 PM8/19/15
to JRebirth Users
Hello Seb

I want to use spring for creating/resolving FXMLController implementations which then able to use autowire, etc. pp.

But at the moment the  DefaultFXMLControllerBuilder class creates the instances. I would like to adapt this class where "controllerClass.newInstance()" will be replaced by a spring resolve.
  
    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    @Override
    public Object call(final Class<?> controllerClass) {
        FXMLController<Model, View<Model, ?, ?>> controller = null;
        try {

            controller = (FXMLController<Model, View<Model, ?, ?>>) controllerClass.newInstance();
            controller.setModel(this.relatedModel);

        } catch (InstantiationException | IllegalAccessException e) {
            LOGGER.log(FXMLMessages.DEFAULT_CTRLR_CREATION_ERROR, e, e.getMessage());
        }

        return controller;
    }

Is any way provided to replace/override the default implementation - because the class contains in it's naming "Default" :).

Thanks a lot in advance

Regards
Andreas

Sebastien Bordes

unread,
Aug 19, 2015, 12:49:03 PM8/19/15
to jrebirth-users

Hi Andreas,
Basically JRebirth is designed to dont need anything from spring, but you are right concerning the naming of the class.
I will add tonight a parameter to let the possibility to add your own builder.
BR

Seb

--
Vous recevez ce message, car vous êtes abonné au groupe Google Groupes "JRebirth Users".
Pour vous désabonner de ce groupe et ne plus recevoir d'e-mails le concernant, envoyez un e-mail à l'adresse jrebirth-user...@googlegroups.com.
Pour obtenir davantage d'options, consultez la page https://groups.google.com/d/optout.

Sebastien Bordes

unread,
Aug 19, 2015, 4:58:18 PM8/19/15
to jrebirth-users

A build is running (8.0.3-SNAPSHOT)

Let me know if you experience some troubles with this patch

Sebastien Bordes

unread,
Aug 20, 2015, 3:54:02 AM8/20/15
to jrebirth-users
To use your own FXMLControllerFactory, you have to define this property into jrebirth.properties file:

fxmlControllerFactory=com.sample.package.MyFXMLControllerFactory

Basically this class call shall implement Callback<Class<?>, Object> but if you need the Model instance you can implement FXMLControllerFactory or extend AbstractFXMLControllerFactory.

Seb

Andreas

unread,
Aug 20, 2015, 5:57:19 AM8/20/15
to JRebirth Users
Hi Seb,

thanks a lot for your fast solution. But, if I create my own implementation and set the property I get following exception:

JTP Slot 1 ERROR [org.jrebirth.af.core.concurrent.JRebirthThreadPoolExecutor] - JTP returned an error
org.jrebirth.af.api.exception.CoreRuntimeException: Impossible to load class net.neobp.play.gui.jrebirth.FXMLControllerFactory
    at org.jrebirth.af.core.resource.parameter.ObjectParameter.parseClassParameter(ObjectParameter.java:136)
    at org.jrebirth.af.core.resource.parameter.ObjectParameter.parseObject(ObjectParameter.java:110)
    at org.jrebirth.af.core.resource.parameter.ParameterBuilder.buildResource(ParameterBuilder.java:226)
    at org.jrebirth.af.core.resource.parameter.ParameterBuilder.buildResource(ParameterBuilder.java:46)
    at org.jrebirth.af.core.resource.builder.AbstractResourceBuilder.get(AbstractResourceBuilder.java:96)
    at org.jrebirth.af.core.resource.builder.AbstractResourceBuilder.get(AbstractResourceBuilder.java:41)
    at org.jrebirth.af.api.resource.parameter.ParameterItem.get(ParameterItem.java:63)
    at org.jrebirth.af.core.util.ParameterUtility.buildCustomizableClass(ParameterUtility.java:58)
    at org.jrebirth.af.core.ui.fxml.FXMLUtils.loadFXML(FXMLUtils.java:98)
    at org.jrebirth.af.core.ui.fxml.AbstractFXMLObjectModel.initInternalModel(AbstractFXMLObjectModel.java:117)
    at org.jrebirth.af.core.ui.AbstractBaseModel.ready(AbstractBaseModel.java:56)
    at org.jrebirth.af.core.component.basic.AbstractComponent.setup(AbstractComponent.java:503)
    at org.jrebirth.af.core.facade.AbstractFacade.retrieve(AbstractFacade.java:216)
    at org.jrebirth.af.core.command.basic.showmodel.PrepareModelCommand.perform(PrepareModelCommand.java:56)
    at org.jrebirth.af.core.command.AbstractBaseCommand.innerRun(AbstractBaseCommand.java:181)
    at org.jrebirth.af.core.command.CommandRunnable.runInto(CommandRunnable.java:67)
    at org.jrebirth.af.core.concurrent.AbstractJrbRunnable.run(AbstractJrbRunnable.java:80)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.ClassNotFoundException: net.neobp.play.gui.jrebirth.FXMLControllerFactory
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Unknown Source)
    at org.jrebirth.af.core.resource.parameter.ObjectParameter.parseClassParameter(ObjectParameter.java:134)
    ... 19 more

excerpt of jrebirth.properites

## The factory used to create FXML controllers
fxmlControllerFactory=net.neobp.play.gui.jrebirth.FXMLControllerFactory

the custom controller factory

package net.neobp.play.gui.jrebirth;

import org.jrebirth.af.api.log.JRLogger;
import org.jrebirth.af.api.ui.Model;
import org.jrebirth.af.api.ui.View;
import org.jrebirth.af.api.ui.fxml.FXMLController;
import org.jrebirth.af.core.log.JRLoggerFactory;
import org.jrebirth.af.core.ui.fxml.AbstractFXMLControllerFactory;
import org.jrebirth.af.core.ui.fxml.FXMLMessages;

import net.neobp.play.gui.application.ApplicationContextUtil;

public class FXMLControllerFactory extends AbstractFXMLControllerFactory {

    /** The class logger. */
    private static final JRLogger LOGGER = JRLoggerFactory.getLogger(FXMLControllerFactory.class);

    @Override
    public Object call(Class<?> controllerClass) {


        FXMLController<Model, View<Model, ?, ?>> controller = null;
        try {
            controller = (FXMLController<Model, View<Model, ?, ?>>) ApplicationContextUtil.GetContext().getBean(controllerClass);
            controller.setModel(getRelatedModel());

        } catch (Exception e) {

            LOGGER.log(FXMLMessages.DEFAULT_CTRLR_CREATION_ERROR, e, e.getMessage());
        }

        return controller;
    }

}

Regards
Andreas

Sebastien Bordes

unread,
Aug 20, 2015, 8:53:14 AM8/20/15
to jrebirth-users
I have performed some tests and it works well with Showcase FXML demo.

The error thrown says that your new Factory class cannot be loaded (Class.forName() is used) are you sure that the class is available into the classpath ?

Seb

Andreas

unread,
Aug 21, 2015, 3:55:26 AM8/21/15
to JRebirth Users
Moin Seb

after 1-2 hours playing with the project settings in eclipse and rebuild of the relevant projects its working now. Unfortunetaly, I don't know the cause - but no matter.

Have a great weekend!

Andreas

Sebastien Bordes

unread,
Aug 21, 2015, 3:56:59 AM8/21/15
to jrebirth-users
Great !

Let me know if you need something else !

Seb

Andreas

unread,
Sep 15, 2015, 6:04:58 AM9/15/15
to JRebirth Users
Hello Seb,

as you know, we use our own implementation for creating controllers (FXMLControllerFactory - see below). I implemented a common navigation approach (to support module based application) and create a class, where the current data context (global) can be set (selected order, notification etc.).

If I navigate the first time to the view(s), all is working fine. If I change the module once and come back to the first module views, the data context selection failes with an exception.

I found, that the FXMLModel of FMXLController seams be finalized/destructed (partially) in the meantime, because the call model.sendWave failes internally, because the LocalFacade is null now.

    private Wave createWave(final WaveGroup waveGroup, final WaveType waveType, final Class<?> componentClass, final WaveData<?>... waveData) {

        final Wave wave = wave()
                                .waveGroup(waveGroup)
                                .waveType(waveType)
                                .fromClass(this.getClass())
                                .componentClass(componentClass)
                                .addDatas(waveData);

        // Track wave creation
        // getLocalFacade is null
        getLocalFacade().getGlobalFacade().trackEvent(JRebirthEventType.CREATE_WAVE, this.getClass(), wave.getClass());

        return wave;
    }

Here an excerpt of data context class:

public class DataContext {

    private static OrderHeaderViewTransfer _orderContext;

    final public void setOrderContext(final OrderHeaderViewTransfer orderContext, Model sourceModel) {
        _orderContext = orderContext;
        final WaveData<Object> waveData = Builders.waveData(NmsWaves.DATACONTEXT, new Object());
        sourceModel.sendWave(NmsWaves.DATA_CONTEXT_CHANGED, waveData);
    }

    final public OrderHeaderViewTransfer GetOrderContext() {
        return _orderContext;
...
    }


Here the call (sample) in controller to set the context (change listener for selecting an element of list)

    private class DefaultListChangeListener<Object> implements ChangeListener<OrderHeaderViewTransfer> {
        final private Model _model;
        final private DataContext _dataContext;

        public DefaultListChangeListener(Model model, DataContext dataContext) {
            _model = model;
            _dataContext = dataContext;
        }

        @Override
        public void changed(ObservableValue<? extends OrderHeaderViewTransfer> observable,
                OrderHeaderViewTransfer oldSelection, OrderHeaderViewTransfer newSelection) {
            System.err.println(newSelection);
            if (oldSelection != null) {
                if (orderHeaderService.isDirty(oldSelection.getModelDelegate()))
                    orderHeaderService.save(oldSelection.getModelDelegate());
            }

            _dataContext.setOrderContext(newSelection, _model);
        }
    }


Because of our own FXMLControllerFactory all controllers are singleton instances and the corresponding models should be valid as long as the application is running.

When the models will be disposed? Do you have any idea how I can keep the models valid?

Thanks a lot in advance!

Regards
Andreas

Sebastien Bordes

unread,
Sep 15, 2015, 7:44:35 AM9/15/15
to jrebirth-users
Hi Andreas,

By default model are released when the root Node of their view has been removed from the scene.


If you want to keep the model into memory during all the life of the application you can add this method into the Model class:

@Releasable
public boolean canRelease() {
    return false;
}

The method name is not significant, but the annotation is obviously mandatory and shall return a boolean.

Why don't you store your DataContext into a Service, it's a component too that can send wave, You just have to strongly reference it somewhere to avoid any release (there isn't any auto release for Service).


Seb

Andreas

unread,
Sep 15, 2015, 7:59:05 AM9/15/15
to JRebirth Users
Hello Seb,

great!

I will think about your advice to use a service for the data context.

Thanks a lot for your fast reply and solution!

Regards
Andreas
Reply all
Reply to author
Forward
0 new messages