02 enero 2011

Jasper Reports -Virtualizar-

Hoy les contare sobre un problema que tuve durante estos días, estoy en el desarrollo de una aplicación constituida con Java para el lado servidor y Flex para el lado cliente, últimamente la aplicación había tenido unos problemas con el consumo de memoria luego de analizarla descubrí que esto era básicamente por dos motivos uno de ellos relacionados con Hibernate y el levantamiento de 60000 objetos en memoria lo cual al fin de cuentas lo termine resolviendo con NameQuery, pero aun tenía problemas cuando se ejecutaban varios reportes ya que la memoria del servidor subía inmesuradamente, a continuación les dejo la solución que encontré.


Antes que nada quiero recomendarles que cuando tengan un problema de memoria utilicen jVisualVM es un programa que te deja conectarte al servidor y ver la cantidad de instancias en memoria y así analizar cuál es la que se queda alocada o cuales consumen mucho, como primer paso use el programa y me conecte al servidor (Tomcat) y comencé a arrojar reportes al parecer el GC (Garbage Collector) corría de forma normal pero cuando había muchos reportes al mismo tiempo veía que la cantidad de instancia de JRTemplatePrintText crecía drásticamente.
Luego de investigar un poco encontré que Jasper Report durante su proceso de fill (Obtener los datos y rellenar la plantilla del informe con estos) levantaba los objetos en memoria, entonces la forma de abordar este problema es de dos maneras:
La paginación y la virtualización.
Paginación se refiere a obtener el informe en trozos, en lugar de recoger toda la información de golpe, pero como al final los datos se tienen que mostrar juntos en un único informe, nos introducen la virtualización. La virtualización es una técnica que consiste en serializar los datos, para no saturar la memoria heap.
Cabe aclarar que incluir virtualización conlleva la pérdida de rendimiento.

Aquí un ejemplo de cómo tendríamos que usar la virtualizacion:


JasperPrint jasperprint = null;
JRFileVirtualizer virtualizer = new JRFileVirtualizer(10, "tmp");

long start = System.currentTimeMillis();  

filterParameters.put(JRParameter.REPORT_VIRTUALIZER, virtualizer);

jasperprint = JasperFillManager.fillReport(this.reportFile,this.filterParameters, this.databaseConnection);

virtualizer.setReadOnly(true);
logger.info("Filling time : " + (System.currentTimeMillis() - start));

// Usamos los exportadores para generar el reporte en lo que nosotros queramos.

virtualizer.cleanup();

Se vemos el ejemplo veremos que básicamente creamos un JasperPrint como lo haríamos normalmente pero al momento de generar este JasperPrint por medio del método fillReport de la clase JasperFillManager notamos que en los parámetros le pasamos un Virtualizer esto básicamente quiere decir que Jasper ira creando archivos temporales en la carpeta que le pasamos como segundo parámetro y el primer parámetro es usado para saber el tamaño máximo de los archivos, como ultima línea no debemos olvidar hacer un cleanup del virtualizer para borrar los archivos creados.

Bueno espero que este simple post les sirva como me sirvió a mí encontrar esta técnica para no malgastar memoria durante la generación de reportes ya sean medianos o grandes
Vale recordarles que deben tener en cuenta el balance entre performance y gasto de memoria esto lo pueden manejar con el tamaño máximo de los archivos.

Saludos,
Luis

Leer más...

28 agosto 2010

Semaphores

Hola como están, hoy estaremos viendo un ejemplo de cómo usar Semaphores en Java, a partir de la versión de 1.5 se hicieron unas extensiones importantes al paquete java.util.concurrent la cual vino acompañada de la clase Semaphore la cual brinda la funcionalidad que necesitaremos.

Para entender su uso lo haremos en base a un ejemplo real, en mi trabajo hace unos días surgió el siguiente issue, tenemos algunas clases singleton que funcionan como Providers, el problema es que a su vez tenemos procesos concurrentes que usan este Provider y cada uno hace un refresh de los datos para tener la ultima versión de los datos. Durante esta semana sucedió que N procesos (Fijaremos N=5) corrieron casi al mismo tiempo por ende cada uno de ellos intento refrescar el singleton todos a la vez como el método que hace el refresh esta sincronizado sucedía que actualizaba los datos para 1 Thread y los otros 4 Thread esperaban, después volvía a refrescar para el siguiente Thread y los restantes esperaban y así sucesivamente, les adjunto una imagen para entender mejor el problema.
Problem Image
Para evitar que los 5 Thread realicen el refresh se decidió ejecutar solo un refresh y encolar los otros 4 esperando que el que está realizando el refresh termine, para esto tuvimos que modificar nuestro singleton y utilizar Semaphores y syncronized por medio de un mutex. Aqui está la clase singleton modificada y ahora explicaremos cada paso.

 
package com.javacuriosities.examples;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;

