viernes, 8 de julio de 2011

Java SE 7 ya está disponible

Saludos amigos.

Como dice el título ya tenemos disponible la nueva versión de java Java SE 7.

La página de internet para mayor información es:

http://jdk7.java.net/

Para ver una documentación de las novedades puedes hacer click aquí también:

http://jdk7.java.net/preview/

Ya tenemos información para investigar. Claro la primera información siempre viene en inglés y a lo mejor para lo que no están familiarizados con el idioma les cueste, pero ¡Adelante! hay camino por recorrer, sigamos investigando para ver las innovaciones que trae este lenguaje, uno de los más utilizados en el mundo.

Nos vemos.

domingo, 31 de octubre de 2010

Patrón de diseño: Adaptador

Saludos a todos.

Ahora hablaremos sobre otro patrón de diseño que puede ser utilizado en Java, que de hecho, hasta en su propia API se ha incluido para el manejo de eventos de interfaces gráficas. Estoy hablando acerca del patrón de diseño: Adaptador.

Si, como su nombre lo indica, trata de adaptar una clase para que no implemente todos los métodos declarados en una interfaz, esto lo hace, usando una clase que ya la interfaz en cuestión y la clase que utiliza la adaptadora debe sólo implementar en método que necesite o requiera, vamos a ver si con ejemplos me entienden mejor:

En java.awt.event se encuentra la interfaz MouseListener, esta interfaz contiene los métodos que sirven para manejar eventos del ratón:
void mouseClicked(MouseEvent e)
void mouseEntered(MouseEvent e)
void mouseExited(MouseEvent e)
void mousePressed(MouseEvent e)
void mouseReleased(MouseEvent e)

Supongamos que nosotros queremos que nuestra clase sólo implemente el evento mouseClicked, si implementamos esta interfaz en nuestra aplicación debemos también implementar los métodos declarados y por lo tanto colocar los demás métodos (mouseEntered, mouseExited, mousePressed y mouseReleased) y dejarlos vacíos (por regla de java en cuanto a que implemento la interfaz).

Con la clase adaptadora eso sería más fácil, yo puedo hacer extender (heredar) mi clase a MouseAdapter. Este clase MouseAdapter, implementa a la interfaz MouseListener y contiene implementado todos los métodos del Listener (aunque vacío, no importa), por lo tanto al heredar sólo sobreescribo el método que necesito. La limitante es que como Java no soporta herencia múltiple, si mi clase ya extiende de otra clase no puedo utilizar la clase adaptadora y me veré obligado a utilizar el Listener requerido.

Veamos código:
Implementando la interfaz me obliga a implementar todos métodos, aunque no los utilice:


package patronesdisenio.adaptador;

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

public class MiClaseImplementada
implements MouseListener {
//La interfaz me obliga a implementar
//todos los métodos
@Override
public void mouseClicked(MouseEvent arg0) {}
@Override
public void mouseEntered(MouseEvent arg0) {}
@Override
public void mouseExited(MouseEvent arg0) {}
@Override
public void mousePressed(MouseEvent arg0) {}
@Override
public void mouseReleased(MouseEvent arg0) {}
}


La clase adaptadora me permite implementar sólo el método que deseo, aunque si mi clase ya extiende de otra clase, no podría utilizarla:



package patronesdisenio.adaptador;

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JOptionPane;

public class MiClaseAdapter extends MouseAdapter {

@Override
public void mouseClicked(MouseEvent e){
JOptionPane.showMessageDialog(
null, "Hizo click en un elemento");
}
}


Esto es un ejemplo dentro del API de java, pero igualmente podría adaptarse a nuestro sistema, en caso de que no necesitemos implementar todos los métodos de una interfaz.

Patrón de diseño: Singleton

Saludos a todos. Hoy comenzaremos con el primer artículo acerca de los patrones de diseño. Para hoy hablaremos acerca del patrón de diseño Singleton.

Recuerdo en mi primer curso de Java, cuando nos asignan realizar un proyecto en este lenguaje, utilizando las ventajas de javax.swing, es decir interfaces gráficas, nuestro grupo creó un menú que llamaba a ventanas o diálogos. Se nos presentó el siguiente problema: Cuando se llamaba a una opción de menú se desplegaba una ventana auxiliar nueva, si de nuevo haciamos click sobre la misma opción, se mostraba otra ventana nueva igual a la anterior, es decir, se creaban duplicados o triplicados, lo cual para el proyecto no era lo adecuado. Empezamos a indagar que pasaba con tal caso, cómo podríamos hacer para que no sucediera esto y nuestro profesor muy sabiamente dijo, eso se resuelve con el patrón Singleton.

