2010-12-13 45 views
4

J'ai créé un Converter personnalisé en JSF 1.2 pour convertir Date objets. Les dates ont un format très particulier. J'ai implémenté mon convertisseur en utilisant la classe principale Java SimpleDateFormat pour faire la conversion, en utilisant la chaîne de formatage montrée dans mes commentaires de code ci-dessous. Tout cela fonctionne très bien.Convertisseur JSF personnalisé pour Date - Est-ce que le thread est sûr?

Ma question concerne la sécurité des threads. Les documents API SimpleDateFormat indiquent qu'il n'est pas thread-safe. Pour cette raison, j'ai créé une instance séparée de l'objet format de date pour chaque instance de mon objet convertisseur. Cependant, je ne suis pas sûr que cela suffise. Mon objet DateFormat est stocké en tant que membre du DTGDateConverter.

QUESTION: Deux threads accéderont-ils simultanément à la même instance d'un objet Converter dans JSF?

Si la réponse est oui, alors mon convertisseur est probablement à risque.

/** 
* <p>JSF Converter used to convert from java.util.Date to a string. 
* The SimpleDateFormat format used is: ddHHmm'Z'MMMyy.</p> 
* 
* <p>Example: October 31st 2010 at 23:59 formats to 312359ZOCT10</p> 
* 
* @author JTOUGH 
*/ 
public class DTGDateConverter implements Converter { 

    private static final Logger logger = 
     LoggerFactory.getLogger(DTGDateConverter.class); 

    private static final String EMPTY_STRING = ""; 

    private static final DateFormat DTG_DATE_FORMAT = 
     MyFormatterUtilities.createDTGInstance(); 

    // The 'format' family of core Java classes are NOT thread-safe. 
    // Each instance of this class needs its own DateFormat object or 
    // runs the risk of two request threads accessing it at the same time. 
    private final DateFormat df = (DateFormat)DTG_DATE_FORMAT.clone(); 

    @Override 
    public Object getAsObject(
      FacesContext context, 
      UIComponent component, 
      String stringValue) 
      throws ConverterException { 
     Date date = null; 
     // Prevent ParseException when an empty form field is submitted 
     // for conversion 
     if (stringValue == null || stringValue.equals(EMPTY_STRING)) { 
      date = null; 
     } else { 
      try { 
       date = df.parse(stringValue); 
      } catch (ParseException e) { 
       if (logger.isDebugEnabled()) { 
        logger.debug("Unable to convert string to Date object", e); 
       } 
       date = null; 
      } 
     } 
     return date; 
    } 

    @Override 
    public String getAsString(
      FacesContext context, 
      UIComponent component, 
      Object objectValue) 
      throws ConverterException { 
     if (objectValue == null) { 
      return null; 
     } else if (!(objectValue instanceof Date)) { 
      throw new IllegalArgumentException(
       "objectValue is not a Date object"); 
     } else { 
      // Use 'toUpperCase()' to fix mixed case string returned 
      // from 'MMM' portion of date format 
      return df.format(objectValue).toUpperCase(); 
     } 
    } 

} 
+0

balusC?!où êtes-vous – mkoryak

+2

@mkoryak: J'ai un travail et une vie aussi :) – BalusC

+0

111K rep, c'est tout ce que je vais dire :) – mkoryak

Répondre

7

Will deux fils tous les accès simultanément la même instance d'un objet Converter dans JSF?

Dépend de la façon dont vous utilisez le convertisseur. Si vous utilisez

<h:inputWhatever> 
    <f:converter converterId="converterId" /> 
</h:inputWhatever> 

alors une nouvelle instance sera créée pour chaque élément d'entrée en vue, qui est threadsafe (attendre du boîtier de bord très rare que l'utilisateur final a deux points de vue identiques dans deux onglets du navigateur dans la même session et publie simultanément une publication sur les deux vues).

Si vous utilisez cependant

<h:inputWhatever converter="#{applicationBean.converter}" /> 

alors la même instance sera partagé entre tous les points de vue de l'ensemble de l'application, ce qui est donc pas threadsafe.

Vous êtes cependant en train de cloner une instance DataFormat statique chaque fois que vous créez le convertisseur. Cette partie n'est déjà pas threadsafe. Vous risquez de cloner une instance alors que son état interne a été modifié car il a été utilisé ailleurs. En outre, le clonage d'une instance existante n'est pas nécessairement moins coûteux que la création d'une nouvelle instance.

Je recommanderais de simplement déclarer threadlocal (c'est-à-dire à l'intérieur du bloc de méthode), quelle que soit la façon dont vous utilisez le convertisseur. Si le coût de la création du DateFormat à chaque fois est une préoccupation majeure (avez-vous le profil?), Puis envisager de le remplacer par JodaTime.

+0

Je l'ai déclaré dans mon faces-config.xml en tant que et il est référencé via le convertisseur-ID comme vous le montrez dans votre premier scénario. Je ne l'ai pas profilé donc vous faites un bon point là. Merci! –

2

formats de date ne sont pas synchronisés. Il est recommandé de créer pour créer des instances de format distinctes pour chaque thread. Si plusieurs threads accèdent simultanément à un format , il doit être synchronisé en externe.

Oui, il n'est pas sûr de thread ici.

locale Mettez-le à la méthode et créer une instance par thread

+0

Donc vous dites que les instances de JSF Converter sont accédées simultanément par plusieurs threads? –

+0

@Jim Bien que ce soit le deuxième point, tout d'abord vous clonez l'objet static 'DateFormat' qui va copier l'état de l'objet à n'importe quelle demande, ce n'est pas préférable dans tous les cas. –

+0

Ce que j'essaie de faire est de minimiser le nombre d'objets DateFormat qui sont créés OU de trouver un moyen sûr et efficace de les partager ou de les réutiliser. Je peux seulement imaginer que ceux-ci sont chers à créer à partir d'une chaîne de format à chaque fois. Ils sont utilisés à l'intérieur d'une table de données dans mon application JSF (qui pourrait avoir un grand nombre d'entrées), donc la dépense de création de tous ces objets DateFormat peut ne pas être triviale. –