public class Singleton {

 private static Singleton instance;

 private volatile boolean refreshing = false;

 private Object mutex = new Object();

 private List<Semaphore> pendings = new ArrayList<Semaphore>();

 public synchronized static Singleton getSingleInstance() {
  if (instance == null) {
   instance = new Singleton();
  }
  return instance;
 }

 public void refreshAll(String threadName) {
  if (!checkRefreshingStatus()) {
   refresh(threadName);
  } else {
   Semaphore markPending = addSemaphore(threadName);
   acquireSemaphore(markPending, threadName);
  }
 }

 private void refresh(String threadName) {
  try {
   refreshInfo(threadName);
  } catch (Exception e) {
   e.printStackTrace();
  } finally {
   releasePending();
  }
 }

 private void acquireSemaphore(Semaphore markPending, String threadName) {
  try {
   if (markPending != null) {
    System.out.println("Acquire semaphore: " + threadName);
    markPending.acquire();
   } else {
    System.out.println("Avoid semaphore: " + threadName);
   }
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
 }

 private Semaphore addSemaphore(String threadName) {
  Semaphore markPending = null;
  synchronized (mutex) {
   if (isRefreshing()) {
    markPending = new Semaphore(0, true);
    pendings.add(markPending);
    System.out.println("Add semaphore to: " + threadName);
   }
  }
  return markPending;
 }

 private void releasePending() {
  synchronized (mutex) {
   System.out.println("Release semaphores");
   for (Semaphore semaphore : pendings) {
    semaphore.release();
   }
   pendings.clear();
   refreshing = false;
  }
 }

 private synchronized boolean checkRefreshingStatus() {
  boolean previousState = refreshing;
  if (!refreshing) {
   refreshing = true;
  }
  return previousState;
 }

 private void refreshInfo(String threadName) {
  for (int i = 0; i < 1000; i++) {
   System.out.println("Executing refresh: " + threadName);
  }
 }

 public boolean isRefreshing() {
  return refreshing;
 }
}
Iré explicando todos los cambios que hemos efectuado para conseguir lo que esperábamos a su vez iremos viendo como seria el flujo de ejecución. Nota: La clase ha sido modifica para que sirva para las pruebas y el ejemplo.
1) El método refreshAll dejo de ser synchronized.
2) Apenas comienza este método tenemos un IF el cual llama a un método synchronized donde se pregunta si se está refrescando y se cambia el estado del flag refreshing.
3) El primer Thread que llegue a esta parte entrara por el IF los demás se irán por el ELSE.
4) El Thread 1 comienza a refrescar la información, los otros van por el lado del ELSE y entran en el método addSemaphore como vemos este método usa sincronización por mutex esto es para asegurarnos que no se agregan semaphores a la lista mientras la estamos liberando por otro lado, si la lista no se esta liberando y además aun está corriendo el refresh creamos un semaphore y lo retornamos.
5) Si se pudo crear el semaphore se intentan hacer acquire del mismo, si no se logro crear significa que el refresh termino y no era necesaria la espera. Usamos el método acquire para decirle a un semaphore que se quede esperando hasta que sea liberado esto sería el equivalente a hacer this.wait() y esperar que por otra parte del código se haga un this.notify().
6) Otro tema importante es que si la lógica del refresh falla igual debemos liberar los semaphores pendientes sino estaremos lockeando el proceso., por eso el releasePending se encuentra en un bloque finally.

Notar que los semaphore son creados con 0 permits lo que signica que al hacer acquire quedaran esperando que la cantidad de permits este en 1, esto se logra por medio del método release que incrementa en 1 los permits del semaphore.

