mercoledì 2 luglio 2014

preventDefault and stopPropagation for anchors in AngularJS

Sometimes in an application we use anchor tags to trigger functions on our controller like the following:

<a href="#" data-ng-click="doSomething()">Click me</a<>

This is not a best practice, but in some cases you need to use it anyways. In this case you need to tell the browser to not to handle the anchor like a link and reload the page.

In plain javascript is really simple:

HTML
<a href="#" onclick="doSomething()">Click me</a>

Javascript
...
function doSomething(event)
{
    event.preventDefault();
    event.stopPropagation();

    alert("Hello, world!");
}
...

But now, you want to do this with AngularJS. It's really simple, like in plain javascript. So, you'll write:

HTML
<a href="#" data-ng-click="doSomething(); $event.preventDefault(); $event.stopPropagation();">Click me</a>

Javascript
...
$scope.doSomething = function()
{
    alert("Hello, world!");
}
...

lunedì 26 maggio 2014

JavaScript e AngularJS: plugin utili per lo sviluppo

Vi propongo un paio di plugin utili per lo sviluppo su AngularJS e JavaScript in generale.
  • Chrome:
    • Angular Scope Inspector
      Inspector per l'analisi delle informazioni e delle funzioni contenute negli scope
    • ng-inspector for AngularJS
      Inspector che mostra l'elenco degli scope, dei controller e delle direttive associate adogni scope.
    • JavaScript Errors Notifier
      Mostra un simbolo nella barra degli indirizzi quando avviene un errore javascript.
    • Web Developer
      Barra che mostra diverse utility (Ridimensionamento pagina, blocco cache, etc).
    • jQuery Debugger
      Mostra, tra gli strumenti per sviluppatori, gli eventi e i dati (jquery data) associati al dom
  • Firefox:
    • AngScope
      Inspector degli scope associati al DOM. Richiede Firebug
    • Web Developer
      Lo stesso plugin presente anche su chrome.
    • Firequery
      Debugger per webapp con jQuery. Mostra anche gli eventi e i dati (jquery data). Richiede Firebug

martedì 27 settembre 2011

Tomcat e VisualVM

VisualVM è un'applicazione messa a disposizione nel Java Development Kit a partire dalla versione 6, update 7, e permette di monitorare tutte le applicazioni Java in esecuzione sul sistema.
L'applicazione non va installata e l'eseguibile si trova nella cartella bin della cartella di installazione JDK.
All'avvio, VisualVM mostra in automatico tutte le applicazioni che sono in esecuzione sul sistema, ma tramite alcune configurazioni, può anche monitorare JVM remote che abbiano porte aperte e accessibili tramite JMX.

Per farlo è sufficiente aggiungere alla configurazione di Tomcat dell'applicazione remota, le seguenti righe

-Dcom.sun.management.jmxremote.port=8086
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false


Dopo di che sarà necessario riavviare Tomcat e aggiungere una connessione JMX in VisualVM che punti alla porta 8086.



Per approfondire:
Monitoring Tomcat with Java VisualVM
VisualVM, troubleshooting per Java

venerdì 29 luglio 2011

ImageHyperlink



Magari può essere utile sapere che esiste un oggetto Hyperlink fatto apposta per le immagini. Esempio:

ImageHyperlink logsExcelExport = new ImageHyperlink(parent, SWT.CENTER);

Image image = ImageDescriptor.createFromURL(Platform.getBundle(AssessmentBaseErrorCodes.ASSESSMENT_CLIENT_BASE_PLUGIN_ID).getEntry("icons/action-savexls.png")).createImage();
logsExcelExport.setImage(image);
logsExcelExport.setLayoutData(GridDataFactory.swtDefaults().align(SWT.END, SWT.END).hint(17, SWT.FILL).create());
logsExcelExport.setToolTipText("Esporta in Excel");


Come dice il nome, genera un'immagine cliccabile.

lunedì 18 luglio 2011

Testing methods secured with Spring Security

Spring Security allows to secure access to methods, allowing execution only for user with defined roles.

For example, using annotation to configure the roles required:

public interface MyService
{

 @PreAuthorize("hasRole('USER') OR hasRole('ADMIN')")
 void hello();

 @PreAuthorize("hasRole('ADMIN')")
 void bye();

}

In order to call the bye() method the user must have the ADMIN role, for hello() method also the the USER role is enough.

If a secured method is called by a user without the needed roles is raised an exception of type org.springframework.security.access.AccessDeniedException.

