Visualizzazione post con etichetta java. Mostra tutti i post
Visualizzazione post con etichetta java. Mostra tutti i post

mercoledì 9 febbraio 2011

ScrollableComposite: quello che la documentazione non dice

Come dice la documentazione, lo ScrolledComposite consente di visualizzare il suo contenuto con lo scorrimento delle barre verticali e orizzontali.

La documentazione è abbastanza dettagliata, fornisce anche un esempio.


public static void main(String[] args)
{
Display display = new Display();
Color red = display.getSystemColor(SWT.COLOR_RED);
Color blue = display.getSystemColor(SWT.COLOR_BLUE);
Shell shell = new Shell(display);
shell.setLayout(new FillLayout());

// set the size of the scrolled content - method 1
final ScrolledComposite sc1 = new ScrolledComposite(shell, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
final Composite c1 = new Composite(sc1, SWT.NONE);
sc1.setContent(c1);
c1.setBackground(red);
GridLayout layout = new GridLayout();
layout.numColumns = 4;
c1.setLayout(layout);
Button b1 = new Button(c1, SWT.PUSH);
b1.setText("first button");
c1.setSize(c1.computeSize(SWT.DEFAULT, SWT.DEFAULT));

// set the minimum width and height of the scrolled content - method 2
final ScrolledComposite sc2 = new ScrolledComposite(shell, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
sc2.setExpandHorizontal(true);
sc2.setExpandVertical(true);
final Composite c2 = new Composite(sc2, SWT.NONE);
sc2.setContent(c2);
c2.setBackground(blue);
layout = new GridLayout();
layout.numColumns = 4;
c2.setLayout(layout);
Button b2 = new Button(c2, SWT.PUSH);
b2.setText("first button");
sc2.setMinSize(c2.computeSize(SWT.DEFAULT, SWT.DEFAULT));

Button add = new Button(shell, SWT.PUSH);
add.setText("add children");
final int[] index = new int[]{0 };
add.addListener(SWT.Selection, new Listener()
{

public void handleEvent(Event e)
{
index[0]++;
Button button = new Button(c1, SWT.PUSH);
button.setText("button " + index[0]);
// reset size of content so children can be seen - method 1
c1.setSize(c1.computeSize(SWT.DEFAULT, SWT.DEFAULT));
c1.layout();

button = new Button(c2, SWT.PUSH);
button.setText("button " + index[0]);
// reset the minimum width and height so children can be seen - method 2
sc2.setMinSize(c2.computeSize(SWT.DEFAULT, SWT.DEFAULT));
c2.layout();
}
});

shell.open();
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}


L'esempio funziona, aggiungendo del contenuto (in questo caso dei bottoni) le scrollbar appaiono non appena il contenuto diventa più grande dell'area visibile.
Nella pratica però, una situazione così semplice (composite direttamente nella shell) non avviene praticamente mai, è molto più probabile che ci sia un meccanismo di scatole cinesi.. composite dentro composite dentro composite.
Se proviamo a mettere un Composite parent che contiene il nostro ScrollableComposite, già rischiamo che il meccanismo non funzioni più. Perchè?

La documentazione non dice che il parent di uno ScrollableComposite deve sempre avere un layout di tipo FillLayout. Questo è fondamentale per farlo funzionare.

Sotto un esempio più complesso del precedente:


public static void main(String[] args)
{
Display display = new Display();
Color blue = display.getSystemColor(SWT.COLOR_BLUE);
Shell shell = new Shell(display);
shell.setLayout(new FillLayout());

FormToolkit toolkit = new FormToolkit(Display.getCurrent());

Composite container = toolkit.createComposite(shell, SWT.NONE);

// container.setLayout(new GridLayout()); NON FUNZIONA!!!!
container.setLayout(new FillLayout());

container.setLayoutData(new GridData(GridData.FILL_BOTH));

Section section = toolkit.createSection(container, ExpandableComposite.TWISTIE
| ExpandableComposite.COMPACT
| ExpandableComposite.TITLE_BAR);

section.setText("Section");
section.setLayout(new GridLayout());
section.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
section.setVisible(false);

// set the minimum width and height of the scrolled content - method 2
final ScrolledComposite sc2 = new ScrolledComposite(section, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
sc2.setExpandHorizontal(true);
sc2.setExpandVertical(true);
final Composite c2 = new Composite(sc2, SWT.NONE);
sc2.setContent(c2);
c2.setBackground(blue);
GridLayout layout2 = new GridLayout();
layout2.numColumns = 4;
c2.setLayout(layout2);

for (int i = 0; i < 100; i++)
{
toolkit.createButton(c2, "button" + i, SWT.PUSH);
}

sc2.setMinSize(c2.computeSize(SWT.DEFAULT, SWT.DEFAULT));
section.setClient(sc2);
section.setVisible(true);
section.setExpanded(false);

shell.open();
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}


Il layout di tipo Fill non consente di dimensionare a proprio piacimento lo spazio per posizionare per esempio due composite affiancati o uno sopra l'altro con altezze differenti (come invece consente il GridLayout). Ma ci sono altri meccanismi utili per fare questo come per esempio il SashForm.

martedì 2 novembre 2010

Monitoraggio e controllo delle applicazioni tramite JMX - Introduzione

JMX (Java Management Extensions) è una tecnologia introdotta nella versione 5.0 di Java che permette il monitoraggio ed il controllo delle applicazioni in maniera standardizzata.

Ogni applicazione, tramite gli MBean (Managed Beans) può esporre all'esterno sia delle proprietà (che possono essere monitorate) che delle operazioni (che si possono eseguire per modificare il comportamento dell'applicazione in runtime). L'applicazione che espone gli MBean può essere vista come un server a cui i client JMX (come, ad esempio, il tool jconsole) possono collegarsi.


La tecnologia JMX offre una maniera semplice e standardizzata per gestire le applicazioni. Il vantaggio per lo sviluppatore è che consente di implementare solo la parte strettamente legata all'applicazione da controllare, mentre fornisce tutta l'infrastruttura per esporre all'esterno le interfacce, gestendo in maniera (quasi) trasparente tutta la parte relativa alla comunicazione tra server e client.

Questa tecnologia è utilizzata anche all'interno della stessa JRE, ad esempio per permettere di controllare l'occupazione della memoria o per invocare su richiesta il garbage collector.


Vediamo un esempio di utilizzo di JMX per il controllo di una semplice applicazione, supponiamo di avere un semplice oggetto Printer che modella la scrittura periodica di una stampa su console:

package it.sinossi.poc.mbeanpoc;

public class Printer {

 private String text;

 private boolean printEnabled = true;

 private int sleepTime = 500;

 private boolean stop = false;

 public Printer(String text) {
  this.text = text;
 }

 public String getText() {
  return text;
 }

 public void setText(String text) {
  if (text != null && !text.isEmpty()) {
   this.text = text;
  } else {
   throw new IllegalArgumentException("text cannot be null or empty");
  }
 }

 public boolean isPrintEnabled() {
  return printEnabled;
 }

 public void setPrintEnabled(boolean printEnabled) {
  this.printEnabled = printEnabled;
 }

 public int getSleepTime() {
  return sleepTime;
 }

 public void setSleepTime(int sleepTime) {
  if (sleepTime >= 100 && sleepTime <= 1000) {
   this.sleepTime = sleepTime;
  } else {
   throw new IllegalArgumentException(Integer.toString(sleepTime) + " is not in range [100, 1000]");
  }
 }

 public void stop() {
  stop = true;
 }

 public boolean shouldStop() {
  return stop;
 }

 public void print() {
  System.out.println(text);
 }

}

La classe permette di modificare alcuni parametri come il testo ed il tempo di attesa, in più si possono eseguire le azioni di pausa, ripresa e stop, notare che i metodi setText e setSleepTime lanciano delle eccezioni nel caso in cui siano invocate con argomenti non validi. Sottolineo come non sia presente in nessuna parte di codice relativa a JMX.

Scriviamo un semplice main che permetta di vedere il codice all'opera

package it.sinossi.poc.mbeanpoc;

public class Main {

 public static void main(String[] args) throws Exception {

  Printer p = new Printer("Ciao");

  int counter = 0;

  while (!p.shouldStop()) {
   if (p.isPrintEnabled()) {
    System.out.print(Integer.toString(counter) + "  ");
    p.print();
   }
   counter++;
   Thread.sleep(p.getSleepTime());
  }
  System.out.println("Exiting");
 }

}

Avviando la classe Main si vedrà in console la stampa periodica della scritta "Ciao".


A questo punto si deve introdurre la componente fondamentale della tecnologia JMX: gli MBean. Gli MBean sono delle normali classi, simili ai java bean, che però devono implementare una interfaccia che sarà appunto quella che definisce i metodi di monitoraggio e controllo esposti all'esterno. Nel nostro caso l'interfaccia si chiamerà PrinterControlMBean e l'implementazione PrinterControl (questo tipo di nomenclatura è obbligatoria, l'interfaccia deve chiamarsi MBean e la relativa implementazione deve chiamarsi ).


package it.sinossi.poc.mbeanpoc;

public interface PrinterControlMBean {

 String getText();

 void setText(String text);

 int getSleepTime();

 void setSleepTime(int millis);

 void pause();

 void resume();

 public void stop();

}


package it.sinossi.poc.mbeanpoc;

public class PrinterControl implements PrinterControlMBean {

 private Printer printer;

 public PrinterControl(Printer printer) {
  this.printer = printer;
 }

 public String getText() {
  return printer.getText();
 }

 public void pause() {
  printer.setPrintEnabled(false);
 }

 public void resume() {
  printer.setPrintEnabled(true);
 }

 public void stop() {
  printer.stop();
 }

 public void setText(String text) {
  printer.setText(text);
 }

 public int getSleepTime() {
  return printer.getSleepTime();
 }

 public void setSleepTime(int millis) {
  printer.setSleepTime(millis);
 }

}

L'interfaccia PrinterControlMBean definisce le funzionalità della nostra classe Printer che vogliamo siano controllabili dall'esterno, in questo caso l'implementazione di questa interfaccia è molto semplice in quanto tutti i metodi delegano all'oggetto Printer, passato nel costruttore. Ancora una volta non è presente codice relativo alle API JMX.

Ora che abbiamo una classe che vogliamo sia gestita dall'esterno (Printer) e l'MBean che determina le funzionalità esposte all'esterno (PrinterControlMBean, PrinterControl), dobbiamo completare con la registrazione sul sistema del nostro MBean in modo che questo venga esposto all'esterno.

Questo verrà fatto nella classe Main, in cui viene istanziato l'MBean e viene registrato nell'MBean Server, che è il componente fornito dal sistema che si occupa di esporre all'esterno gli MBean.

public class Main {

 public static void main(String[] args) throws Exception {

  Printer p = new Printer("Ciao");

  registerMBean(p);

  int counter = 0;

  [...]
 }

 private static void registerMBean(Printer p) throws Exception {
  PrinterControl mbean = new PrinterControl(p);
  ObjectName objectName = new ObjectName("printercontrol:type=printer,name=console_printer");
  MBeanServer server = ManagementFactory.getPlatformMBeanServer();
  server.registerMBean(mbean, objectName);
 }

}

Ogni MBean viene registrato in associazione ad un'istanza di ObjectName, questo permette l'identificazione univoca della risorsa gestita dall'MBean. L'ObjectName è costruito con una stringa che codifica un dominio (in questo caso il dominio è "printercontrol") più delle coppie chiave=valore (type = printer, name = console_printer); gli spazi presenti nella stringa non vengono eliminati, quindi meglio che non ce ne siano.

A questo punto non resta che far pratire l'applicazione e successivamente avviamo jconsole.





Connettiamoci alla JVM relativa al nostro progetto.
Dal tab MBeans si possono vedere e controllare tutti gli MBeans registrati sul sistema, il nostro si trova sotto il dominio it.sinossi.
Jconsole permette di vedere e modificare gli attributi e di invocare i metodi esposti.



Proviamo a mettere in pausa, modificare la stringa che viene stampata, riattivare la stampa e poi stoppare il programma. Nella console si può vedere come le azioni svolte si ripercuotono sul comportamento del programma.


Ricordiamo che la classe Printer permette di impostare uno sleepTime compreso tra 100 e 1000 millisecondi, nel caso in cui si inserisca un valore non valido viene lanciata un'eccezione. Per vedere come si comporta il sistema proviamo ad impostare il valore 10. Il risultato è quello che si vede in figura: l'eccezione impedisce di impostare il valore non corretto, questo senza influire sul regolare funzionamento dell'applicazione che continua a girare senza accorgersi di niente.



See also:
Sorgenti del progetto su github
JMX Home Pape
Java Tutorial on JMX
JMX Best Practices
Java theory and practice: Instrumenting applications with JMX

martedì 26 gennaio 2010

How to start a java application for remote debugging

In order to allow remote debugging for a java application the command line should be customized with the following arguments:
java -Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n MyClass
-Xdebug enables debugging mode;
-Xrunjdwp configures Java Debug Wire Protocol with sub-options; like the communication mode between jvm and debugger, the listening port, whether wait until the debugger connection or not, etc. See links for further details

See also:
-Xrunjdwp sub-options
Remote Debugging with Eclipse

venerdì 23 ottobre 2009

Fare il debug dei test avviati con maven

Nel caso in cui si presenti la necessità di fare il debug di test avviati tramite maven è sufficiente aggiungere il flag -Dmaven.surefire.debug.
Da console:
mvn test -Dmaven.surefire.debug
questo fa in modo che i test, subito dopo l'avvio, si mettano in pausa in attesa che un debugger remoto si colleghi alla porta 5005 (valore di default eventualmente modificabile).

Se volessimo connettere Eclipse in debug non dovremmo fare altro che creare una nuova Debug Configuration (menù Run > Debug Configuration...), selezionare il tipo "Remote Java Application" e impostare nel campo Port il valore 5005. Cliccando su Debug, Eclipse si collegherà ai test, precedentemente entrati in pausa, facendoli immediatamente ripartire. L'esecuzione si fermerà su tutti breakpoint che avremo impostato permettendo il normale debug all'interno di Eclipse.

Vedi anche:
Maven Surefire Plugin > Examples > Debugging Tests

lunedì 21 settembre 2009

Recuperare i nomi dei mesi/giorni nelle varie localizzazioni

Le localizzazioni delle stringhe relative alle date sono gestite dalla classe DateFormatSymbols.
Tramite metodi come getMonths o getWeekdays è possibile recuperare i valori che vengono poi utilizzati nella formattazione delle date.

DateFormatSymbols dfs = new DateFormatSymbols(Locale.ITALIAN);
String[] monthNames = dfs.getMonths();
for (String month : monthNames) {
 System.out.println(month);
}

L'output sarà:
gennaio
febbraio
marzo
aprile
maggio
giugno
luglio
agosto
settembre
ottobre
novembre
dicembre

Si deve fare particolare attenzione alle lettere maiuscole/minuscole delle stringhe restituite, che seguiranno la convenzione della lingua selezionata. Ad esempio in italiano i nomi dei mesi e dei giorni sono dei nomi comuni ed hanno l'iniziale minuscola (gennaio, febbraio, ...) mentre in inglese i nomi iniziano con la lettera maiuscola (January, February, ...).

Un'altra particolarità, dovuta alle stranezze della classe Calendar, sono gli array di stringhe restituiti: nel caso dei mesi l'array ha 12 elementi che hanno indici da 0 a 11; nel caso dei giorni della settimana invece l'array ha 8 elementi, con il primo elemento, quello con indice 0, contenente una stringa vuota, gli elementi successivi contengono i nomi dei giorni della settimana, a partire dalla domenica.
Qual è la causa di questa anomalia?
Questi array vengono acceduti utilizzando come indici le costanti della classe Calendar, quindi l'anomalia dell'array dei nomi dei giorni è dovuta al fatto che mentre le costanti che rappresentano i mesi partono da 0 (Calendar.JANUARY == 0), le costanti che identificano i giorni della settimana partono da 1 (Calendar.SUNDAY == 1).

DateFormatSymbols dfs = new DateFormatSymbols(Locale.ITALIAN);
String[] weekdays = dfs.getWeekdays();
for (int i = 0; i < weekdays.length; i++) {
 System.out.println(i + " " + weekdays[i]);
}
Visualizza
0 
1 domenica
2 lunedì
3 martedì
4 mercoledì
5 giovedì
6 venerdì
7 sabato

Vedi anche:
DateFormatSymbols

venerdì 18 settembre 2009

Come deprecare un metodo

Quando si vuole eliminare un metodo da una classe, evitando di interferire sul codice che da questa dipende, la soluzione sta nella deprecazione.
Dalla versione 1.5 di Java per deprecare un metodo si deve usare l'annotazione @Deprecated, che può essere usata sulle classi, sui metodi o sui campi.
E' buona norma accompagnare l'annotazione con i javadoc, usando il tag @deprecated nel cui testo è opportuno specificare perchè il metodo viene deprecato e qual è l'alternativa da utilizzare.


/**
* [...]
* @deprecated use {@link #importTrade(Trade)} instead ...
*/
@Deprecated
Long importTradeFromXML(String xml) {
...


Per creare nei javadoc un collegamento ad un altro metodo viene utilizzato il tag @link la cui sintassi è:

{@link package.class#member label}

Nel caso di overloading dei metodi si deve specificare il metodo facendo seguire al nome l'elenco dei tipi degli argomenti presi in ingresso.

Vedi anche:
How and When To Deprecate APIs
javadoc @deprecated
javadoc @link