Les adjunto un zip con la clase singleton mas una clase de test que dispara 20 Threads. Espero que este post les sea de ayuda para ver algún ejemplo de concurrencia y threading en Java, cualquier cosa que no se entienda del post todas las preguntas son bienvenidas al igual que las criticas.

Este post es en honor a tuky que últimamente está muy curioso sobre temas varios de programación.

Example

Saludos,
Luis

Leer más...

09 mayo 2010

OSGi (First Steps)

Decidí hacer este post porque no encontré ningun tutorial que nos explique OSGi desde cero, aclaro que en este post haremos una introducción para que después podamos continuar con algo más avanzado.

¿Que es OSGi?
OSGI (Open Services Gateway Initiative), podríamos definirlo como un sistema (o framework) modular para Java que establece las formas de crear módulos y la manera en que estos interactuaran entre sí en tiempo de ejecución. OSGi intenta solventar los problemas del tradicional "classloader" de la máquina virtual y de los servidores de aplicaciones Java. En OSGI, cada módulo tiene su propio classpath separado del resto de classpath de los demás módulos.

Este framework proporciona a los desarrolladores un entorno orientado a servicios y basado en componentes, ofreciendo estándares para manejar los ciclos de vida del software.
OSGi esta divido en capas que en proximo post iremos cubriendo por ahora se mencionan para que se conozca la existencia de ellas.

Security Layer.
Module Layer.
Life Cycle Layer.
Service Layer.
Execution Environment.

OSGi Alliance
La OSGi Alliance es un consorcio de empresas tecnológicas a nivel mundial que trata de asegurar la interoperabilidad de las aplicaciones y servicios basados en esta plataforma entre las empresas que componen este consorcio, podemos encontrar compañías de diversa índole: automoción, aeronáutica, fabricantes de electrodomésticos, telecomunicaciones, fabricantes de teléfonos... Algunos ejemplos de miembros: Motorola, Nokia, Mitsubishi Electric Corporation, Vodafone Group Services, LinkedIn, LG Electronics...
La alianza proporciona las especificaciones, las implementaciones de referencia, las suites de prueba y la certificación.

Especificacion OSGi
OSGI proporciona un marco de trabajo java de uso general, seguro y administrado que
soporta el despliegue dinámico de aplicaciones conocidas como "Bundles" o módulos.
Algunas de las características que componen este marco de trabajo:

  • Es un sistema de módulos para la plataforma java.
  • Incluye reglas de visibilidad, gestión de dependencias y versionado de los bundles.
  • Es dinámico.
  • La instalación, arranque, parada, actualización y desinstalación de bundles se realiza dinámicamente en tiempo de ejecución sin tener que detener por completo la
    plataforma.
  • Se trata de una arquitectura orientada a servicios.
  • Los servicios pueden ser registrados y consumidos dentro de la VM.
Implementacones OSGi
Lo bueno de tener una aplicación modular es que a diferencia de una aplicación web nosotros vamos deployando unidades independientes las cuales se pueden cambiar con gran facilidad, esto quiere decir que si necesitáramos reemplazar un modulo por otro muchas veces no sería necesario detener toda la aplicación sino ciertos módulos y todo seguiría andando, además tenemos versionado de Bundles (Módulos) lo cual es muy útil para poder ir haciendo que nuestros módulos usen otros pero de alguna versión especifica.
Podemos encontrar varias plataformas certificadas en OSGi las cuales cumplen con la especificación de modo que si nosotros desarrolláramos nuestros módulos ellos podrían correr en cualquiera de estas implementaciones, algunas de ellas son:
  • Apache Felix.
  • Eclipse Equinox.
  • Knopflerfish.
  • Newton Project.
Preparando mi entorno OSGi
Para este post utilizaremos la implementación de Eclipse Equinox para nuestros ejemplos, nos dirigimos a Eclipse Equinox desde aquí podremos realizar la descarga del entorno de trabajo, tenemos varias opciones:

Descarga de la implementación OSGI base: Se trata de un fichero .jar (bundle),
que contiene los elementos básicos para poder ejecutar nuestro entorno.

Descarga completa de equinox: Se trata de un fichero .zip que contiene el bundle
base comentado anteriormente, más todas las configuraciones e implementaciones
de los servicios OSGI.

Descarga de los bundles opcionales: Mediante esta opción podremos descargar
uno a uno los bundles de equinox que implementan los diferentes servicios de
OSGI.