Method security is active even during tests, so we have developed an annotation and a listener that can be used to setup easily user roles when testing (we use TestNG, probably something like this can be done also with Junit).

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface Authenticate {

 String username() default "";

 String[] roles() default {};
}

public class AuthenticationListener implements IInvokedMethodListener
{

 public static final String DEFAULT_USERNAME = "default";

 private static final String FAKE_PASSWORD = "fakePassword";

 /**
  * {@inheritDoc}
  */
 @Override
 public void beforeInvocation(IInvokedMethod method, ITestResult testResult)
 {
  if (method.isTestMethod())
  {
   ITestNGMethod testMethod = method.getTestMethod();
   Method javaMethod = testMethod.getMethod();
   Authenticate userAnnotation = javaMethod.getAnnotation(Authenticate.class);
   if (userAnnotation != null)
   {
    String username = userAnnotation.username();
    if (username == null || username.isEmpty())
    {
     username = DEFAULT_USERNAME;
    }
    String[] roles = userAnnotation.roles(); // role may be null/empty
    authenticateUser(username, roles);
   }
  }
 }

 private void authenticateUser(String username, String... roles)
 {
  Set<GrantedAuthority> grantedAuthorities = new HashSet<GrantedAuthority>();
  if (roles != null)
  {
   for (String role : roles)
   {
    grantedAuthorities.add(new GrantedAuthorityImpl(role));
   }
  }
  UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, FAKE_PASSWORD,
   grantedAuthorities);
  SecurityContextHolder.getContext().setAuthentication(token);
 }

 /**
  * {@inheritDoc}
  */
 @Override
 public void afterInvocation(IInvokedMethod method, ITestResult testResult)
 {
  if (method.isTestMethod())
  {
   SecurityContextHolder.clearContext();
  }
 }

}

The Authenticate annotation allows to set the username and a list of roles that must be used when executing a test method, the AuthenticationListener take care to configure the authentication token before test execution and to reset the security context after execution.

This is an example of a test method that uses the Authenticate annotation:

@ContextConfiguration("classpath:META-INF/spring/applicationContext*.xml")
@Listeners(AuthenticationListener.class)
public class MyServiceTest extends AbstractTestNGSpringContextTests
{

 @Autowired
 private MyService myService;

 @Test
 @Authenticate(username = "pippo", roles = {"ADMIN" })
 public void testCallHelloAsAdmin()
 {
  myService.hello();
 }

 @Test
 @Authenticate(username = "pippo", roles = {"USER" })
 public void testCallHelloAsUser()
 {
  myService.hello();
 }

 @Test
 @Authenticate(username = "pippo", roles = {"ADMIN" })
 public void testCallByeAsAdmin()
 {
  myService.bye();
 }

 @Test(expectedExceptions = AccessDeniedException.class)
 @Authenticate(username = "pippo", roles = {"USER" })
 public void testCallByeAsUser()
 {
  myService.bye();
 }
}


See also:
The demo project on GitHub
Spring Security
TestNG Listeners
Annotations

martedì 12 luglio 2011

Liquibase migration for Spring Security tables

Spring security default implementation requires to access to a database in order to do its job.

Here is the liquibase migration file for creating these tables.

The changesets 1, 2 and 2a create tables for authentication and authorization.
Changesets 3, 3a, 4, 5, 6 and 6a create tables for domain object security (ACL).
Tha last changeset, 7, insert some data in tables (2 users wioht their roles).



This migration is customized for MySql database, enforces the InnoDB engine when creates tables, but should work also on other databases.

See also:
Spring Security
Security Database Schema
Liquibase

mercoledì 22 giugno 2011

Recupero della versione del pom all'interno di una classe java

Java permette di recuperare la versione di un jar tramite codice. La versione deve essere dichiarata all'interno del manifest nell'attributo Implementation-Version.
Per un progetto maven è sufficiente customizzare la generazione del manifest configurando il maven-jar-plugin nel pom:
[...]

 
  
   org.apache.maven.plugins
   maven-jar-plugin
   true
   
    
     
      
      it.sinossi.poc.Main
      
      true
     
    
   
  
 

[...]

Nel codice java possiamo recuperare la versione con il seguente codice:
String version = getClass().getPackage().getImplementationVersion(); 

Questa soluzione funziona solo eseguendo il jar, se per esempio viene eseguito
il codice da eclipse la variabile version sarà null.


Per maggiorni informazioni vedere anche:

Package.getImplementationVendor()
Maven - Manifest customization

lunedì 28 febbraio 2011

