2010-07-17 20 views
45

Sto sviluppando un'applicazione enterprise Java, che attualmente sta eseguendo operazioni di sicurezza Java EE per limitare l'accesso per determinate funzioni a utenti specifici. Ho configurato il server di applicazione e tutto, e ora sto usando il RolesAllowed-annotazione per fissare i metodi:Utilizzare il tipo Enum come parametro valore per @RolesAllowed-Annotation

@Documented 
@Retention (RUNTIME) 
@Target({TYPE, METHOD}) 
public @interface RolesAllowed { 
    String[] value(); 
} 

Quando uso l'annotazione in questo modo, funziona benissimo:

@RolesAllowed("STUDENT") 
public void update(User p) { ... } 

Ma questo non è quello che voglio, dato che devo usare una stringa qui, il refactoring diventa difficile, e gli errori di battitura possono accadere. Quindi, invece di usare una stringa, mi piacerebbe usare un valore Enum come parametro per questa annotazione. L'Enum assomiglia a questo:

public enum RoleType { 
    STUDENT("STUDENT"), 
    TEACHER("TEACHER"), 
    DEANERY("DEANERY"); 

    private final String label; 

    private RoleType(String label) { 
     this.label = label; 
    } 

    public String toString() { 
     return this.label; 
    } 
} 

Così ho cercato di usare l'Enum come parametro come questo:

@RolesAllowed(RoleType.DEANERY.name()) 
public void update(User p) { ... } 

Ma poi ho il seguente errore del compilatore, anche se Enum.name solo restituisce una stringa (che è sempre costante, non è vero?).