Para este ejemplo bajaremos la descarga completa para facilitar las cosas, luego podremos descomprimir este zip en nuestro disco. Dentro de la carpeta plugins del zip que descargamos encontraremos un jar llamado org.eclipse.osgi_3.x.x_xxx.jar el cual es util para dos cosas la primera levanta el entorno OSGi y desde aqui podremos instalar, iniciar, detener, desinstalar nuestros módulos, y además podremos usar este jar para desarrollar en el eclipse ya que contiene las clases/interfaces de la implementación OSGi. Les dejo un print screen de mi eclipse con el desarrollo del ejemplo que sigue.

OSGiEclipse.png

Ejemplo Basico
Como es típico empezaremos con un Hello World, ósea aquí no estaríamos construyendo una aplicación que nos diga Hello World sino que estaríamos creando un modulo el cual por vivir en un contender OSGi tiene un ciclo de vida completo. Básicamente crear un modulo sería crear un jar con nuestras clases y un archivo MANIFEST.MF

Entendiendo el MANIFEST.MF
El Manifesft.mf no es más que un archivo de texto en el cual se agregan directivas del tipo clave: valor, las cuales juegan un papel muy importante ya que son las que irán diciéndole al marco de trabajo OSGi algunas de las cosas importante sobre nuestro modulo a continuación muestro un Manifest de ejemplo y explico sus directivas.

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: HelloWorld
Bundle-SymbolicName: com.javacuriosities.osgi.helloworld
Bundle-Version: 1.0.0
Bundle-Activator: com.javacuriosities.osgi.helloworld.Activator
Import-Package: org.osgi.framework;specification-version="1.4.0"
Manifest-Version
Define la versión del archivo MANIFEST.MF.

Bundle-ManifestVersion
Indica la especificación de OSGi sobre la que está basada el bundle. Por defecto, si no se indica nada, el valor es 1. Este valor indica que el bundle se basa en las reglas de la especificación OSGI 3. Si tiene valor 2, se tratará de la especificación de OSGI 4.

Bundle-Name
Indica el nombre corto del Bundle debe ser human-readable.

Bundle-SymbolicName
Esta etiqueta define un nombre único para el bundle. Con este nombre es con el que trabajaremos en el entorno de ejecución.

Bundle-Version
Indica la versión de nuestro bundle, dentro de nuestro entorno podríamos tener diferentes versiones de un mismo bundle.

Bundle-Activator
Indica que clase funcionara como Activator, la cual es una clase que debe implementar la interfaz BundleActivator de modo que podamos entrar dentro del ciclo de vida de nuestro Bundle.

Import-Package
Por medio de esta directiva le decimos que paquetes queremos importar, si nos ponemos a pensar esta línea es muy importante porque nuestro jar solamente tendra nuestras clases pero por ejemplo la interfaz BundleActivator ya es parte del modulo core de OSGi asi no estaría dentro de nuestro jar así que por medio de esta directiva la importamos para que todo funcione.

Implementando nuestro Activator

package com.javacuriosities.osgi.helloworld;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

public class Activator implements BundleActivator{
@Override
public void start(BundleContext arg0) throws Exception {
 System.out.println("Hello World"); 
}

@Override
public void stop(BundleContext arg0) throws Exception {
 System.out.println("Bye Bye"); 
}
}
Empaquetando e instalando
Luego de poder crear nuestro modulo deberemos empaquetarlo como un jar podríamos usar el eclipse o el comando jar que viene con java, el jar nos debería quedar de la siguiente forma

com.javacuriosities.osgi.helloworld_1.0.0.jar
    com\javacuriosities\osgi\helloworld\Activar.class
    META-INF\MANIFEST.MF
Debemos iniciar nuestro entorno OSGi para esto haremos lo siguiente iremos al directorio donde descomprimimos el entorno Eclipse Equinox y dentro de la carpeta plugins ejecutaremos la siguiente línea de comando, luego veremos que el prompt cambia a osgi> y ahí pondremos ss para ver nuestros bundles instalados.

java -jar org.eclipse.osgi_3.x.x_xxx.jar -console

Ahora instalaremos nuestro bundle(.jar) copiaremos nuestro bundle al Disco C:\ y ejecutaremos el comando