Netstat: ricavare informazioni sulla rete

Dal man di netstat:
Netstat prints information about the Linux networking subsystem.
Si tratta di un comodo comando che permette di ottenere informazioni sul sistema di rete.

Lanciando il comando senza parametri vengono visualizzati i socket unix aperti e le connessioni internet attive (connessione ai server di posta, dns, etc)

Se invece si volessero visualizzare i socket aperti verso l'esterno (in ascolto) è necessario passare qualche altro parametro. Il comando

netstat -l --inet


mostra i socket aperti, ma è lento poiché esegue delle query dns per risalire al nome del dominio a partire dall'indirizzo IP. Usando il parametro -n, netstat mostrerà direttamente gli indirizzi IP e i numeri delle porte.

Se oltre a sapere quali socket sono attivi, si vuole sapere quali processi li stanno utilizzando, il comando

netstat -p -n -l --inet

permette di scoprirlo.

I parametri utilizzati finora sono:
-p stampa il nome del programma e il pid del processo principale
-n stampa indirizzi numerici al posto di nomi di host, tipi di porte e nomi utente
-l stampa solo i socket in ascolto (normalmente questa opzione non è attiva)
--inet stampa solo i socket di tipo inet (i classici socket tcp/udp); esclude la visualizzazione dei socket unix

Altri parametri utili sono:
-e visualizza informazioni estese (utente esecutore del processo, etc)
-c netstat rimane in esecuzione e ogni secondo mostra l'informazione richiesta
--tcp visualizza solo i socket TCP
--udp visualizza solo i socket UDP
-4 visualizza solo i socket via IPv4
-6 visualizza solo i socket via IPv6

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.

venerdì 4 febbraio 2011

Eclipse RCP: aggiungere voci di menu dinamiche

Eclipse mette a disposizione un semplice meccanismo che permette di aggiungere in maniera dinamica delle voci ad un menu contestuale.

Per creare un menu è necessario dichiarare nel plugin.xml un estension point org.eclipse.ui.menus.
Supponiamo di voler aggiungere un nuovo menu alla toolbar dell'applicazione.
Aggiungiamo all'extension poin org.eclipse.ui.menus un menuContribution con

locationURI="menu:org.eclipse.ui.main.menu?after=additions"

Lo schema menu indica che il nuovo menu deve apparire sulla toolbar, mentre after=additions indica che deve apparire dopo i menu standard.

Al menuContribution aggiungiamo ora un menu, indicando id e label.


point="org.eclipse.ui.menus">
locationURI="menu:org.eclipse.ui.main.menu?after=additions">
id="it.sinossi.menu.toolbarMenu"
label="Menu Dinamico">





Aggiungiamo ora al menu un nodo di tipo dynamic e specifichiamo gli attributi id e class


point="org.eclipse.ui.menus">
locationURI="menu:org.eclipse.ui.main.menu?after=additions">
id="it.sinossi.menu.toolbarMenu"
label="Menu Dinamico">
id="it.sinossi.menu.sinossiCompoundContributionItem"
class="it.sinossi.menu.SinossiCompoundContributionItem">






La classe it.sinossi.menu.sinossiCompoundContributionItem deve estendere la classe astratta org.eclipse.ui.actions.CompoundContributionItem e implementare il metodo getContributionItems


import org.eclipse.jface.action.IContributionItem;
import org.eclipse.swt.SWT;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.CompoundContributionItem;
import org.eclipse.ui.menus.CommandContributionItem;
import org.eclipse.ui.menus.CommandContributionItemParameter;


public class SinossiCompoundContributionItem extends CompoundContributionItem
{
private static int counter = 0;

@Override
protected IContributionItem[] getContributionItems()
{
final CommandContributionItemParameter contributionParameter =
new CommandContributionItemParameter(
PlatformUI.getWorkbench().getActiveWorkbenchWindow(),
"it.sinossi.menu.sinossiCompoundContributionItem",
"it.sinossi.command.dynamicCommand",
SWT.NONE);
contributionParameter.label = "Dynamic Menu Item " + counter++;
items[i] = new CommandContributionItem(contributionParameter);
}
return new IContributionItem[] { new CommandContributionItem(contributionParameter) };
}
}