Empezamos a buscar información sobre este patrón y nos encontramos que la finalidad de este patrón es poder crear una sola instancia de una Clase, por lo tanto si se intentaba crear una nueva instancia se llamaba a la misma creada anteriormente, garantizando así la no duplicación. Ahora bien ahora lo que nos faltaría por ver es como puede llevarse a cabo este patrón:

Existen varias maneras de implementar Singleton, aquí va la más utilizada:



public class ClaseSingleton {

private static ClaseSingleton instancia = null;

private ClaseSingleton() { } //constructor privado

public static ClaseSingleton getInstanciaUnica() {

if (instancia == null) {
instancia = new ClaseSingleton();
}
return instancia;
}
}


Como puede verse existe en la clase un constructor privado, esto se hace con la finalidad de que sólo la clase singleton pueda crear instancias. Existe también un método getInstanciaUnica que crea la instancia si aun no ha sido creada, pero si ya fue creada una, devuelve la misma creada anteriormente.

Veamos un ejemplo con las Ventanas de javax.swing:
Clase Principal: Con Main:


package patronesdisenio;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class VentanaPrincipal extends JFrame
implements ActionListener {

public VentanaPrincipal() {
super("Ventana Principal");
JButton boton = new JButton("Nueva Ventana");
Container contenedor = getContentPane();
contenedor.add(boton, BorderLayout.SOUTH);
boton.addActionListener(this);
setSize(600, 600);
setVisible(true);
}

public static void main(String[] args) {
VentanaPrincipal app = new VentanaPrincipal();
app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}

public void actionPerformed(ActionEvent e) {
VentanaAuxiliar.getVentanaAuxiliarUnica();
}
}


Aquí la Ventana Auxiliar a llamar (Singleton)


package patronesdisenio;

import javax.swing.JDialog;

public class VentanaAuxiliar extends JDialog{

public static VentanaAuxiliar vtnUnica = null;

private VentanaAuxiliar(){ }

public static VentanaAuxiliar
getVentanaAuxiliarUnica(){
if(vtnUnica == null){
vtnUnica = new VentanaAuxiliar();
vtnUnica.setTitle("Ventana Auxiliar");
vtnUnica.setSize(490, 400);
vtnUnica.setVisible(true);
return vtnUnica;
}
return vtnUnica;
}
}


De esta manera cuando se llame a la ventana auxiliar desde la ventana principal con el botón inferior, no se generará ventana duplicada ya que Ventana auxiliar es singleton.

domingo, 26 de septiembre de 2010

Respuesta Clase Scanner Java 5.

Esta es la respuesta a un comentario realizado en el post sobre la clase Scanner de Java 5.
El comentario dice así:

Tengo un problema que no se como solucionar.... tengo un fichero de esta manera:

AU Gutierrez, M
Marin, C
Bonachea, J
AF Gutierrez, Mateo
Lucha, Pedro
Gutierrez, Francisco
TI Are talus flatiron sequences in Spain climate-controlled landforms?
SO ZEITSCHRIFT FUR GEOMORPHOLOGIE
LA English
DT Article

Cada empiece con 2 letras mayusculas es un campo, y quiero leerlo y que interprete que es un campo diferente, es decir AU un campo con sus autores: Gutierrez, marin y Bonachea, estos autores que los meta todos juntos en un string, y AU en otro.
Estoy intentando leyendolo linea por linea, luego separarlo por tokens, y concatenar, pero no me sale nada!!!

haber si me podeis ayudar,

un saludo,
Toño.

Ahora bien, para dar respuesta a esta duda, yo lo resolví de la siguiente manera:



import java.io.*;
import java.util.*;
import java.util.regex.*;

