io.datafx.provider.ObjectDataProvider Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of datasources Show documentation
Show all versions of datasources Show documentation
Visualizing Enterprise Data in JavaFX
/**
* Copyright (c) 2011, 2014, Jonathan Giles, Johan Vos, Hendrik Ebbers
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of DataFX, the website javafxdata.org, nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL DATAFX BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package io.datafx.provider;
import java.lang.reflect.Field;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.concurrent.Worker;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.EventHandler;
import io.datafx.io.DataReader;
import io.datafx.io.ServerSentEventReader;
import io.datafx.io.WritableDataReader;
import io.datafx.io.WriteBackHandler;
import io.datafx.io.WriteBackProvider;
import io.datafx.core.concurrent.ConcurrentUtils;
import io.datafx.core.concurrent.ObservableExecutor;
/**
*
* The ObjectDataProvider is an implementation of {@link DataProvider} that allows
* the retrieval and parsing of data that is represented as a single Java instance.
* In case a list of entities are expected to be retrieved, a {@link ListDataProvider}
* should be used. Instances of this class are typically used to populate Observable
* objects.
*
* This class requires a {@link io.datafx.io.DataReader} that either can be passed
* with the constructor or using the {@link #setDataReader(io.datafx.io.DataReader) }
* method.
*
* No external data will be retrieved until the {@link #retrieve()} method is called.
*
* Developers that prefer the builder approach can choose to use the
* {@link ObjectDataProviderBuilder} class to create an
* instance of ObjectDataProvider.
*
* Example:
*
* XmlConverter xmlConverter = new XmlConverter(MyEntity.class);
* RestSource restSource = new RestSource(HOST, xmlConverter);
* restSource.setPath("somepath");
* ObjectDataProvider<MyEntity> sodp = new ObjectDataProvider(restSource);
* sodp.setResultObjectProperty(model.myActiveEntity());
*
* @author johan
* @param the type of data this provider will deliver
*/
public class ObjectDataProvider implements DataProvider, WriteBackProvider{
private ObjectProperty objectProperty;
private Executor executor;
private DataReader reader;
private WriteBackHandler writeBackHandler;
private static final Logger LOGGER = Logger.getLogger(ObjectDataProvider.class.getName());
/**
*
* Create an ObjectDataProvider. Before calling the {@link #retrieve()} method, a
* {@link io.datafx.io.DataReader} instance should be set using
* {@link #setDataReader(io.datafx.io.DataReader) }.
*/
public ObjectDataProvider () {
this (null, null);
}
/**
* Create an ObjectDataProvider that will use the passed reader
for
* retrieving the data
* @param reader the source of the data.
*/
public ObjectDataProvider(DataReader reader) {
this(reader, null);
}
/**
* Create an ObjectDataProvider that will use the passed reader
for
* retrieving the data and the executor
for executing the request
* @param reader the source of the data.
* @param executor the Executor that will be used for doing the call to the
* data source. In case this parameter is null
, a new Thread will be used.
*/
public ObjectDataProvider(DataReader reader, Executor executor) {
this.reader = reader;
this.executor = executor;
this.objectProperty = new SimpleObjectProperty();
}
/**
* Set the DataReader that contains the data that will be provided by this
* ObjectDataProvider
* @param reader the source of the data.
*/
public void setDataReader (DataReader reader) {
this.reader = reader;
}
/**
*
* Explicitly set the {@link java.util.concurrent.Executor} that should be
* used for retrieving the data.
* @param executor the Executor instance that should be used when retrieving the data
*/
public void setExecutor(Executor executor) {
this.executor = executor;
}
/**
* Sets the ObjectProperty that contains the result of the data retrieval.
* This method should not be called once the
* retrieve
method has been called // TODO: enforce this
*
* @param result the Property that should be filled with the retrieved value
*/
@Override
public void setResultProperty(Property result) {
setResultObjectProperty((ObjectProperty) result);
}
/**
* Convenience (backward compatible) method. This method is the
* same as calling {@link #setResultProperty(javafx.beans.property.Property) }.
* @param result the Property that should be filled with the retrieved value
*/
public void setResultObjectProperty(ObjectProperty result) {
this.objectProperty = result;
}
@Override
public Worker retrieve() {
final Service retriever = createService(objectProperty);
return ConcurrentUtils.executeService(executor, retriever);
}
private void handleKeepReading (final ServerSentEventReader reader) {
Service retriever = createKeepReadingService(reader);
if (executor != null && executor instanceof ObservableExecutor) {
((ObservableExecutor) executor).submit(retriever);
} else {
if (executor != null) {
retriever.setExecutor(executor);
}
retriever.start();
}
}
private Service createKeepReadingService (final ServerSentEventReader reader) {
return new Service() {
@Override
protected Task createTask() {
return createKeepReadingTask(reader);
}
};
}
private Task createKeepReadingTask(final ServerSentEventReader reader) {
Task answer = new Task() {
@Override
protected Object call() throws Exception {
reader.keepReading();
return null;
}
};
return answer;
}
protected Task createReceiverTask(final DataReader reader) {
Task answer = new Task() {
@Override
protected T call() throws Exception {
final T entry = reader.get();
Platform.runLater(new Runnable() {
@Override
public void run() {
objectProperty.set(entry);
}
});
LOGGER.log(Level.FINE, "[datafx] Reader did read entry {0}", entry);
return entry;
}
};
return answer;
}
protected Service createService(ObjectProperty value) {
return new Service() {
@Override
protected Task createTask() {
LOGGER.fine("[datafx] create Receiver task");
final Task task = createReceiverTask(reader);
task.setOnSucceeded(new EventHandler() {
@Override
public void handle(WorkerStateEvent arg0) {
T value = null;
try {
LOGGER.fine("[datafx] get the value of the task");
value = task.get();
LOGGER.log(Level.FINE, "[datafx] task returned value {0}", value);
} catch (InterruptedException e) {
e.printStackTrace();
// Execution of the task was not working. So we do
// not need
// to update the property
return;
} catch (ExecutionException e) {
e.printStackTrace();
// Execution of the task was not working. So we do
// not need
// to update the property
return;
}
LOGGER.log(Level.FINER, "[datafx] I will set the value of {0} to {1}", new Object[]{objectProperty, value});
// objectProperty.set(value);
System.out.println("DONE settting value");
LOGGER.log(Level.FINER, "Do we have a writeBackHandler? {0}", writeBackHandler);
if (writeBackHandler != null) {
checkProperties(value);
}
if (reader instanceof ServerSentEventReader){
handleKeepReading ((ServerSentEventReader)reader);
}
}
});
return task;
}
};
}
@Override
public ObjectProperty getData() {
return objectProperty;
}
@Override
public void setWriteBackHandler(WriteBackHandler handler) {
this.writeBackHandler = handler;
}
private void checkProperties(final T target) {
Class c = target.getClass();
Field[] fields = c.getDeclaredFields();
for (final Field field : fields) {
Class clazz = field.getType();
if (Observable.class.isAssignableFrom(clazz)) {
try {
Observable observable = AccessController.doPrivileged(new PrivilegedAction() {
public Observable run() {
try {
field.setAccessible(true);
Object f = field.get(target);
Observable answer = (Observable) f;
return answer;
} catch (IllegalArgumentException ex) {
Logger.getLogger(ObjectDataProvider.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
Logger.getLogger(ObjectDataProvider.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
});
if (observable != null) {
observable.addListener(new InvalidationListener() {
@Override
public void invalidated(final Observable o) {
final WritableDataReader reader = writeBackHandler.createDataSource(objectProperty.get());
Service service = new Service() {
@Override
protected Task createTask() {
Task task = new Task() {
@Override
protected Object call() throws Exception {
reader.writeBack();
return o;
}
};
return task;
}
};
if (executor != null) {
service.setExecutor(executor);
}
service.start();
}
});
}
} catch (IllegalArgumentException ex) {
Logger.getLogger(ObjectDataProvider.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
}