Il metodo restituisce un array di IContributionItem. Ogni elemento dell'array è un oggetto di classe org.eclipse.ui.menus.CommandContributionItem, e rappresenta una voce da aggiungere al menu. Ogni CommandContributionItem è costruito a partire da un org.eclipse.ui.menus.CommandContributionItemParameter, che contiene le caratteristiche della voce stessa.
Nel suo costruttore infatti devono essere specificati l'id dell'elemento dynamic a cui associare il CommandContributionItem e l'id del command che dovra essere eseguito quando la voce viene selezionata.
Si noti che il metodo getContributionItems viene eseguito ogni volta che viene aperto il menu, perciò la variabile counter verrà incrementata ogni volta e questo fa si che la label della voce di menu sia aggiornata dinamicamente.

Nel plugin.xml aggiungiamo l'estension point org.eclipse.ui.commands per definire il command e l'handler di default che saranno attivati alla selezione della voce


point="org.eclipse.ui.commands">
id="it.sinossi.command.dynamicCommand"
defaultHandler="it.sinossi.command.DynamicCommand"
name="Dynamic Command">


mercoledì 5 gennaio 2011

Concaterare liste con Spring

E' possibile concatenare liste di oggetti in Spring.
Con Spring 2.5 esistono due possibilità:

1) utilizzando la gerarchia di bean





Foo






Bar






Ernie
Bert





In questo modo listTree conterrà [Foo, Bar, Ernie, Bert].
Se non si vuole introdurre una gerarchia di bean, bisogna seguire un'altra strada.

2) creando una classe apposita che esegue la concatenazione


package com.company.utils.spring;
import java.util.*;
import org.springframework.beans.factory.config.ListFactoryBean;

public class ListMergerFactory extends ListFactoryBean
{
private final List listOfLists;

public ListMergerFactory(List listOfLists) throws Exception
{
this.listOfLists = listOfLists;
setSourceList(new ArrayList());
}

protected Object createInstance()
{
List listOrigin = (List) super.createInstance();
for (Iterator iter = listOfLists.iterator(); iter.hasNext();)
{
List element = (List) iter.next();
listOrigin.addAll(element);
}
return listOrigin;
}
}


E utilizzandola per creare la lista finale:






Foo






Bar















Ernie
Bert





Anche in questo modo listTree conterrà [Foo, Bar, Ernie, Bert].

lunedì 20 dicembre 2010

Securely delete files in Linux

Shred is a command line utility which can be used to securely delete files or entire file-systems.
It overwrites the file repeatedly (on my system default is 3 times, but can be specified) in order to make harder to recover the data with professional tools.
As usual for Unix's utility you can tune the program specifying parameters such as the number of overwrites.

Examples


Delete a single file:
shred -f -u -v /home/marco/file_with_secrets.txt
  • -f change permissions to allow writing if necessary
  • -u truncate and remove file after overwriting
  • -v show progress

Wipe an entire disk partition:
shred -n 10 -z -v  /dev/sdb3
  • -n # Overwrite # times instead of the default
  • -z add a final overwrite with zeros to hide shredding

Note: shred could be not so effective overwriting files in journaled file-systems (like ext3, ext4 ReiserFS, XFS, JFS) or RAID based file-systems.

Links:


shred invocation
shred - Linux man page

venerdì 10 dicembre 2010

How to mirror a remote subversion repository

When dealing with a remote repository the svnadmin command doesn't work, because it can only be used on the machine that holds the repository. But Subversion meets our need with the svnsycn command.

Svnsync works by essentially asking the Subversion server to “replay” revisions, one at a time. Neither the source nor the target repository needs to be locally accessible to machine on which svnsync is running, all the requirements are read access to the source repository and read/write access to the target/mirror repository.

Svnsync stores its bookkeeping information in special revision properties on revision 0 of the destination repository, but in order to make changes to revision properties you'll need to explicitly implement the pre-revprop-change hook, and your script must allow svnsync to set and change its special properties.

Let's see a script for local mirroring a remote svn repository:

TARGET_REPO_PATH=/tmp/svn/mirror_repo
SOURCE_REPO_PATH=https://xyz.svn.sourceforge.net/svnroot/xyz
SOURCE_REPO_USER=anonymous

svnadmin create $TARGET_REPO_PATH
echo '#!/bin/sh' > $TARGET_REPO_PATH/hooks/pre-revprop-change
chmod +x $TARGET_REPO_PATH/hooks/pre-revprop-change
svnsync init file://$TARGET_REPO_PATH $SOURCE_REPO_PATH --source-username $SOURCE_REPO_USER
svnsync sync file://$TARGET_REPO_PATH --source-username $SOURCE_REPO_USER


In line 5 we create the local target repository

In lines 6 and 7 we create the pre-revprop-change hook and make it executable