public class Autores {
public static void main(String[] args)
throws FileNotFoundException {
Scanner scaner = new Scanner(
new File("archivos/datos.txt"));
Pattern patron = Pattern.compile("[A-Z]{2}");
ArrayList resultado =
new ArrayList();
StringBuffer sb = new StringBuffer();
/* sb: Guarda los autores relacionados en una sola
cadena separados por punto y coma (;)*/
/* resultado: guarda los autores relacionado de
stringbuffer en registros distintos */
int nroLinea = 1;
do{
String linea = scaner.nextLine();
Matcher matcher = patron.matcher(linea);
boolean coincide = matcher.find();
if(coincide && nroLinea == 1){
sb = new StringBuffer();
sb.append(linea.substring(3));
}else if(coincide && nroLinea != 1){
resultado.add(sb.toString());
sb = new StringBuffer();
sb.append(linea.substring(3));

}else{
sb.append(";"+linea);
}
nroLinea++;
}while(scaner.hasNext());
resultado.add(sb.toString());
for(String cadena: resultado){
System.out.println(cadena);
}
}
}


Explico:
La clase Scanner recibe un Archivo que es el que voy a leer (hay que asegurarse que la ruta es le correcta ya sea relativa o absoluta), de no ser así se lanza una FileNotFoundException.

Lo primero que hago es recorrer las líneas de este archivo con el ciclo do-while externo. Utilizo ls expresiones regulares para ver si mi comienzo de línea contiene dos caracteres en mayúsculas ([A-Z]{2}), si coincide puede pasar dos cosas: que sea la primera línea o no; si es la primera línea sólo agrego la cadena a un stringbuffer que almacena todos los autores relacionados, si no, si coincide pero no es la primera línea, agrego todos los autores relacionados (stringbuffer)a una Coleccion ArrayList de string y luego creo uno nuevo para la nueva cadena, si no es ninguno de estos dos casos, entonces quiere decir que la lectura de la línea es un autor más por lo que procedo a agregarlo al stringbuffer.

Además la línea después del while externo resultado.add(sb.toString) se asegura de agregar el último registro después de hacer el recorrido del archivo.

Nota: La el metodo substring(3) devuelve una nueva cadena a partir del caracter número 3, haciendo el conteo a partir de cero(0).

Toño si tienes otra duda, no vaciles en preguntar. Hasta Luego.

miércoles, 15 de septiembre de 2010

Java Persistence API con Hibernate.

Saludos compañeros y gracias por acompañarme nuevamente, acá en Acerca de Java para discutir temas acerca de la tecnología java. Ahora hablemos un poco acerca de Java Persistence Api (JPA), en su implementación Hibernate.

Generalmente cuando creamos aplicaciones, debemos interactuar con una base de datos relacional, en este caso es necesario realizar sentencias SQL desde nuestro código de programa para poder enviar solicitudes de base de datos y traer, actualizar o borrar datos.

Con la implementación Hibernate, ya esto no es necesario. Hibernate me permite realizar persistencias de objetos java en una base de datos relacional sin necesidad de sentencias sql, es decir puedo almacenar mi objeto java, en una base de datos, relacionando mis datos de objeto con los campos de la base de datos.

Para poder realizar este tipo de persistencia se necesitan cinco requisitos:

* Objeto Java Persistente: Objeto a ser serializado.
* Archivo de Mapeo Hibernate:
* Archivo de Configuración Hibernate.
* API Hibernate.
* Manejador de Base de Datos, en este caso yo utilizaré PostgreSQL 9.0.

Objeto Java Persistence: Una innovación con Hibernate es que no necesita extender a ninguna clase y tampoco es necesario que implemente una interfaz. Pero si es necesario declarar un constructor público que no reciba parámetros, además como buena práctica de programación deben sobreescribir los métodos equals y hashCode, no es un requisitor indispensable pero se considera buena práctica de programación.

Ejemplo de un tipo de objeto persistente:


package mypersistence;

import java.util.Date;

public class Empleado {

private int id;
private String nombre;
private Date nacimiento;

public Empleado(){ }

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getNombre() {
return nombre;
}

public void setNombre(String nombre) {
this.nombre = nombre;
}

public Date getNacimiento(){
return nacimiento;
}

public boolean equals(Object objeto){
if(objeto == this)
return true;
Empleado empleado = (Empleado) objeto;
if(this.nombre.equals(empleado.nombre))
return true;
return false;
}

public int hashCode(){
return this.nombre.hashCode();
}

public void setNacimiento(Date fecha) {
nacimiento = fecha;
}
}