install file:c:\com.javacuriosities.osgi.helloworld_1.0.0.jar

si todo va bien aparecerá un mensaje del tipo Bundle id is XX, ahora con este id ya podremos iniciar, detener y desinstalar nuestro modulo. Para probar ejecutaremos estos dos comandos.

start XX --> Ahi veremos nuestro mensaje del método start de la clase Activator.
stop XX --> Ahi veremos nuestro mensaje del método stop de la clase Activator.

Adjunto un screen de la consola con los comandos.

OSGiConsole.png

Bueno esto ha sido por este post, espero haber sido lo suficientemente claro para que se entienda el ejemplo y los conceptos básicos de OSGi, cualquier pregunta o sugerencia es bien recibida.

Saludos y hasta pronto.

Leer más...

13 marzo 2010

Java Best Practices

Después de mucho tiempo de no realizar ningún post vuelvo para compartir una presentación que estoy realizando sobre buenas prácticas de programación espero les sea útil.

La idea de estar presentación es ir actualizándola lo más que se pueda, así queda como un documento de referencia para las personas que estén interesadas en ir programando cada día mejor.
Aclaro que no soy ningún experto acepto cualquier comentario que puede ir mejorando la presentación ya que esto está pensado para todos y la idea es que si alguien tiene alguna idea que agregar me avise y la agregamos.


Saludos

Leer más...

04 agosto 2009

JavaFX Sintaxis Declarativa

En el post anterior hemos visto un ejemplo sobre cómo realizar un Hello World con JavaFX pero sin entrar en mucho detalle sobre algunas líneas del código, ahora seguiremos con algunos ejemplos más y explicando un poco sobre esto de Sintaxis Declarativa, por ejemplo un objeto puede ser creado con un Objeto Literal. Lo que significa una sintaxis declarativa concisa parecida a javascript, en lugar de explicar "como" realizar algo (el algoritmo, en los lenguajes imperativos), deberemos describir "que" es ese algo. Por ejemplo, en HTML, se declara el contenido de la página, pero no se explica cómo este será mostrado en la pantalla. Ahora veremos algunos ejemplos para aclarar esto.

Iremos desarrollando un ejemplos casi línea por línea para ir notando la simpleza del lenguaje e ir viendo algunos efectos, para este ejemplo usaremos una imagen que la pueden encontrar en el siguiente link ,comencemos.

Paso 1:


import javafx.stage.Stage;

Stage {
width: 400
height: 400
}

con las pocas líneas de arriba ya hemos definido una instancia de un objeto Stage, el cual es necesario para renderizar una ventana.

Paso 2:

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.image.Image;

var image = Image {
url: "{__DIR__}JavaFX.png"
}

Stage {
width: 400
height: 400
scene: Scene {
content: [
ImageView {
x: 75
y: 60
image: image
}
]
}
}

aquí hemos complicado un poco la cosa, pero veremos cada detalles, los primeros import de Scene, Image e ImageView son necesarios por los siguiente motivos.
Scene es el objeto el cual nos permite renderizar elementos gráficos dentro de el, el ImageView es el contenedor para una determinada imagen, y por último el Image es la imagen en sí. Si notamos la definición de Scene, vemos que a su propiedad content(Aquí dibujamos los elementos gráficos), hemos agregado una instancia de ImageView y a esta le hemos asignado la imagen previamente cargada en la variable image. Como podrán notar toda la sintaxis es muy simple y siempre nos preocupamos en la declaración de lo que deseamos y no en como generar todo esto.
El código hasta ahora nos generaría una ventana, con la imagen posicionada en el centro, pero nuestra Scene por defecto tiene un color blanco de fondo, si vemos en posts anteriores veremos que podíamos asignar colores en forma de gradientes, pues bueno eso haremos como siguiente paso.

Paso 3:

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.image.Image;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.paint.Color;

/**
* @author softdesigner
*/

var image = Image {
url: "{__DIR__}JavaFX.png"
}

Stage {
width: 400
height: 400
scene: Scene {
content: [
ImageView {
x: 75
y: 60
image: image
}
]
fill: LinearGradient {
startX : 0.0
startY : 0.0
endX : 0.0
endY : 1.0
stops: [
Stop {
color : Color.WHITE
offset: 0.0
},
Stop {
color : Color.BLACK
offset: 1.0
}
]
}
}
}