In line 8  we register in our target repository the fact that it will be a mirror of the source repository. We do this using the svnsync initialize subcommand. Our target repository will now remember that it is a mirror of the public Subversion source code repository (in Subversion 1.5, you can use svnsync to mirror only some subtree of the repository)

In line 9  with a single subcommand, we can tell svnsync to copy all the as-yet-unmirrored revisions from the source repository to the target. Svnsync performs careful bookkeeping that allows it to be safely interrupted and restarted without ruining the integrity of the mirrored data.
Do not modify a mirror repository in such a way as to cause its version history to deviate from that of the repository it mirrors. The only commits and revision property modifications that ever occur on that mirror repository should be those performed by the svnsync tool.

Whenever we want to update the mirror applying the changes from the remote source repository we just have to call the svnsync sync command as in line 9 of the example script.


Copyright note: most of this post comes from Version Control with Subversion.

See also:

Version Control with Subversion - Repository Administration - Repository Replication
Version Control with Subversion - Repository Administration - Implementing Repository Hooks
Version Control with Subversion - Repository Hooks - pre-revprop-change

venerdì 26 novembre 2010

Come abilitare lo shortcut di Eclipse "Search Occurrences in File" nell'ambiente Gnome

Come abilitare lo shortcut di Eclipse "Search Occurrences in File" nell'ambiente Gnome

Durante l'analisi del codice i comandi più utili messi a siposizione da Eclipse sono probabilemnte quelli di ricerca (sotto la voce di menù Search). Tra questi uno di quelli che uso più spesso è Search > Occurrences in File, che, come dice il nome, cerca le occorrenze dell'elemento selezionato all'interno del file aperto. La particolarità di questo comando è che, a differenza di altri comandi di ricerca come ad esempio Search > References, nella vista dei risultati della ricerca mostra proprio il contenuto delle righe in cui è presente il riferimento, permettendo di individuare in maniera molto semplice l'uso che viene fatto.

Esempio di Search References per la variabile "t":



Esempio di Search Occurrences in File per la variabile "t":




Il problema nell'usare questo comando nasce negli ambienti Gnome, in cui quello che è lo shortcut predefinito (SHIFT + CTRL + U) è associato all'inserimento dei caratteri unicode e quindi non funziona all'interno di Eclipse.
Per modificare la combinazione di tasti predefinita:

  • Aprire Window > Preferences
  • Selezionare General > Keys
  • Inserire nel filtro di ricerca la stringa occurrences
  • Selezionare la voce Shows the Occurrences in File quick menu in corrispondenza della quale la colonna "When" indica "In Windows"
  • Modificare il campo Binding inserendo la combinazione tasti desiderata
  • Cliccare su "OK" per uscere

La combinazione tasti che ho scelto nella mia configurazione è SHIFT + CTRL + Z, in quanto era libera ed è semplice da usare anche con una sola mano.

Vedi anche:

Eclipse Help - Searching the workbench
Eclipse Help - Java Search
Eclipse Help - Search Actions
Ubuntu Documentation > Community Documentation > ComposeKey > Unicode composition
Sinossi's Blog - Come inserire caratteri speciali in Linux

giovedì 18 novembre 2010

Merge tracking con Subversion 1.5

Una delle novità introdotte nella versione 1.5 di Subversion è la capacità di tenere traccia dei merge, ovvero include quelle funzionalità che prima venivano garantite da tool esterni come lo script python svnmerge.py.

La storia dei merge effettuati viene mantenuta tramite la proprietà svn:mergeinfo.

Supponiamo di avere nel trunk il ramo di sviluppo principale e di avere su un branch chiamato feature_dev gli sviluppi relativi ad una nuova funzionalità. Alla fine dello sviluppo il branch feature_dev dovrà essere riportato sul trunk.

I percorsi svn coinvolti sono:
  • https://svn.sinossi.it/svn/myproject/trunk
  • https://svn.sinossi.it/svn/myproject/branches/feature_dev
I comandi messi a disposizione da SVN per gestire il merge sono:
  • svn merge
  • svn mergeinfo

Il primo serve per effettuare il merge vero e proprio, il secondo mostra delle informazioni relative ai merge.

Poichè la destinazione del merge è il trunk usiamo questo come working copy. Vediamo lo stato attuale del merge
> svn co https://svn.sinossi.it/svn/myproject/trunk
> cd trunk
trunk> svn mergeinfo https://svn.sinossi.it/svn/myproject/branches/feature_dev
[nessun output]
trunk> svn mergeinfo --show-revs eligible https://svn.sinossi.it/svn/myproject/branches/feature_dev
r195
r196
r197
r198
r220
r221

