JavaFX for server-side image generation - java

This could sound strange but I want to generate my chart images on server side using JavaFX. Because JavaFX has nice canvas API to perform image transformations joins and positioning.
In particular I have a spring MVC service to generate my charts as images.
The main problem is how to invoke javaFX API from a convenient Spring bean.
If I try to just run javafx code from java application (not extending javaFX Application class) I get
java.lang.IllegalStateException: Toolkit not initialized
Do you have any suggestions/ideas how to solve this issue?

So after some research I've implemented canvas draw with JavaFX and here is a simplified example:
First I made the JavaFX application which is being launched in a separate thread (I use Spring taskExecutor but a plain java thread can be used).
public class ChartGenerator extends Application {
private static Canvas canvas;
private static volatile byte[] result;
public static void initialize(TaskExecutor taskExecutor) {
taskExecutor.execute(new Runnable() {
#Override
public void run() {
launch(ChartGenerator.class);
}
});
}
public static synchronized byte[] generateChart(final Object... params) {
Platform.runLater(new Runnable() {
#Override
public void run() {
ByteArrayOutputStream baos = null;
try {
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
/**
* Do the work with canvas
**/
final SnapshotParameters snapshotParameters = new SnapshotParameters();
snapshotParameters.setFill(Color.TRANSPARENT);
WritableImage image = canvas.snapshot(snapshotParameters, null);
BufferedImage bImage = SwingFXUtils.fromFXImage(image, null);
baos = new ByteArrayOutputStream();
ImageIO.write(bImage, chartType.outputFormat, baos);
result = baos.toByteArray();
} catch (InstantiationException e) {
throw new ChartGenerationException(e);
} catch (IllegalAccessException e) {
throw new ChartGenerationException(e);
} catch (NoSuchMethodException e) {
throw new ChartGenerationException(e);
} catch (InvocationTargetException e) {
throw new ChartGenerationException(e);
} catch (IOException e) {
throw new ChartGenerationException(e);
} finally {
IOUtils.closeQuietly(baos);
}
}
});
while (result == null) {
//wait
}
byte[] ret = result;
result = null;
return ret;
}
#Override
public void start(Stage stage) {
canvas = new Canvas();
}
public static class ChartGenerationException extends RuntimeException {
public ChartGenerationException(String message) {
super(message);
}
public ChartGenerationException(Throwable cause) {
super(cause);
}
}
}
Then I call the initialize() method when the Spring application is started:
#Autowired private TaskExecutor taskExecutor;
#PostConstruct private void initChartGenerator() {
ChartGenerator.initialize(taskExecutor);
}
This solution of cource can be ported to a non-Spring application.
This is a single-threaded solution (in my case it's enough) but I think it could be adopted to multithreaded usage (maybe use RMI to invoke draw method).
Also this solution works "as is" on my windows workstation but on linux server environment some additional actions should be invoked:
You cannot use JavaFX on OpenJDK (as of Aug 2013) - have to switch to Oracle JDK
Java version must be no less than Java 7u6
The most complex - you have to use virtual display to make JavaFX run on headless environments:
apt-get install xvfb
// then on application server start:
export DISPLAY=":99"
start-stop-daemon --start --background --user jetty --exec "/usr/bin/sudo" -- -u jetty /usr/bin/Xvfb :99 -screen 0 1024x768x24
P.S. You can also use other JavaFX capabilities on server side (e.g. export html to image) with this solution.

In case other people are looking for this, this is a much simpler way.
Using JavaFX 2.2 i was able to perform the following operations.
waitForInit = new Semaphore(0);
root = new Group();
root.getChildren().add(jfxnode);
FxPlatformExecutor.runOnFxApplication(() -> {
snapshot = jfxnode.snapshot(new SnapshotParameters(), null);
waitForInit.release();
});
waitForInit.acquireUninterruptibly();
BufferedImage bi = SwingFXUtils.fromFXImage(snapshot, null);
There is no need to add the node to a group.
From there you can do any operation you want with the image.
The FxPlatformExecutor is from a JME3-JFX library I am using for my project.
See: https://github.com/empirephoenix/JME3-JFX/blob/master/src/main/java/com/jme3x/jfx/FxPlatformExecutor.java
You can easily create the runOnFxApplication() method or create the FxPlatformExecutor class.
Here is the code.
package com.jme3x.jfx;
import javafx.application.Platform;
/**
* TODO This Class should be replaced by some Workmanager implemntation
* in the future
* #author Heist
*/
public class FxPlatformExecutor {
public static void runOnFxApplication(Runnable task) {
if (Platform.isFxApplicationThread()) {
task.run();
} else {
Platform.runLater(task);
}
}
}
I did not write this code, the github link is above.

Perhaps something similar to this solution would be helpful?
JavaFX 2.1: Toolkit not initialized
Otherwise, I would consider creating a service and pushing the image to a datastore and retrieving it in your spring application.
Hope that provides at least a little help!

Related

Flickering Annotations in WorldWind Java

I'm trying to implement my own clutter filter in NASA Worldwind for Java and its causing a weird problem -- the clutter filter isn't doing much yet, but I will use it to move things around when I get passed the "flickering" issue. Whenever the mouse is moved the GlobeAnnotation renderables are flickering. When I have the clutter filter set to null, the flickering does not seem to occur.
Here is a GIF that shows what I mean: https://media.giphy.com/media/xT9IgFiZwYZ3VJHQU8/giphy.gif
I've cloned the NASA worldwind code from here: https://github.com/NASAWorldWind/WorldWindJava. I've made a couple of changes to make things work for my eventual filter. One note is that I want the GlobeAnnotations to appear as Always On Top of everything else.
How can I make the GlobeAnnotations not fight with each other and flicker, but still appear on top of everything else -- while having the Clutter Filter turned on?
Note that the following code is just an example I put together to show the issue that I'm seeing in my "real" application. I want the GlobeAnnotations to always be on top of everything else -- but not flickering and fighting with each other.
Here is my test driver:
package gov.nasa.worldwindx.examples;
import java.awt.Color;
import gov.nasa.worldwind.geom.LatLon;
import gov.nasa.worldwind.geom.Position;
import gov.nasa.worldwind.layers.AnnotationLayer;
import gov.nasa.worldwind.layers.RenderableLayer;
import gov.nasa.worldwind.render.GlobeAnnotation;
import gov.nasa.worldwind.render.Material;
import gov.nasa.worldwind.render.airspaces.CappedCylinder;
public class FlashyAnnotations extends ApplicationTemplate {
#SuppressWarnings("unchecked")
private static class AppFrame extends ApplicationTemplate.AppFrame {
private AnnotationLayer layer;
public AppFrame() {
this.getWwd().getSceneController().setClutterFilter(new SimpleClutterFilter());
CappedCylinder cappedCyl = new CappedCylinder(LatLon.fromDegrees(27, -100), 3000000);
cappedCyl.getAttributes().setDrawInterior(true);
cappedCyl.getAttributes().setInteriorMaterial(Material.GREEN);
cappedCyl.getAttributes().setInteriorOpacity(.75f);
cappedCyl.setAltitudes(10, 100000);
RenderableLayer renderLayer = new RenderableLayer();
renderLayer.addRenderable(cappedCyl);
insertBeforeCompass(this.getWwd(), renderLayer);
// Create example annotations
this.setupAnnotations();
}
private void setupAnnotations() {
// Create an AnnotationLayer with lots of annotations
this.layer = new AnnotationLayer();
GlobeAnnotation ga = new GlobeAnnotation("Annotation", Position.fromDegrees(20, -100.9, 1000));
ga.getAttributes().setTextColor(Color.white);
ga.getAttributes().setBackgroundColor(Color.BLACK);
ga.getAttributes().setOpacity(.75f);
ga.setAlwaysOnTop(true);
layer.addAnnotation(ga);
ga = new GlobeAnnotation("Annotation", Position.fromDegrees(25, -100.9, 1000));
ga.getAttributes().setTextColor(Color.white);
ga.getAttributes().setBackgroundColor(Color.BLACK);
ga.getAttributes().setOpacity(.75f);
ga.setAlwaysOnTop(true);
layer.addAnnotation(ga);
// Add layer to the layer list and update the layer panel
insertBeforeCompass(this.getWwd(), layer);
}
}
public static void main(String[] args) {
ApplicationTemplate.start("WorldWind Annotations", AppFrame.class);
}
}
Here is my (essentially no-op) Clutter Filter:
package gov.nasa.worldwindx.examples;
import java.util.List;
import gov.nasa.worldwind.render.Declutterable;
import gov.nasa.worldwind.render.DrawContext;
import gov.nasa.worldwind.util.ClutterFilter;
public class SimpleClutterFilter implements ClutterFilter{
#Override
public void apply(DrawContext dc, List<Declutterable> shapes) {
for(Declutterable shape: shapes) {
dc.addOrderedRenderable(shape);
}
}
}
And I also had to update the gov.nasa.worldwind.render.BasicAnnotationRenderer to have the OrderedAnnotations it creates implement Declutterable. (The only change to this inner class was adding isEnableDecluttering and getBounds):
public class OrderedAnnotation implements OrderedRenderable, Declutterable
{
protected Annotation annotation;
protected double eyeDistance;
protected Layer layer;
public OrderedAnnotation(Annotation annotation, double eyeDistance)
{
this.annotation = annotation;
this.eyeDistance = eyeDistance;
}
public OrderedAnnotation(Annotation annotation, Layer layer, double eyeDistance)
{
this.annotation = annotation;
this.eyeDistance = eyeDistance;
this.layer = layer;
}
public double getDistanceFromEye()
{
return this.eyeDistance;
}
public void render(DrawContext dc)
{
OGLStackHandler stackHandler = new OGLStackHandler();
BasicAnnotationRenderer.this.beginDrawAnnotations(dc, stackHandler);
try
{
this.doRender(dc, this);
// Draw as many as we can in a batch to save ogl state switching.
while (dc.peekOrderedRenderables() instanceof OrderedAnnotation)
{
OrderedAnnotation oa = (OrderedAnnotation) dc.pollOrderedRenderables();
this.doRender(dc, oa);
}
}
catch (WWRuntimeException e)
{
Logging.logger().log(Level.SEVERE, "generic.ExceptionWhileRenderingAnnotation", e);
}
catch (Exception e)
{
Logging.logger().log(Level.SEVERE, "generic.ExceptionWhileRenderingAnnotation", e);
}
finally
{
BasicAnnotationRenderer.this.endDrawAnnotations(dc, stackHandler);
}
}
public void pick(DrawContext dc, java.awt.Point pickPoint)
{
OGLStackHandler stackHandler = new OGLStackHandler();
BasicAnnotationRenderer.this.pickSupport.clearPickList();
BasicAnnotationRenderer.this.beginDrawAnnotations(dc, stackHandler);
try
{
this.annotation.setPickSupport(BasicAnnotationRenderer.this.pickSupport);
this.doRender(dc, this);
// Draw as many as we can in a batch to save ogl state switching.
while (dc.peekOrderedRenderables() instanceof OrderedAnnotation)
{
OrderedAnnotation oa = (OrderedAnnotation) dc.pollOrderedRenderables();
oa.annotation.setPickSupport(BasicAnnotationRenderer.this.pickSupport);
this.doRender(dc, oa);
}
}
catch (WWRuntimeException e)
{
Logging.logger().log(Level.SEVERE, "generic.ExceptionWhilePickingAnnotation", e);
}
catch (Exception e)
{
Logging.logger().log(Level.SEVERE, "generic.ExceptionWhilePickingAnnotation", e);
}
finally
{
BasicAnnotationRenderer.this.endDrawAnnotations(dc, stackHandler);
BasicAnnotationRenderer.this.pickSupport.resolvePick(dc, pickPoint, this.layer);
BasicAnnotationRenderer.this.pickSupport.clearPickList(); // to ensure entries can be garbage collected
}
}
protected void doRender(DrawContext dc, OrderedAnnotation oa)
{
// Swap the draw context's current layer with that of the ordered annotation
Layer previousCurrentLayer = dc.getCurrentLayer();
try
{
dc.setCurrentLayer(oa.layer);
oa.annotation.renderNow(dc);
}
finally
{
dc.setCurrentLayer(previousCurrentLayer); // restore the original layer
}
}
#Override
public boolean isEnableDecluttering() {
return (annotation instanceof GlobeAnnotation);
}
#Override
public Rectangle2D getBounds(DrawContext dc) {
if(annotation instanceof GlobeAnnotation) {
return ((GlobeAnnotation) annotation).computeBounds(dc);
}
return null;
}
}
First of all;
Draw order of PointPlacemarks
https://forum.worldwindcentral.com/forum/world-wind-java-forums/development-help/13263-layer-priority-order
In setupAnnotations method, you set alwaysOnTop as true for both GlobeAnnotation objects. This might be the reason.
private void setupAnnotations() {
// Create an AnnotationLayer with lots of annotations
this.layer = new AnnotationLayer();
GlobeAnnotation ga = new GlobeAnnotation("Annotation", Position.fromDegrees(20, -100.9, 1000));
ga.getAttributes().setTextColor(Color.white);
ga.getAttributes().setBackgroundColor(Color.BLACK);
ga.getAttributes().setOpacity(.75f);
**ga.setAlwaysOnTop(true);**
layer.addAnnotation(ga);
ga = new GlobeAnnotation("Annotation", Position.fromDegrees(25, -100.9, 1000));
ga.getAttributes().setTextColor(Color.white);
ga.getAttributes().setBackgroundColor(Color.BLACK);
ga.getAttributes().setOpacity(.75f);
**ga.setAlwaysOnTop(true);**
layer.addAnnotation(ga);
// Add layer to the layer list and update the layer panel
insertBeforeCompass(this.getWwd(), layer);
}
Instead of that, putting annotations that you want to be always on top into separate layer and remaining ones into another layer might be solution by using the links above.

JavaFX thread issue

i'm using thread to resolve the problem of GUI freeze. But with thread i'm facing a problem that i'm unable to pass format of the report as argument in run method or even with the help of constructor i'm unable to do it.....
public class BirtReportExportCon implements Runnable {
#FXML
Button exportButton;
#FXML
CheckBox pdfCheckBox;
#FXML
CheckBox xlsCheckBox;
#FXML
CheckBox docCheckBox;
#FXML
CheckBox mailCheckBox;
public String fileFormat;
Allow to Check Single CheckBox on Gui
public void eventCheckBoxPdf() {
if (pdfCheckBox.isSelected() == true) {
xlsCheckBox.setSelected(false);
docCheckBox.setSelected(false);
}
}
public void eventCheckBoxXls() {
if (xlsCheckBox.isSelected() == true) {
pdfCheckBox.setSelected(false);
docCheckBox.setSelected(false);
}
}
public void eventCheckBoxDoc() {
if (docCheckBox.isSelected() == true) {
pdfCheckBox.setSelected(false);
xlsCheckBox.setSelected(false);
}
}
Provide the Chosen fileFormat
public void onButtonClick() throws EngineException {
if (docCheckBox.isSelected() == true) {
fileFormat = "docx"; // I WANT THIS FILE FORMAT IN MY RUN METHOD
Runnable r = new BirtReportExportCon();
new Thread(r).start();
}
else if (pdfCheckBox.isSelected() == true) {
fileFormat = "pdf";
Runnable r = new BirtReportExportCon();
new Thread(r).start();
}
else if (xlsCheckBox.isSelected() == true) {
fileFormat = "xls";
Runnable r = new BirtReportExportCon();
new Thread(r).start();
}
}
Run Method
public void run()
{
try
{
exportFile(fileFormat); // HERE I WANT THAT SO I CAN ABLE TO CREATE REPORT OF REQUIRED FORMAT
}
catch (EngineException e) {
e.printStackTrace();
}
}
save report and open the report
public void exportFile(String fileFormat) throws EngineException {
String output = "output path";
String reportDesignFilePath = "report path";
try {
EngineConfig configure = new EngineConfig();
Platform.startup(configure);
IReportEngineFactory reportEngineFactory = (IReportEngineFactory) Platform
.createFactoryObject(IReportEngineFactory.EXTENSION_REPORT_ENGINE_FACTORY);
IReportEngine engine = reportEngineFactory.createReportEngine(configure);
engine.changeLogLevel(Level.WARNING);
IReportRunnable runnable = engine.openReportDesign(reportDesignFilePath);
IRunAndRenderTask task = engine.createRunAndRenderTask(runnable);
IRenderOption option = new PDFRenderOption();
option.setOutputFormat(fileFormat);
option.setOutputFileName(output + fileFormat);
task.setRenderOption(option);
task.run();
task.close();
} catch (Exception e) {
e.printStackTrace();
}
// Open Created File
File fileOpen = new File(output + fileFormat);
if (fileOpen.exists()) {
if (Desktop.isDesktopSupported()) {
try {
Desktop desktop = Desktop.getDesktop();
desktop.open(fileOpen);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
I had a similar problem like this. I think the problem lies in the fileOpening stage. The Desktop class you are using comes from java.awt package.When you use the Desktop class then the JAVAFX thread gets blocked as commented by a user in the link given at the bottom of this answer. But the user has a low reputation (only 11)so we cannot rely on him.
To make your application unfreeze, you will have to create a new Thread.
Here is a part of my code, i used in my application and this code worked perfectly. I have also put a link to a github issue of my application where i stated the freezing problem, similar to yours. The issue was created 2 days ago.
#FXML
void openWithAction(ActionEvent event) {
boolean flag = false;
Task task = new Task<Void>() {
#Override
protected Void call() throws Exception {
try {
Desktop.getDesktop().open(new File(fileModel.getFileLocation()));
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
};
new Thread(task).start();
}
Github issue link:
https://github.com/karanpant/SearchEverything/issues/3
I also suggest you to use concurrency provided by JavaFX.
Here is the other SO post link. Hope this helps.
JavaFX Freeze on Desktop.open(file), Desktop.browse(uri)
EDIT: I am sorry if i don't understand your question . Is your question about application freezing or about not being able to pass a parameter or about not being able to pass a parameter because of application freezing.
Try something like this:
if ( docCheckBox.isSelected() == true ) {
BirtReportExportCon r = new BirtReportExportCon();
r.fileFormat = "docx"; // I WANT THIS FILE FORMAT IN MY RUN METHOD
new Thread(r).start();
}
You should run this code on the Swing thread instead of calling it from the Java FX thread. Like the following:
#FXML
void openWithAction(ActionEvent event) {
SwingUtilities.invokeLater( () -> Desktop.getDesktop().
open(new File(fileModel.
getFileLocation())));
}

Concurrency on Vertx

i have joined to one of those Vertx lovers , how ever the single threaded main frame may not be working for me , because in my server there might be 50 file download requests at a moment , as a work around i have created this class
public abstract T onRun() throws Exception;
public abstract void onSuccess(T result);
public abstract void onException();
private static final int poolSize = Runtime.getRuntime().availableProcessors();
private static final long maxExecuteTime = 120000;
private static WorkerExecutor mExecutor;
private static final String BG_THREAD_TAG = "BG_THREAD";
protected RoutingContext ctx;
private boolean isThreadInBackground(){
return Thread.currentThread().getName() != null && Thread.currentThread().getName().equals(BG_THREAD_TAG);
}
//on success will not be called if exception be thrown
public BackgroundExecutor(RoutingContext ctx){
this.ctx = ctx;
if(mExecutor == null){
mExecutor = MyVertxServer.vertx.createSharedWorkerExecutor("my-worker-pool",poolSize,maxExecuteTime);
}
if(!isThreadInBackground()){
/** we are unlocking the lock before res.succeeded , because it might take long and keeps any thread waiting */
mExecutor.executeBlocking(future -> {
try{
Thread.currentThread().setName(BG_THREAD_TAG);
T result = onRun();
future.complete(result);
}catch (Exception e) {
GUI.display(e);
e.printStackTrace();
onException();
future.fail(e);
}
/** false here means they should not be parallel , and will run without order multiple times on same context*/
},false, res -> {
if(res.succeeded()){
onSuccess((T)res.result());
}
});
}else{
GUI.display("AVOIDED DUPLICATE BACKGROUND THREADING");
System.out.println("AVOIDED DUPLICATE BACKGROUND THREADING");
try{
T result = onRun();
onSuccess((T)result);
}catch (Exception e) {
GUI.display(e);
e.printStackTrace();
onException();
}
}
}
allowing the handlers to extend it and use it like this
public abstract class DefaultFileHandler implements MyHttpHandler{
public abstract File getFile(String suffix);
#Override
public void Handle(RoutingContext ctx, VertxUtils utils, String suffix) {
new BackgroundExecutor<Void>(ctx) {
#Override
public Void onRun() throws Exception {
File file = getFile(URLDecoder.decode(suffix, "UTF-8"));
if(file == null || !file.exists()){
utils.sendResponseAndEnd(ctx.response(),404);
return null;
}else{
utils.sendFile(ctx, file);
}
return null;
}
#Override
public void onSuccess(Void result) {}
#Override
public void onException() {
utils.sendResponseAndEnd(ctx.response(),404);
}
};
}
and here is how i initialize my vertx server
vertx.deployVerticle(MainDeployment.class.getCanonicalName(),res -> {
if (res.succeeded()) {
GUI.display("Deployed");
} else {
res.cause().printStackTrace();
}
});
server.requestHandler(router::accept).listen(port);
and here is my MainDeployment class
public class MainDeployment extends AbstractVerticle{
#Override
public void start() throws Exception {
// Different ways of deploying verticles
// Deploy a verticle and don't wait for it to start
for(Entry<String, MyHttpHandler> entry : MyVertxServer.map.entrySet()){
MyVertxServer.router.route(entry.getKey()).handler(new Handler<RoutingContext>() {
#Override
public void handle(RoutingContext ctx) {
String[] handlerID = ctx.request().uri().split(ctx.currentRoute().getPath());
String suffix = handlerID.length > 1 ? handlerID[1] : null;
entry.getValue().Handle(ctx, new VertxUtils(), suffix);
}
});
}
}
}
this is working just fine when and where i need it , but i still wonder if is there any better way to handle concurencies like this on vertx , if so an example would be really appreciated . thanks alot
I don't fully understand your problem and reasons for your solution. Why don't you implement one verticle to handle your http uploads and deploy it multiple times? I think that handling 50 concurrent uploads should be a piece of cake for vert.x.
When deploying a verticle using a verticle name, you can specify the number of verticle instances that you want to deploy:
DeploymentOptions options = new DeploymentOptions().setInstances(16);
vertx.deployVerticle("com.mycompany.MyOrderProcessorVerticle", options);
This is useful for scaling easily across multiple cores. For example you might have a web-server verticle to deploy and multiple cores on your machine, so you want to deploy multiple instances to take utilise all the cores.
http://vertx.io/docs/vertx-core/java/#_specifying_number_of_verticle_instances
vertx is a well-designed model so that a concurrency issue does not occur.
generally, vertx does not recommend the multi-thread model.
(because, handling is not easy.)
If you select multi-thread model, you have to think about shared data..
Simply, if you just only want to split EventLoop Area,
first of all, you make sure Check your a number of CPU Cores.
and then Set up the count of Instances .
DeploymentOptions options = new DeploymentOptions().setInstances(4);
vertx.deployVerticle("com.mycompany.MyOrderProcessorVerticle", options);
But, If you have 4cores of CPU, you don't set up over 4 instances.
If you set up to number four or more, the performance won't improve.
vertx concurrency reference
http://vertx.io/docs/vertx-core/java/

Vaadin and Spring with Touchkit. servlet and annotation based?

I have a fully working spring and vaadin application based off spring boot. The application class has now been modified to create a custom servlet so I can use both touchkit and spring within the project as such.
I have been following this git project to perform this:git project example
public class SmartenderApplication {
public static void main(String[] args) {
SpringApplication.run(SmartenderApplication.class, args);
}
#Bean
public VaadinServlet vaadinServlet() {
return new SpringAwareTouchKitServlet();
}}
I modified the custom servlet to follow the vaadin docs for using a UI provider to choose between the touchkit UI and the browswer fallback UI as so
public class SpringAwareTouchKitServlet extends SpringVaadinServlet {
TouchKitSettings touchKitSettings;
MyUIProvider prov = new MyUIProvider();
#Override
protected void servletInitialized() throws ServletException {
super.servletInitialized();
getService().addSessionInitListener(
new SessionInitListener() {
#Override
public void sessionInit(SessionInitEvent event)
throws ServiceException {
event.getSession().addUIProvider(prov);
}
});
touchKitSettings = new TouchKitSettings(getService());
}
}
class MyUIProvider extends UIProvider {
#Override
public Class<? extends UI>
getUIClass(UIClassSelectionEvent event) {
String ua = event.getRequest()
.getHeader("user-agent").toLowerCase();
if ( ua.toLowerCase().contains("ios")) {
return myTouchkitUI.class;
} else {
return myUI.class;
}
}
}
My application works when I do not call this section of code to choose a UI provider. But it will always go to a touchkit UI. :
getService().addSessionInitListener(
new SessionInitListener() {
#Override
public void sessionInit(SessionInitEvent event)
throws ServiceException {
event.getSession().addUIProvider(prov);
}
});
My issue is that although it will choose between which UI class to return as soon as it begins to progress through the chosen UI code it passes back null objects that were originally autowired through spring. Seeing as this works when i dont choose a UI and just goes for touchkit, im assuming it must be somewhere in my UI provider choice code thats stopping the Spring functionality from allowing my classes to autowire, etc?
Well, the UIProvider is supposed to manage UI instances. Furthermore, since you're using Spring (Boot or not) it should retrieve beans from the Spring context instead of creating the instances itself when one is necessary:
UIProvider / DefaultUIProvider:
public UI createInstance(UICreateEvent event) {
try {
return event.getUIClass().newInstance();
} catch (InstantiationException e) {
throw new RuntimeException("Could not instantiate UI class", e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Could not access UI class", e);
}
}
Thus, I'd say that instead of extending the simple UIProvider (or rather the DefaultUIProvider) you should extend the SpringUIProvider, which retrieves instances from your app's Spring context, so the automagic will begin to happen again.
SpringUIProvider:
#Override
public UI createInstance(UICreateEvent event) {
final Class<UIID> key = UIID.class;
final UIID identifier = new UIID(event);
CurrentInstance.set(key, identifier);
try {
logger.debug(
"Creating a new UI bean of class [{}] with identifier [{}]",
event.getUIClass().getCanonicalName(), identifier);
return webApplicationContext.getBean(event.getUIClass());
} finally {
CurrentInstance.set(key, null);
}
}

Xuggle and java library path

I'm writing a screencast application in Java.
I decided to use Xuggle to do it and I followed up the installation instructions on the xuggle wiki.
I set up the PATH environment with %XUGGLE_HOME%\bin and %XUGGLE_HOME%\lib. Everything seems OK.
I made this application as a RCP plugin. I tried it on the "RCP-mail" template and the plugin is working and the video is generated correctly.
But when I decided to use it on a "real" application, the plug-in crashed with a strange error message:
Starting Capture
2011-11-10 08:08:45,438 [Thread-5] WARN com.xuggle.ferry.JNILibraryLoader - Failure: library load of library: xuggle-xuggler; version: 3: absolute path: C:\Program Files (x86)\Xuggle\bin\libxuggle-xuggler-3.dll; error: java.lang.UnsatisfiedLinkError: C:\Program Files (x86)\Xuggle\bin\libxuggle-xuggler-3.dll: Can't find dependent libraries
2011-11-10 08:08:45,447 [Thread-5] WARN com.xuggle.ferry.JNILibraryLoader - Failure: library load of library: xuggle-xuggler; version: 3: absolute path: C:\Program Files (x86)\Xuggle\bin\libxuggle-xuggler-3.dll; error: java.lang.UnsatisfiedLinkError: C:\Program Files (x86)\Xuggle\bin\libxuggle-xuggler-3.dll: Can't find dependent libraries
2011-11-10 08:08:45,453 [Thread-5] ERROR com.xuggle.ferry.JNILibraryLoader - Could not load library: xuggle-xuggler; version: 3; Visit http://www.xuggle.com/xuggler/faq/ to find common solutions to this problem
But this strange because the java.library.path is well defined:
logger.info(System.getProperty("java.library.path"));
returns
Nov 10, 2011 8:08:45 AM com.gvs.tools.ui.record.video.handler.RecordHandler startRecording
INFO: C:\Program Files (x86)\Java\jre6\bin;C:\Windows\Sun\Java\bin;C:\Windows\system32;C:\Windows;C:/Program Files (x86)/Java/jre6/bin/client;C:/Program Files (x86)/Java/jre6/bin;C:/Program Files (x86)/Java/jre6/lib/i386;C:\Program Files (x86)\Xuggle\bin;C:\Program Files (x86)\Xuggle\lib;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\JProbe 8.3\bin;C:\Program Files\TortoiseSVN\bin;D:\Work\Paul\eclipse;;.
What I'm missing to make the plug-in work with this application?
Is this issue due to the fact that the application uses other native libraries such as 3D-dll?
Here is the code used to make the screencast video:
RecordHandler.java:
private void startRecording() {
Logger logger = Logger.getLogger(RecordHandler.class.getName());
logger.info(System.getProperty("java.library.path"));
// Initialize framesQueue
framesQueue = new LinkedBlockingQueue<BufferedImage>();
// Initialize the capture thread
captureThread = new ScreenCapturer();
captureThread.setCaptureFramesQueue(framesQueue);
// Initialize the recorder
encoderThread = new FrameEncoder("test.mp4");
encoderThread.setCapturedFramesQueue(framesQueue);
// Start capture
captureThread.start();
// wait for the Queue to be feed before encoding
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
}
encoderThread.start();
}
ScreenCapturer.java:
#Override
public void run() {
// Retrieve the application main window's shell
Display.getDefault().asyncExec(new Runnable() {
#Override
public void run() {
appShell = Display.getCurrent().getActiveShell();
}
});
isRunning = true;
System.out.println("Starting Capture");
for (numberOfFramesTaken = 0; isRunning && numberOfFramesTaken <= IVideoEncoderConfiguration.MAXIMUM_NUMBER_OF_FRAMES; numberOfFramesTaken++) {
try {
takeScreenShot();
Thread.sleep(IVideoEncoderConfiguration.CAPTURE_TIME_INTERVAL_MILLIS);
} catch (InterruptedException e) {
}
}
System.out.println("Capture has ended");
System.out.println("Number of frames taken: "
+ numberOfFramesTaken);
}
/**
* Take a screen capture and store it in the capturedFramesQueue
*/
private void takeScreenShot() {
Display.getDefault().asyncExec(new Runnable() {
#Override
public void run() {
if (appShell != null) {
Rectangle bounds = appShell.getBounds();
java.awt.Rectangle awtBounds = new java.awt.Rectangle(bounds.x, bounds.y, bounds.width, bounds.height);
final BufferedImage screenCapture = robot.createScreenCapture(awtBounds);
try {
capturedFramesQueue.put(screenCapture);
} catch (InterruptedException e) {
}
}
}
});
}
FrameEncoder.java:
public void run() {
isRunning = true;
String outFile = outputdirectoryPath + outputFileName;
// First, let's make a IMediaWriter to write the file.
final IMediaWriter writer = ToolFactory.makeWriter(outFile);
// Retrieve the first frame to guess video dimensions
BufferedImage firstFrame = null;
try {
firstFrame = capturedFramesQueue.take();
} catch (InterruptedException e) {
}
if (firstFrame == null) {
return;
}
// We tell it we're going to add one video stream, with id 0,
// at position 0, and that it will have a fixed frame rate of
// FRAME_RATE.
writer.addVideoStream(0, 0,
IVideoEncoderConfiguration.FRAME_RATE,
firstFrame.getWidth(), firstFrame.getHeight());
long startTime = System.nanoTime();
for (numberOfFramesRecorded = 0; isRunning
&& numberOfFramesRecorded <= IVideoEncoderConfiguration.MAXIMUM_NUMBER_OF_FRAMES; numberOfFramesRecorded++) {
// Retrieve the captured frame
try {
final BufferedImage currentFrame = convertToType(capturedFramesQueue.take(), BufferedImage.TYPE_3BYTE_BGR);
// encode the next frame
writer.encodeVideo(0, currentFrame, System.nanoTime() - startTime,
TimeUnit.NANOSECONDS);
// sleep, time depending of FRAME_RATE
Thread.sleep(IVideoEncoderConfiguration.CAPTURE_TIME_INTERVAL_MILLIS);
} catch (InterruptedException e) {
}
}
// Get the remaining frame on the queue
Collection<BufferedImage> frames = new LinkedList<BufferedImage>();
capturedFramesQueue.drainTo(frames, IVideoEncoderConfiguration.MAXIMUM_NUMBER_OF_FRAMES - numberOfFramesRecorded);
for (BufferedImage frame : frames) {
BufferedImage currentFrame = convertToType(frame, BufferedImage.TYPE_3BYTE_BGR);
writer.encodeVideo(0, currentFrame, System.nanoTime() - startTime,
TimeUnit.NANOSECONDS);
}
// close the MediaWriter, write the trailer if needed
writer.close();
}
It's a little late I know, but the problem is that Xuggler requires ALL THE DLLs to be in the operating system load-path environment, not just the java.library.path
That means that all DLLs that install with Xuggle (for example, libavcodec.dll) need to be in the %PATH% environment variable of the process that launched Java.
Cause Could be un-availability of dependency jars or version conflicts.
Adding the following jars in the class path worked fine for me:
xuggle-xuggler-5.4.jar
slf4j-api-1.6.4.jar
logback-core-1.0.0.jar
logback-classic-1.0.0.jar

Categories

Resources