Si vemos la propiedad fill del objeto Scene, nos damos cuenta que estamos asignando un LinearGradient a esta propiedad, como podemos deducir esto generara un Gradiente linear por medios de las propiedades startX, startY, endX, endY definimos la dirección lineal y por medio de los stops definimos que distintos Stop usar, cada Stop define necesita su propiedad color y su propiedad offset la cual indicada cuando se detendrá ese gradiente.

Muchas veces cuando estamos retocando imágenes o logrando algún efecto se nos plantea la necesidad de lograr un efecto de reflejo sobre el agua o algún material, JavaFX viene con muchas de estas dificultades resueltas ya que brinda diversos efectos para ser aplicados a nuestras imágenes, para ver esto usaremos un efecto de Reflection para lograr reflejar nuestra imagen.

Paso 4:


import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.image.Image;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.paint.Color;
import javafx.scene.effect.Reflection;

/**
* @author softdesigner
*/

var image = Image {
url: "{__DIR__}JavaFX.png"
}

Stage {
width: 400
height: 600
scene: Scene {
content: [
ImageView {
x: 75
y: 60
image: image
effect: Reflection {
fraction: 0.75
topOffset: 0.0
topOpacity: 0.5
bottomOpacity: 0.0
}
}
]
fill: LinearGradient {
startX : 0.0
startY : 0.0
endX : 0.0
endY : 1.0
stops: [
Stop {
color : Color.WHITE
offset: 0.0
},
Stop {
color : Color.BLACK
offset: 1.0
}
]
}
}
}

Ahora tenemos nuestra imagen con un efecto de Reflection, este es asignado al ImageView ya que es el contenedor de la imagen y ahora explicaremos cada propiedad de este efecto.
fraction: Es la fracción que se muestra reflejada en base a la imagen original(Valor porcentual).
topOffset: Es la distancia entre la imagen y el comienzo de su reflejo(Valor libre).
topOpacity: Es la opacidad del comienzo del reflejo(Valor porcentual).
bottomOpacity: Es la opacidad del final del reflejo(Valor porcentual).

Para ir terminando el ejemplo como último detalle, queremos mostrar dos efectos al mismo tiempo, los cuales serán una DropShadow y un efecto Reflection.

Paso 5:

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.image.Image;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.paint.Color;
import javafx.scene.effect.Reflection;
import javafx.scene.effect.DropShadow;

/**
* @author softdesigner
*/

var image = Image {
url: "{__DIR__}JavaFX.png"
}

Stage {
width: 400
height: 600
scene: Scene {
content: [
ImageView {
x: 75
y: 60
image: image
effect: Reflection {
fraction: 0.75
topOffset: 0.0
topOpacity: 0.5
bottomOpacity: 0.0
input: DropShadow {
offsetX: 10
offsetY: 10
color: Color.BLACK
radius: 10
}
}
}
]
fill: LinearGradient {
startX : 0.0
startY : 0.0
endX : 0.0
endY : 1.0
stops: [
Stop {
color : Color.WHITE
offset: 0.0
},
Stop {
color : Color.BLACK
offset: 1.0
}
]
}
}
}

Si leemos acerca de la propiedad effect del ImageView descubriremos que solo acepta un objeto effect, y aquí nos entra la duda de cómo haremos mas agregar dos efectos al mismo tiempo, para esto casi todos los efectos tiene una propiedad input la cual recibe un effect, así que si nosotros quisiéramos ir encadenando efectos deberíamos hacerlo por medio de esta propiedad, en la parte superior del código se nota que el efecto Reflection recibe un input de un DropShadow, las propiedades del DrowShadow son las siguientes:
offsetX: Cantidad de pixeles sobre el eje x.
offsetY: Cantidad de pixeles sobre el eje y.
color: Color de relleno de la sombra.
radius: Radio del difuminado para la sombra.

Con esto daremos por finalizada esta mínima explicación y demostración sobre la Sintaxis declarativa que ofrece JavaFX y la simplicidad y poder de este lenguaje.
Les dejo un link al ejemplo del proyecto netbeans, luego intentare dejar el jnlp para que lo puedan descargar.

Proyecto
DeclarativeSyntax.rar



Saludos

Leer más...