The value for annotation attribute RolesAllowed.value must be a constant expression`

La prossima cosa che ho provato è stato quello di aggiungere una stringa finale, rispetto alla mia Enum:

public enum RoleType { 
    ... 
    public static final String STUDENT_ROLE = STUDENT.toString(); 
    ... 
} 

Ma anche questo non funziona come parametro, con conseguente lo stesso errore del compilatore:

// The value for annotation attribute RolesAllowed.value must be a constant expression 
@RolesAllowed(RoleType.STUDENT_ROLE) 

Come posso ottenere il comportamento desiderato? Ho persino implementato il mio intercettore per usare le mie annotazioni, che sono belle, ma troppe righe di codice per un piccolo problema come questo.

NEGAZIONE

Questa domanda era in origine una domanda Scala. Ho scoperto che Scala non è la fonte del problema, quindi prima provo a farlo in Java.

+1

Spiacente, questo è un po 'irrilevante per risolvere il tuo problema ma ho pensato di menzionare che puoi rinunciare all'argomento String ai tuoi costruttori enum se hai intenzione di averli come quelli che li chiami ; puoi accedere allo stesso valore chiamando .name() sull'enumerazione. Credo che il metodo toString() deleghi comunque a name(). – I82Much

+0

possibile duplicato di [Come fornire valore Enum a un'annotazione da una costante in Java] (http://stackoverflow.com/questions/13253624/how-to-supply-enum-value-to-an-annotation-from- a-constant-in-java) –

risposta

29

Non credo che il tuo approccio all'uso dell'enumerazione funzionerà. Ho trovato che l'errore del compilatore è andato via se ho cambiato il campo STUDENT_ROLE nel vostro ultimo esempio in una stringa costante, al contrario di un'espressione:

public enum RoleType { 
    ... 
    public static final String STUDENT_ROLE = "STUDENT"; 
    ... 
} 

Tuttavia, questo allora significa che i valori enum, non sarebbero stati utilizzati ovunque, perché invece useresti le costanti di stringa nelle annotazioni.

Mi sembra che stareste meglio se la vostra classe RoleType contenesse nient'altro che una manciata di costanti statiche della stringa finale.


capire perché il codice non stava compilando, ho avuto uno sguardo al Java Language Specification (JLS).Il JLS per annotations afferma che un'annotazione con un parametro di tipo T e valore V,

if T is a primitive type or String , V is a constant expression.

A constant expression include, tra le altre cose,

Qualified names of the form TypeName . Identifier that refer to constant variables

e constant variable è definita as

a variable, of primitive type or type String , that is final and initialized with a compile-time constant expression

+4

Grazie per i tuoi sforzi! Fatti interessanti. Soprattutto l'ultimo. Ho sempre pensato che le finali fossero già costanti. Ok questo è il motivo per cui non può funzionare. Mi sembra che questa sia già la risposta. Anche se non ne sono davvero contento;) (A proposito, ho davvero bisogno dell'Enum, non solo per l'annotazione) – ifischer

+0

Ho effettivamente incontrato [un esempio] (http://java.dzone.com/articles/glassfish- 3-30 minuti) che dichiara l'interfaccia pubblica Roles {String REGISTERED = "registered"; } 'e quindi usa' @RolesAllowed ({Roles.REGISTERED}) '. Ovviamente, un esempio * non * che usa 'enum' non significa che' enum' produce problemi, ma bene ;-) – Arjan

7

Qui ' s una soluzione che utilizza un'interfaccia aggiuntiva e una meta-annotazione. Ho incluso una classe di utilità per aiutare a fare la riflessione per ottenere i tipi di ruolo da un insieme di annotazioni, e un piccolo test per esso:

/** 
* empty interface which must be implemented by enums participating in 
* annotations of "type" @RolesAllowed. 
*/ 
public interface RoleType { 
    public String toString(); 
} 

/** meta annotation to be applied to annotations that have enum values implementing RoleType. 
* the value() method should return an array of objects assignable to RoleType*. 
*/ 
@Retention(RetentionPolicy.RUNTIME) 
@Target({ANNOTATION_TYPE}) 
public @interface RolesAllowed { 
    /* deliberately empty */ 
} 

@RolesAllowed 
@Retention(RetentionPolicy.RUNTIME) 
@Target({TYPE, METHOD}) 
public @interface AcademicRolesAllowed { 
    public AcademicRoleType[] value(); 
} 

public enum AcademicRoleType implements RoleType { 
    STUDENT, TEACHER, DEANERY; 
    @Override 
    public String toString() { 
     return name(); 
    } 
} 


public class RolesAllowedUtil { 

    /** get the array of allowed RoleTypes for a given class **/ 
    public static List<RoleType> getRoleTypesAllowedFromAnnotations(
      Annotation[] annotations) { 
     List<RoleType> roleTypesAllowed = new ArrayList<RoleType>(); 
     for (Annotation annotation : annotations) { 
      if (annotation.annotationType().isAnnotationPresent(
        RolesAllowed.class)) { 
       RoleType[] roleTypes = getRoleTypesFromAnnotation(annotation); 
       if (roleTypes != null) 
        for (RoleType roleType : roleTypes) 
         roleTypesAllowed.add(roleType); 
      } 
     } 
     return roleTypesAllowed; 
    } 

    public static RoleType[] getRoleTypesFromAnnotation(Annotation annotation) { 
     Method[] methods = annotation.annotationType().getMethods(); 
     for (Method method : methods) { 
      String name = method.getName(); 
      Class<?> returnType = method.getReturnType(); 
      Class<?> componentType = returnType.getComponentType(); 
      if (name.equals("value") && returnType.isArray() 
        && RoleType.class.isAssignableFrom(componentType)) { 
       RoleType[] features; 
       try { 
        features = (RoleType[]) (method.invoke(annotation, 
          new Object[] {})); 
       } catch (Exception e) { 
        throw new RuntimeException(
          "Error executing value() method in " 
            + annotation.getClass().getCanonicalName(), 
          e); 
       } 
       return features; 
      } 
     } 
     throw new RuntimeException(
       "No value() method returning a RoleType[] type " 
         + "was found in annotation " 
         + annotation.getClass().getCanonicalName()); 
    } 

} 

public class RoleTypeTest { 

    @AcademicRolesAllowed({DEANERY}) 
    public class DeaneryDemo { 

    } 

    @Test 
    public void testDeanery() { 
     List<RoleType> roleTypes = RolesAllowedUtil.getRoleTypesAllowedFromAnnotations(DeaneryDemo.class.getAnnotations()); 
     assertEquals(1, roleTypes.size()); 
    } 
} 
3

ne dici di questo?

public enum RoleType { 
    STUDENT(Names.STUDENT), 
    TEACHER(Names.TEACHER), 
    DEANERY(Names.DEANERY); 

    public class Names{ 
     public static final String STUDENT = "Student"; 
     public static final String TEACHER = "Teacher"; 
     public static final String DEANERY = "Deanery"; 
    } 

    private final String label; 

    private RoleType(String label) { 
     this.label = label; 
    } 

    public String toString() { 
     return this.label; 
    } 
} 

E in annotazione è possibile utilizzarlo come

@RolesAllowed(RoleType.Names.DEANERY) 
public void update(User p) { ... } 

Un piccolo preoccupazione è, per qualsiasi modifica, abbiamo bisogno di cambiare in due punti. Ma dal momento che sono nello stesso file, è improbabile che manchi. In cambio, otteniamo il vantaggio di non utilizzare stringhe non elaborate ed evitare il meccanismo sofisticato.

Oppure questo suona totalmente stupido? :)

+0

Grazie, mi piace molto e lo userò. Sono giunto a questa domanda in origine perché sto cercando un modo più semplice per specificare la proprietà 'name' nell'annotazione di '@ JsonSubTypes.Type 'di Jackson, in modo che l'enumerazione che definisce quei nomi logici possa essere usata nell'annotazione come oltre ad essere disponibile per altre parti dell'applicazione. – Trevor

+0

Felice di aver apprezzato @Trevor – Samiron

Problemi correlati