Archivo de mapeo Hibernate: Es un archivo XML que que dice al API Hibernate como va a ser serializado mi Objeto Java Persistence. Hay 4 maneras:
* Mapeo de clase a tabla.
* Un elemento id para identificar la clave principal de la tabla.
* Mapeo de propiedad a columna o lo que es lo mismo de Atributo a campo.
* Relaciones con otros objetos persistentes.

Ejemplo de XML:
Nombre del archivo: Empleado.hbm.xml




<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd ">

<hibernate-mapping>
<class name = "mypersistence.Empleado" table="Empleados">

<id name = "id" column = "id">
<generator class="sequence"/>
</id>

<property name = "nombre" column = "nombre"
type = "string"></property>
<property name = "nacimiento" column = "fecha_nacimiento"
type = "timestamp" update = "false"></property>

</class>
</hibernate-mapping>


Archivo de configuracion Hibernate: Como es de esperarse este archivo define las opciones de configuración globales para la serialización y por ello normalmente sólo existe un archivo XML de configuración por aplicación. Aquí se definen: Información sobre la conexión a la base de datos, dialecto a utilizar (varía de acuerda al RDBMS utilizado), una opción que es bueno resaltar es la propiedad show_sql que muestra el SQL que ha sido ejecutado además hibernate.hbm2dll.auto crea las tablas basado en el archivo de mapeo. Aquí ahora el ejemplo de archivo de configuración hibernate:

Nombre del archivo: hibernate.cfg.xml


<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd ">

<hibernate-configuration>
<session-factory>
<property name = "show_sql">true</property>
<property name = "hibernate.dialect">
org.hibernate.dialect.PostgreSQLDialect</property>
<property name = "hibernate.hbm2dll.auto">
create-drop</property>
<property name = hibernate.connection.driver_class">
org.postgresql.Driver</property>
<property name = "hibernate.connection.username">
postgres</property>
<property name = "hibernate.connection.password">
miclave</property>
<property name = "hibernate.connection.url">
jdbc:postgresql://localhost:5432/postgres</property>
<mapping resource = "Empleado.hbm.xml" />
</session-factory>
</hibernate-configuration>


API Hibernate: Es el conjunto de herramientas que me permite realizar la serialización, es decir, esta es la maquinaria necesaria para utilizar todos los otros tres recursos antes vistos e implementar de una vez la serialización. En nuestro caso vamos a utilizar el paquete org.hibernate.*.

Veamos ahora que debemos hacer para utilizar este API Hibernate y realizar la serialización:

Creación de HibernateUtil: interactúa con el archivo de configuración. Inicia sesión.



package mypersistence;

import java.io.File;

import org.hibernate.Session;
import org.hibernate.cfg.Configuration;
import org.hibernate.SessionFactory;

public class HibernateUtil {
private final static SessionFactory sf;
static {
Configuration cfg = new Configuration();
File f = new File(
"C:\\AcercaDeJava\\Hibernate\\files\\hibernate.cfg.xml");
cfg.configure(f);
sf = cfg.buildSessionFactory();
}

public static Session currentSession(){
return sf.openSession();
}

public static void close(Session session){
session.close();
}
}


HibernateManager: Utiliza a HibernateUtil para iniciar sesión e inicia el proceso de grabación:


package mypersistence;


import java.util.Date;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;

import mypersistence.Empleado;
import mypersistence.HibernateUtil;

public class EmpleadoManager {

Session s = HibernateUtil.currentSession();

public void save(Empleado empleado){
try{
Transaction tx = s.beginTransaction();
s.saveOrUpdate(empleado);
tx.commit();
}catch(HibernateException e){
e.printStackTrace();
}
}

public static void main(String[] args) {
Empleado empleado = new Empleado();
empleado.setNombre("Rafael Atencio");
empleado.setNacimiento(new Date());

EmpleadoManager manager = new EmpleadoManager();
manager.save(empleado);
}
}


Ahora si: Aquí tengo una lista de las librerías que tuve que usar, casi todas pueden encontrarse en la página web http://www.findjar.com/index.x:

cglib-full-2.0.jar
commons-collections-3.1.jar
commons-logging-api.jar
dom4j-1.1.jar
ehcache-1.6.2.jar
hibernate-3.0.5.jar
hibernate-jpa-2.0-api-1.0.0.Final.jar
hibernate-validator-4.1.0.Final.jar
j2ee-1.4.jar
log4j-1.2.14.jar
postgresql-9.0-801.jdbc4.jar
slf4j-api-1.5.6.jar
slf4j-log4j12-1.5.6.jar
validation-api-1.0.0.GA.jar

Además hay que asegurarse que la ruta de los archivos hibernate.cfg.xml y Empleado.hbm.xml están en el Build Path o CLASSPATH.

Para ello, en eclipse, se hace clic secundario Proyecto, se elige el Build Path, luego Configure Build Path..., se va a la prestaña Source y se agrega la carpeta donde están los archivos de mapeo y configuración.

Adicionalmente como PostgreSQL no trabaja directamente con campos AUTOINCREMENT hay que realizar un objeto de secuencia hibernate ejecutando la siguiente sentencia SQL en nuestra BD Postgres:



CREATE SEQUENCE hibernate_sequence
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 1
CACHE 1;


Bueno no queda más que ejecutar la clase HibernateManager para generar un registro en Base de Datos PostgreSQL y obtener por consola el SQL que ha sido ejecutados.

Por ahora no tengo más que decir más que desearle suerte en la ejecución de su proyecto con Hibernate, cualquier duda pueden escribir comentarios en el post.

Nos vemos, ¡Hasta Luego!

miércoles, 4 de agosto de 2010

Libros de Java SE 6

Hasta hace poco, era difícil encontrar un libro que haga referencia a Java SE 6, pero me he dado cuenta que últimamente ya están disponibles en las librerías.

A mediados del año pasado encontré uno (muy famoso por cierto en cuanto a tecnología Java) llamado Profesional Java JDK 6, por W. Clay Richardson y otros autores y digo famoso debido a que tiene el sello WROX o no se si decir es de WROX.

Todavía estoy contento de mi adquisición, este libro me ha servido de ayuda para la mayoría de mis artículos tanto de Java SE 5 y Java SE 6. Comienza con un recuento de las novedades de Java 5 y luego (mucho más adelante) comienza a revelarte los secretos de Java 6.

He encontrado algunos pequeños errores, pero me parecen en algunos casos insignificantes, no hay que conformarse con lo que te dicen allí, hay que probar, analizar, ejecutar, etc... para darse cuenta que algunas veces cometen errores pequeños.

Bueno, no quiero dejar este comentario a medias, un error que encontré fue en la página 63 (hablo la versión en español) en un programa que habla sobre las anotaciones, donde hay dos clases y una interfaz, la interfaz es la que aparece "anotada", se llama TestParameters.

En una clase llamada StringUtility tiene un método concat con el siguiente cuerpo:



public String concat (String s1, String s2){
return (s1 + s2);
}

//y un método testConcat() de la siguiente manera