Poichè ci interessa modificare il trunk usiamo quello come working copy, e come indirizzo di confronto quello del branch feature_dev, che è quello di rispetto a cui vogliamo conoscere lo stato dei merge fatti e/o da fare.
Il primo comando mostra l'elenco dei commit che sono stati riportati da feature_dev verso trunk (non essendo stato ancora effettuato nessun merge non è indicata nessuna revisione); il comando mergeinfo con l'opzione --show-revs eligible mostra i commit che sono stati effettuati su feature_dev e non sono ancora stati portati sul trunk.

Tramite il comando
> svn log https://svn.sinossi.it/svn/myproject/branches/feature_dev
siamo in grado di capire quali commit debbano essere riportati sul trunk e quali no.

Supponiamo di voler riportare il commit 198, il comando sarà
trunk> svn merge -c 198 https://svn.sinossi.it/svn/myproject/branches/feature_dev
trunk> svn commit -m "rientro del branch feature_dev"

Questo applicherà sul trunk le mofiche relative al commit 198 effettuato sul branch feature_dev.
Le modifiche effettuate da svn merge sono locali, quindi per renderle effettive sarà necessario committare.

Se prima di committare si lancia il comando svn status si noterà che molte cartelle sono state modificate, più di quelle coinvolte nel commit 198, questo perchè è stata aggiunta la proprietà svn:mergeinfo che tiene appunto memeoria dei merge effettuati.

Se ora riproviamo ad esaminare lo stato dei merge avremo
trunk> svn mergeinfo https://svn.sinossi.it/svn/myproject/branches/feature_dev
r198
trunk> svn mergeinfo --show-revs eligible https://svn.sinossi.it/svn/myproject/branches/feature_dev
r195
r196
r197
r220
r221
Quindi il commit 198 risulta riportato sul trunk, gli altri sono ancora disponibili.
Supponiamo di sapere che i commit 195,196,197 non debbano essere portati, svn merge permette di considerare i commit come integrati anche senza riportare realmente sul trunk le modifiche, questo può essere fatto tramite l'opzione --record-only
trunk> svn merge --record-only -c 195,196,197 https://svn.sinossi.it/svn/myproject/branches/feature_dev
trunk> svn commit -m "rientro del branch feature_dev"
Il commit è necessario per aggiornare nel repository lo stato della proprietà svn:mergeinfo, ma con l'opzione --read-only non viene apportata nessuna modifica ai nostri file.
A questo punto lo stato è:
trunk> svn mergeinfo https://svn.sinossi.it/svn/myproject/branches/feature_dev
r195
r196
r197
r198
trunk> svn mergeinfo --show-revs eligible https://svn.sinossi.it/svn/myproject/branches/feature_dev
r220
r221
Ora le commit 195, 196 e 197 risultano integrate.


Come ulteriore esempio vediamo il caso in cui si voglia aggiornare un tag riportando un commit effettuato sul trunk.
I rami coinvolti sono
  • https://svn.sinossi.it/svn/myproject/trunk
  • https://svn.sinossi.it/svn/myproject/tags/1.0.3

Poichè la destinazione del merge è il tag usiamo questo come working copy un checkout del tag 1.0.3.
> svn co https://svn.sinossi.it/svn/myproject/tags/1.0.3
> cd 1.0.3
1.0.3> svn mergeinfo https://svn.sinossi.it/svn/myproject/trunk
[nessun output]
1.0.3> svn mergeinfo --show-revs eligible https://svn.sinossi.it/svn/myproject/trunk
r234
r237
r238
Il commit che si vuole riportare è il 238:
trunk> svn merge -c 238 https://svn.sinossi.it/svn/myproject/trunk
trunk> svn commit -m "aggiornamento del tag 1.0.3"
Lo stato del merge ora sarà:
1.0.3> svn mergeinfo https://svn.sinossi.it/svn/myproject/trunk
r238
1.0.3> svn mergeinfo --show-revs eligible https://svn.sinossi.it/svn/myproject/trunk
r234
r237


Per sfruttare al meglio le funzionalità introdotte con il tracking dei merge è stata aggiunta l'opzione -g (oppure --use-merge-history) ai comandi svn log e svn blame, ciò permette di utilizzare le informazioni relative ai merge nell'output di questi comandi.
Come ultima indicazione, per chi ha utilizzato lo script svnmerge.py per tracciare i merge, è disponibile lo script svnmerge-migrate-history.py che consente di migrare alla modalità di merge di Subversion 1.5 senza perdere lo storico delle informazioni di merge accumulate.


Per maggiorni informazioni vedere anche:

Subversion 1.5 release notes
svn merge
svn mergeinfo
svnmerge.py

martedì 16 novembre 2010

Top Level Class e Nested Class in Java

La Top Level Class è una classe la cui dichiarazione non risiede all'interno di un'altra classe o di un'interfaccia. Tutti i tipi che vengono dichiarati all'interno di una Top Level Class possono essere indicati con il termine nested.

Vi sono svariati modi in cui i tipi nested possono essere dichiarati all'interno di una Top Level Class. Ad esempio, tutti i tipi nested non static sono membri della classe contenitore. I tipi dichiarati static invece, non possono essere essere considerati membri della classe contenitore. La differenza fondamentale sta nel fatto che i membri sono instanziati al momento in cui viene istanziata la classe contenitore e sono fortemente accoppiati con essa. I tipi statici invece presentano un livello di accoppiamento più debole e non dipendono direttamente da un'istanza della classe contenitore, ma solo da un suo riferimento.

Fra i tipi permessi, vi è anche il tipo class. Infatti Java permette di dichiarare una classe all'interno di una Top Level Class. La classe dichiarata internamente è detta Nested Class. La Nested Class segue le regole di tutti gli altri tipi, infatti può essere dichiarata public, private, protected o package private e può essere static.

Una Static Nested Class può essere definita nel seguente modo:


public class OuterWithStaticNested {

private String outerField;

private static String OUTER_STATIC_FIELD = "Outer Static Field";

public OuterWithStaticNested(String outerField) {
this.outerField = outerField;
}

public String getOuterField() {
return outerField;
}

static class StaticInnerClass {

void print() {
System.out.println(OUTER_STATIC_FIELD);
System.out.println(new OuterWithStaticNested("Outer Field")
.getOuterField());
}

}

}

Dal punto di vista comportamentamentale la Static Nested Class è molto simile ad una Top Level Class. Non ha, infatti, accesso diretto a membri e metodi privati della classe che la contiene, e per utilizzarli, deve appoggiarsi ad un'istanza concreta della classe contenitore. Ha invece accesso diretto ai tipi statici, anche se privati. Questo implica un basso livello di accoppiamento fra la Top Level Class e la Static Nested Class, la quale può essere istanziata in maniera diretta, senza che ci sia bisogno di un'istanza della Top Level Class.

StaticNestedClass staticNestedClass = new OuterWithStaticNested.StaticNestedClass();

Le Inner Classes sono invece fortemente accoppiate con l Top Level Class in cui sono dichiarate. Hanno infatti accesso a tutti i membri e metodi privati della classe contenitore e possono essere a loro volta considerate come un membro di essa.


public class OuterWithInner {

private String outerField;

private static String OUTER_STATIC_FIELD = "Outer Static Field";

public OuterWithInner(String outerField) {
this.outerField = outerField;
}

class InnerClass {

void print() {
System.out.println(outerField);
System.out.println(OUTER_STATIC_FIELD);
}

}

}

A testimonianza di questo forte accoppiamento, la Inner Class può essere inizializzata esternamente alla Top Level Class solo a partire da una sua istanza concreta.

OuterWithInner outerClass = new OuterWithInner("Outer Field");
InnerClass innerClass = outerClass.new InnerClass();

Per questi motivi, una Inner Class non può contenere metodi o membri statici.

Fra le Nested Classes troviamo anche le Anonymous Inner Class, cioè classi che vengono dichiarate e istanziate nel punto in cui devono essere usate. Non hanno nome e non possono essere considerate come un membro della classe in cui sono dichiarate. Sono permesse in qualunque punto in cui è ammessa una espressione.

Possono essere dichiarate sia in un contesto statico che non statico, ma non possono avere membri statici.

Uso comune delle Anonymous è la creazione di oggetti funzionali (cioè istanze di classi senza stato, i cui metodi operano solo sugli oggetti passati nella signature) “on the fly”. Un tipico esempio è l'utilizzo del metodo

java.util.Collections.sort(List, Comparator)

dove spesso si implementa un Comparator "al volo" per indicare la logica di ordinamento degli elementi.