public boolean testConcat(){
String s1 = "test";
String s2 = " 123";
return(concat(s1,s2).equals("test 123");
}


Por supuesto, esto no arroja ningún error de compilación, luego en la clase TestFramework está el método invoke que utiliza la reflección (java.lang.reflect) de la siguiente manera:



static String invoke(Method m, Object o){
String result = "PASSED";
try{
m.invoke(o);
} catch (Exception ex){
System.out.println("Exception: " + ex +
"\n" + ex.getCause);
result "FAILED";
}
return (result);
}


Que tampoco tiene error de compilación, pero hay un error lógico, supongamos que el método testConcat() devuelva false debido a que se escribió return(concat(s1,s2).equals("test 12");, para el método invoke no hay error (No hay Exception) y por lo tanto imprime igualmente PASSED.

Debido a este error debe cambiarse el método testConcat a algo como esto:



public boolean testConcat() throws Exception
{
String s1 = "test";
String s2 = " 123";
boolean resultado = (concat(s1,s2).equals("test 123"));
if(resultado){
return resultado;
}
throw new Exception();
}


De esta forma, se arroja una Exception si el método falla y se captura cuando se hace la llamada m.invoke(o), es decir result sería "FAILED".

Espero haberme explicado; este error fue el mas grave que encontré, la verdad tiene muchos errores de transcripción pero creo que este tipo de error es insignificante para el buen lector que le interesa el contenido.

Bueno, siguiendo con el tema de los libros, decía que yo adquirí este libro aproximadamente hace un año y me sentí bastante satisfecho de su contenido, pero la verdad este post lo escribo para que tengamos en cuenta que ya existen muchos libros de Java SE 6.0 en las librerías y aquí le tengo otros dos que ví en ellas:

Java. Como Programar. Septima Edición. DEITEL. (Basado en Java SE 6)

Yo adquirí este libro en su versión Java SE 1.4 y me pareció excelente, es extenso en sus explicaciones y ejemplos y además te da un CD con todo el código fuente del libro. Fue muy buena fuente de consulta y me dio herramientas para realizar algunas de las preguntas de nuestros post acerca de la preparación para la prueba Sun Java Programmer que se escribieron en este blog desde Agosto hasta Diciembre 2009.

Mi sorpresa mayor fue que al lado de este libro, otro que se llama Java. Manual de referencia. Séptima edición por Herbert Schildt. McGrawHill (también basado en Java SE 6)de este no tengo referencias, pero he visto que el mismo autor en otros libros conocidos.

Lo que me alegra es que ya en las librerías nos presenten bibliografías de la última versión de Java.

Bueno por ahora no tengo más que agregar, si a la disposición de seguir aportando acerca de la tecnología Java y a la espero de una nueva versión que podamos probar y que nos siga generando ganancias en el sentido del conocimiento, ahorro de tiempo de programación y administración de recursos de sistema.

Hasta la próxima.

sábado, 31 de julio de 2010

La nueva clase SystemTray de Java SE 6

La nueva clase SystemTray permite (si es soportado por el sistema operativo) crear un pequeño ícono en la bandeja de escritorio, de tal manera que pueda accederse a ella sin necesidad de crear una ventana común. Sólo un pequeño ícono que para windows por ejemplo se encuentra cerca del reloj, donde generalmente corren las aplicaciones como Antivirus, reloj, etc...

De hecho creo que este es el artículo mas corto que escrito, debido a lo fácil que se me hizo realizarlo veamos el ejemplo:



package traydemo;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class DemoTray {
public static void main(String[] args) {
final TrayIcon icono;
Image imagen = null;
final JFrame ventana = new JFrame("Principal");
if(SystemTray.isSupported()){
SystemTray tray = SystemTray.getSystemTray();
imagen = Toolkit.getDefaultToolkit().
getImage("Tray.jpg");
PopupMenu popup = new PopupMenu();
MenuItem optComenzar = new MenuItem("Comenzar...");
MenuItem optSalir = new MenuItem("Salir");
optSalir.addActionListener( new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
System.exit(0);
}
});
optComenzar.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ventana.setSize(300,300);
ventana.setVisible(true);
}
});
popup.add(optComenzar);
popup.add(optSalir);
icono = new TrayIcon(imagen, "Tray", popup);
icono.setImageAutoSize(true);
icono.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e){
if(e.getButton() == 1){
ventana.setSize(300,300);
ventana.setVisible(true);
}
}
});
try {
tray.add(icono);
} catch (AWTException e1) {
System.out.println(e1.getStackTrace());
}
}
}
}


Creo que es fácil entender y usar.

Primero que todo hay que preguntar si nuestro sistema operativo soporta SystemTray (es decir, SystemTray.isSupported()) de ser así creo la instancia de SystemTray, elijo la imagen que va a ser colocada en la bandeja de escritorio a través de la clase Image, recuerde que esta imagen debe estar en la misma ruta del proyecto Java.

Creo un menú auxiliar (PopupMenu) que se desplegará si el usuario hace clic secundario en este ícono, tendrá dos opciones comenzar la aplicación (llama a ventana Swing.JFrame principal de la aplicación por supuesto) o salir (System.exit(0)), creo un TrayIcon llamado icono que que será el ícono de la aplicación pasándole la imagen seleccionada, ajusto con setImageAutoSize(true) para que la imagen se ajuste al tamaño del ícono de la bandeja de Escritorio, si el usuario hace clic en el ícono con el botón principal también se desplega la ventana principal. Por último y muy importante es agregar el icono (TrayIcon) al tray, una vez hecho esto, el ícono queda visible en el system tray, es decir, la bandeja de escritorio, cerca del reloj, antivirus, etc...

Bueno esto es todo por ahora, espero haberme explicado y les sea de utilidad este post. Nos vemos en una próxima edición.