public class OuterWithAnonymous {

private List outerFields;

public OuterWithAnonymous(List outerFields) {
this.outerFields = outerFields;
}

public void sortOuterFieldsByFieldLenghtAsc() {
Collections.sort(outerFields, new Comparator() {

@Override
public int compare(String field1, String field2) {
if (field1.length() > field2.length()) {
return 1;
} else if (field1.length() == field2.length()) {
return 0;
} else {
return -1;
}
}

});
}
}

Altro caso d'uso frequente è per la creazione di oggetti process come Runnable, Thread o TimerTask.

Infine, il tipo meno comune di Nested Class è la Local Class. Le Local Classes sono dichiarate all'interno di un metodo, allo stesso modo di una variabile locale, e obbediscono alla stessa politica di scope.

public class OuterWithLocal {

private List outerFields;

public OuterWithLocal(List outerFields) {
this.outerFields = outerFields;
}

public void sortOuterFieldsByFieldLenghtAsc() {
Comparator stringComparator = new Comparator() {

@Override
public int compare(String field1, String field2) {
if (field1.length() > field2.length()) {
return 1;
} else if (field1.length() == field2.length()) {
return 0;
} else {
return -1;
}
}

};
Collections.sort(outerFields, stringComparator);
}
}

Come le Inner Classes, hanno nome e possono essere utilizzate più volte.Come le Anonimous Inner Classes, non possono avere membri statici e hanno un riferimento alla classe contenitore solo se sono dichiarate in un contesto non statico.


Le quattro tipoligie di Nested Classes hanno tutte caratteristiche diverse, che possono tradursi in svantaggi se non sfruttate al meglio. Se la Nested Class non deve avere accesso diretto a metodi e membri della classe contenitore, allora è preferibile che sia dichiarata statica, in modo da evitare che ogni istanza della Nested Class necessiti di un'istanza concreta della Top Level Class.

Le Anonimous Inner Classes e le Local classes devono essere brevi e molto semplici, in modo da non diminuire troppo la leggibilità del codice. Le Anonimous sono da preferire alle Local quando devono essere istanziate una sola volta, nel punto in cui devono essere eseguite. Le Local sono da preferire alle Inner se devono essere usate più volte in un unico metodo.

lunedì 15 novembre 2010

Hibernate: differenza tra get e load

Il metodo get() sollecita immediatamente il database. Cio significa che, non appena avviene la chiamata la get(), Hibernate genera un'istruzione SQL per il database, nel tentativo di recuperare i dati (di solito una riga nel database) per ricostruire l'oggetto persistente richiesto.

Una chiamata load(), invece, non comporta una chiamata immediata al database. Il metodo load() implica la costruzione di un oggetto proxy che rapprensenta l'oggetto persistente. Solo dopo qualche passaggio l'oggetto proxy sollecita la generazione dell'istruzione SQL appropriata per il database e Hibernate costruisce il vero oggetto persistente.

Quando si usa get(), il metodo restituisce null se non viene trovato il dato richiesto.
Poiché il metodo load() non recupera immediatamente l'oggetto, se non viene trovato il dato viene generata una ObjectNotFoundException.
Quindi, se non si è sicuri dell'esistenza dell'oggetto, è preferibile usare la get().

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ì 19 ottobre 2010

Secure ssh: from password to public key authentication

Disabling ssh access with password authentication is an easy way to make more secure your system.
The alternative to password is authentication with public/private key pair. This prevents some security holes like sending the password over the net or brute force attacks.
First of all generate a pair of key in the client:
ssh-keygen -t rsa
you will be prompted for files where store the keys (default is ~/.ssh/id_rsa for private key and ~/.ssh/id_rsa.pub for public key) and for a pass-phrase to protect the private key (you will be prompted for every time you try to log to a remote host with public key authentication).
Next, upload the public key in the remote host, from the client host launch
ssh-copy-id remote-user@remote_host
This command installs the public key in the ~/.ssh/authorized_keys file of the remote-user on the remote_host. Now the client host can log in on remote_host as remote-user without typing the password, the authentication is done automatically under the hood.
Verify the correct execution of the command logging in to the remote host:
ssh remote-user@remote_host
no password should be asked.
The public key of the client can be upload to every user/host where you want to be authenticated without password.
The last step is to disable password authentication on remote_host. This is done editing /etc/ssh/sshd_config (as root user); the following lines must be present
PasswordAuthentication no
RSAAuthentication yes
PubkeyAuthentication yes
Then reload the ssh configuration
/etc/init.d/ssh reload
Verify that password authentication is disabled trying to log in from another client which has not setup public key authentication. The user should be refused, on my machine the message is:
Permission denied (publickey)