Jersey 2.6 #QueryParam is missing from #BeanParam when using MOXY but not with Jackson - moxy

I am developing a set of REST web services for my company. We are trying to settle on what technologies we want to use.
I have a template web service that works with JAX-RS Jersey 2.6 when I use the Jackson JSON providers, but doesn't seem to marshal the #QueryParam correctly when I use the Moxy providers.
The search may include multiple "types" such as type=keyword&type=product_number&type=fubar
These are mapped to the List types which contains all of the "type" QueryParam. When I build the project with Jackson, the values of type are correctly collected into the List, when I use MOXy the List is null. MOXy does map all of the other Query and Path Params in the BeanParam.
The problem seems to be in how JERSEY is
When I use Jackson the service works great:
http://XXX:8080/SearchTermJersey/search/1/as/wat?type=product_number&type=keyword&count=4&lang=en_US
This is the JSON it returned:
{"autoSuggestions":{"product_number":{"<span>wat</span>21000":34},"keyword":{"<span>wat</span>er":100,"<span>wat</span>er solution":50,"<span>wat</span>er purity":100}},"language":"en_US","requestDate":1393623225135,"responseDate":1393623225135,"term":"wat","version":"1"}
The URL for the Moxy version of the service returns:
{"language":"en_US","requestDate":1393622174166,"responseDate":1393622174166,"term":"wat","version":"1"}
The Java code is identical between the MOXy and Jackson versions
This is the BeanParam:
public class AutoSuggestParam {
#PathParam("version")
private String version;
#PathParam("term")
private String term;
private List<String>types;
private Integer count;
String language;
public AutoSuggestParam(#QueryParam("count")int count, #QueryParam("type")List<String>types, #QueryParam("lang")String language) {
this.types = types;
this.count = count;
this.language = language;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getTerm() {
return term;
}
public void setTerm(String term) {
this.term = term;
}
public Integer getCount() {
return count;
}
public String getLanguage() {
return language;
}
public List<String>getTypes() {
return types != null ? types : new ArrayList<String>();
}
The problem seems to be in how the types parameter is handled. With Jackson the types QueryParams are correctly marshalled into the List, but MOXy fails and just returns a null. So getTypes is returning an empty List. The simple QueryParam count and lang are handled correctly. Is this a bug in Jersey or do I need to do something else with MOXy?
Here is my Resource class:
#javax.ws.rs.Path("/search/{version}/as/{term}")
public class AutoSuggestResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public AutoSuggestResponse getAutoSuggest(#BeanParam AutoSuggestParam autoSuggestParam) {
System.out.printf("Request: term=%s version=%s lang=%s type=%s count=%d%n",
autoSuggestParam.getTerm(),autoSuggestParam.getVersion(), autoSuggestParam.getLanguage(), autoSuggestParam.getTypes().get(0), autoSuggestParam.getCount());
return search(autoSuggestParam);
}
private AutoSuggestResponse search(AutoSuggestParam autoSuggestParam) {
AutoSuggestResponse autoSuggestResponse = new AutoSuggestResponse();
autoSuggestResponse.setRequestDate(new Date().getTime());
autoSuggestResponse.setVersion(autoSuggestParam.getVersion());
autoSuggestResponse.setTerm(autoSuggestParam.getTerm());
autoSuggestResponse.setLanguage(autoSuggestParam.getLanguage());
int cnt = 0;
for (String type : autoSuggestParam.getTypes()) {
if ("product_number".equals(type)) {
Map<String, Object> values = autoSuggestResponse.getAutoSuggestions().get(type);
if (values == null) {
values = new LinkedHashMap<String, Object>();
autoSuggestResponse.getAutoSuggestions().put(type, values);
}
String key = String.format("<span>%s</span>21000", autoSuggestParam.getTerm());
values.put(key, 34);
cnt++;
}
else if ("keyword".equals(type)) {
Map<String, Object> values = autoSuggestResponse.getAutoSuggestions().get(type);
if (values == null) {
values = new LinkedHashMap<String, Object>();
autoSuggestResponse.getAutoSuggestions().put(type, values);
}
String key = String.format("<span>%s</span>er", autoSuggestParam.getTerm());
values.put(key, 100);
cnt++;
key = String.format("<span>%s</span>er solution", autoSuggestParam.getTerm());
values.put(key, 50);
cnt++;
key = String.format("<span>%s</span>er purity", autoSuggestParam.getTerm());
values.put(key, 100);
cnt++;
}
if (cnt >= autoSuggestParam.getCount()) {
break;
}
}
autoSuggestResponse.setResponseDate(new Date().getTime());
return autoSuggestResponse;
}
The Response class:
public class AutoSuggestResponse {
private Long requestDate;
private Long responseDate;
private String version;
private String term;
private String language;
private Map<String, Map<String,Object>>autoSuggestions = new LinkedHashMap<String, Map<String,Object>>();
public Long getRequestDate() {
return requestDate;
}
public void setRequestDate(Long requestDate ) {
this.requestDate = requestDate;
}
public Long getResponseDate() {
return responseDate;
}
public void setResponseDate(Long responseDate) {
this.responseDate = responseDate;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getTerm() {
return term;
}
public void setLanguage(String language) {
this.language = language;
}
public String getLanguage() {
return language;
}
public void setTerm(String term) {
this.term = term;
}
public Map<String, Map<String,Object>>getAutoSuggestions() {
return autoSuggestions;
}
}
The web.xml
<display-name>MoxyAS</display-name>
<servlet>
<servlet-name>MoxyAutoSuggest</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.sial.search.ws</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<servlet-mapping>
<servlet-name>MoxyAutoSuggest</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>

By default EclipseLink JAXB (MOXy) will not marshal properties that only have a getter. You can add an #XmlElement annotation to have it become mapped:
#XmlElement
public Map<String, Map<String,Object>>getAutoSuggestions() {
return autoSuggestions;
}
By default MOXy does not use the map key as the JSON key. Below is a link to an example that explains how to set this up:
http://blog.bdoughan.com/2013/06/moxys-xmlvariablenode-using-maps-key-as.html

I figured out the problem. It had nothing to do with MOXy, adding the genson-0.98.jar to the path fixed the problem with the QueryParam not getting marshaled in the BeanParam.
Adding the #XmlElement to the resource did make Moxy work, sort of. If I add XmlElement to the Map in the Response Class I now get an error:
javax.servlet.ServletException: org.glassfish.jersey.server.ContainerException: java.lang.NoClassDefFoundError: org/eclipse/persistence/internal/libraries/asm/ClassWriter
But this is a new problem.

Related

Swagger oneOf type: Jackson trying to instantiate interface instead of implementation?

I'm using the oneOf feature to define several possible schemas that can go into a request body property of my service. In the generated Java client code, the Java implementations of these schemas implement an interface, but when I send a request through, Jackson is trying to create an instance of the interface, instead of the concrete class.
Swagger-codegen version
<groupId>io.swagger.codegen.v3</groupId>
<artifactId>swagger-codegen-maven-plugin</artifactId>
<version>3.0.14</version>
Swagger declaration file content
schemas:
TestRequest:
description:
Test request
type:
object
required:
- criteria
properties:
criteria:
oneOf:
- $ref: '#/components/schemas/CriteriaA'
- $ref: '#/components/schemas/CriteriaB'
...
CriteriaA:
description: Criteria A
type: object
required:
- type
- query
properties:
type:
description: A description
type: string
enum:
- CriteriaA
query:
description: A query.
type: object
Steps to reproduce
The Java client code generated by swagger codegen looks like this:
Interface:
public interface OneOfTestRequestCriteria {}
Concrete class:
#Schema(description = "")
#javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.JavaClientCodegen", date = "2020-01-28T13:06:29.942Z[Europe/London]")
public class CriteriaA implements OneOfTestRequestCriteria {
#JsonAdapter(TypeEnum.Adapter.class)
public enum TypeEnum {
CriteriaA("CriteriaA");
private String value;
TypeEnum(String value) {
this.value = value;
}
public String getValue() {
return value;
}
#Override
public String toString() {
return String.valueOf(value);
}
public static TypeEnum fromValue(String text) {
for (TypeEnum b : TypeEnum.values()) {
if (String.valueOf(b.value).equals(text)) {
return b;
}
}
return null;
}
public static class Adapter extends TypeAdapter<TypeEnum> {
#Override
public void write(final JsonWriter jsonWriter, final TypeEnum enumeration) throws IOException {
jsonWriter.value(enumeration.getValue());
}
#Override
public TypeEnum read(final JsonReader jsonReader) throws IOException {
String value = jsonReader.nextString();
return TypeEnum.fromValue(String.valueOf(value));
}
}
} #SerializedName("type")
private TypeEnum type = null;
#SerializedName("query")
private Object query = null;
public CriteriaA type(TypeEnum type) {
this.type = type;
return this;
}
#Schema(required = true, description = "")
public TypeEnum getType() {
return type;
}
public void setType(TypeEnum type) {
this.type = type;
}
public CriteriaA query(Object query) {
this.query = query;
return this;
}
#Schema(required = true, description = "")
public Object getQuery() {
return query;
}
public void setQuery(Object query) {
this.query = query;
}
#Override
public boolean equals(java.lang.Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
CriteriaA criteriaA = (CriteriaA ) o;
return Objects.equals(this.type, criteriaA.type) &&
Objects.equals(this.query, criteriaA.query);
}
#Override
public int hashCode() {
return Objects.hash(type, query);
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("class CriteriaA {\n");
sb.append(" type: ").append(toIndentedString(type)).append("\n");
sb.append(" query: ").append(toIndentedString(query)).append("\n");
sb.append("}");
return sb.toString();
}
private String toIndentedString(java.lang.Object o) {
if (o == null) {
return "null";
}
return o.toString().replace("\n", "\n ");
}
}
I'm trying to use this generated client code to send a request:
final TestRequest testRequest = new TestRequest();
final CriteriaA criteriaA = new CriteriaA ();
criteriaA .setType(CriteriaA .TypeEnum.CriteriaA);
criteriaA .setQuery("a query");
testRequest .setCriteria(criteriaA );
final ApiResponse<Void> apiResponse = testApi.createOrUpdateTestWithHttpInfo(testRequest);
Running the above client code results in this error when Jackson tries to deserialize it. It seems to be trying to construct an instance of the interface OneOfTestRequestCriteria, instead of the concrete implementation of the interface; CriteriaA:
[Request processing failed; nested exception is
org.springframework.http.converter.HttpMessageConversionException:
Type definition error: [simple type, class
com.acme.tag.models.OneOfTestRequestCriteria]; nested exception is
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot
construct instance of com.acme.tag.models.OneOfTestRequestCriteria (no
Creators, like default construct, exist): abstract types either need
to be mapped to concrete types, have custom deserializer, or contain
additional type information\n
If I annotate the generated interface:
public interface OneOfTestRequestCriteria {}
with the following:
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes({
#Type(value = CriteriaA.class, name = "CriteriaA")
})
public interface OneOfTestRequestCriteria {
}
Then the request gets deserialized correctly into CriteriaA - am I missing something in my swagger.yaml that would result in this interface not getting annotated by the codegen tool?
<groupId>io.swagger.codegen.v3</groupId>
<artifactId>swagger-codegen-maven-plugin</artifactId>
<version>3.0.18</version>
See also: https://github.com/swagger-api/swagger-codegen-generators/pull/585

Deserializing Enum from DescriptionAttribute using custom JSON.NET JsonConverter stopped working

Looking for help with Newtonsoft Json on asp.net core 2.2.
I have a JsonEnumConverter<T> which was responsible for serializing/deserializing values from DescriptionAttribute from an Enum type. It was working fine until about 2 weeks ago and now it has completely stopped working.
here's what I have:
//From PerformersController:
public async Task<ActionResult<PagedPerformers>> GetPagedPerformersAsync([FromQuery] PerformerRequest performerRequest) { ... }
[JsonObject]
public class PerformerRequest : PageRequest
{
[FromQuery(Name = "performer_id")]
[JsonProperty(PropertyName = "performer_id", Order = 1)]
public override string Id { get; set; }
....
}
[JsonConverter(typeof(JsonEnumConverter<SortDirectionType>))]
public enum SortDirectionType
{
[Description("asc")]
ASCENDING,
[Description("desc")]
DESCENDING
}
public abstract class PageRequest
{
[FromQuery(Name = "page")]
[JsonProperty("page")]
public int Page { get; set; }
[FromQuery(Name = "limit")]
[JsonProperty("limit")]
public int PageSize { get; set; } = 100;
[FromQuery(Name = "sort_field")]
[JsonProperty("sort_field")]
public string SortField { get; set; } //= "Id";
[FromQuery(Name = "sort_dir")] [JsonConverter(typeof(JsonEnumConverter<SortDirectionType>))]
[JsonProperty("sort_dir")]
public SortDirectionType SortDirection { get; set; }
[FromQuery(Name = "id")]
[JsonProperty("id")]
public virtual string Id { get; set; }
}
public class JsonEnumConverter<T> : JsonConverter where T : struct, IComparable, IConvertible, IFormattable
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(T);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var type = typeof(T);
if (!type.IsEnum) throw new InvalidOperationException();
var enumDescription = (string)reader.Value;
return enumDescription.GetEnumValueFromDescription<T>();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var type = typeof(T);
if (!type.IsEnum) throw new InvalidOperationException();
if (value != null)
{
if (value is Enum sourceEnum)
{
writer.WriteValue(sourceEnum.GetDescriptionFromEnumValue());
}
}
}
}
public static class EnumExtensions
{
public static string GetDescriptionFromEnumValue(this Enum #enum)
{
FieldInfo fi = #enum.GetType().GetField(#enum.ToString());
DescriptionAttribute[] attributes =
(DescriptionAttribute[])fi.GetCustomAttributes(
typeof(DescriptionAttribute),
false);
if (attributes != null &&
attributes.Length > 0)
return attributes[0].Description;
else
return #enum.ToString();
}
public static T GetEnumValueFromDescription<T>(this string description)
{
var type = typeof(T);
if (!type.IsEnum)
throw new InvalidOperationException();
foreach (var field in type.GetFields())
{
if (Attribute.GetCustomAttribute(field,
typeof(DescriptionAttribute)) is DescriptionAttribute attribute)
{
if (attribute.Description == description)
return (T)field.GetValue(null);
}
else
{
if (field.Name == description)
return (T)field.GetValue(null);
}
}
throw new ArgumentException($"No matching value for enum {nameof(T)} found from {description}.",$"{nameof(description)}"); // or return default(T);
}
}
this was working absolutely fine until recently. Now I'm not sure whats going on I get ValidationProblemDetails response right away. If I suppress asp.net core 2.2 model state invalid filter then modelState.IsValid will still have false. If I put a breakpoint in ReadJson of my JsonEnumConverter it wont even hit. Even tried to set JsonSerializerSettings in startup with no success or luck. Have already tried replacing Description with EnumMember and StringEnumConverter. Still the same issue. Seems like there is some issue with ModelBinder and Json.NET not playing well with each other.
NOTE: This issue is happening on ASP.NET Core 2.2. Suggesting solutions for 3.0 is not helpful!!
If you are using aspnet core 3 / netstandard 2.1, you can try this library https://github.com/StefH/System.Text.Json.EnumExtensions which defines some extensions to the JsonStringEnumConverter to support attributes like EnumMember, Display and Description.

Mapping DTO with final members in MapStruct

is there a way to map a DTO using MatStruct which have a few final data members as well and cannot have a default constructor , like :
public class TestDto {
private final String testName;
private int id;
private String testCase;
public TestDto(String testName) {
this.testName = testName;
}
public String getTestName() {
return testName;
}
public int getId() {
return id;
}
public String getTestCase() {
return testCase;
}
public void setId(int id) {
this.id = id;
}
public void setTestCase(String testCase) {
this.testCase = testCase;
}
}
please suggest how could this DTO be mapped using MapStruct.
You can use #ObjectFactory that would construct an instance of your DTO.
For example:
#Mapper
public interface MyMapper {
#ObjectFactory
default TestDto create() {
return new TestDto("My Test Name");
}
//the rest of the mappings
}
You can also enhance the #ObjectFactory to accept the source parameter, that you can use to construct the TestDto. You can even use a #Context as an Object Factory.
NB: You don't have to put the #ObjectFactory method in the same Mapper, or even a MapStruct #Mapper. You can put it in any class (or make it static) and then #Mapper(uses = MyFactory.class)

No converter found capable of converting from type [java.lang.String] to type [org.springframework.data.solr.core.geo.Point]

I am trying to use spring-data-solr in order to access to my Solr instance through my Spring boot application. I have the following bean class:
#SolrDocument(solrCoreName = "associations")
public class Association implements PlusimpleEntityI {
#Id
#Indexed
private String id;
#Indexed
private String name;
#Indexed
private Point location;
#Indexed
private String description;
#Indexed
private Set<String> tags;
#Indexed
private Set<String> topics;
#Indexed
private Set<String> professionals;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Point getLocation() {
return location;
}
public void setLocation(Point location) {
this.location = location;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Set<String> getTags() {
return tags;
}
public void setTags(Set<String> tags) {
this.tags = tags;
}
public Set<String> getTopics() {
return topics;
}
public void setTopics(Set<String> topics) {
this.topics = topics;
}
public Set<String> getProfessionals() {
return professionals;
}
public void setProfessionals(Set<String> professionals) {
this.professionals = professionals;
}
}
I have implemented the following repository in order to access to the related information:
public interface AssociationsRepository extends SolrCrudRepository<Association, String> {
}
I have created a configuration class which looks like the following one:
#Configuration
#EnableSolrRepositories(basePackages = {"com.package.repositories"}, multicoreSupport = true)
public class SolrRepositoryConfig {
#Value("${solr.url}")
private String solrHost;
#Bean
public SolrConverter solrConverter() {
MappingSolrConverter solrConverter = new MappingSolrConverter(new SimpleSolrMappingContext());
solrConverter.setCustomConversions(new CustomConversions(null));
return solrConverter;
}
#Bean
public SolrClientFactory solrClientFactory () throws Exception {
return new MulticoreSolrClientFactory(solrClient());
}
#Bean
public SolrClient solrClient() throws Exception {
return new HttpSolrClient.Builder(solrHost).build();
}
#Bean
public SolrOperations associationsTemplate() throws Exception {
SolrTemplate solrTemplate = new SolrTemplate(solrClient());
solrTemplate.setSolrConverter(solrConverter());
return solrTemplate;
}
}
Unfortunately, when I try to read an association from my Solr instance I got the following error:
org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [org.springframework.data.solr.core.geo.Point]
I don't understand why it is not able to find a converter if I have explicitly defined it in the solrTemplate() method.
This is my POM definition:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-solr</artifactId>
<version>2.1.4.RELEASE</version>
</dependency>
Thank you for your help.
EDIT:
I've also tried with different BUILD-RELEASEs but they are highly unstable and I've found a lot of errors using them.
Alessandro, as you can see directly in the GeoConverters class on GitHub, the implemented converters are only for:
org.springframework.data.geo.Point
and not for:
org.springframework.data.solr.core.geo.Point
Simply use this class and you don't even need a custom converter for this. Spring Data for Solr will perform the conversion for you.
I'm using a slightly patched version of the 3.0.0 M4, but I'm pretty sure this solution should apply seamlessly also to your case.

Weblogic Providers

I have created a custom authentication provider that checks if a user exists in a datasource and allows it to login or not.
Now I also have to check the roles of that user, but I don't understand if the same provider can take care of Authentication and Role mapping or if I have to do another provider.
I had tried to created another provider, for the role mapping, but I can't find it, or not looking in the right place to configurate it, but my MBean type also doesn't any configs to be inserted.
Can anyone help me with this?
I tried to find examples of role mapping, with no luck.
Thanks
Have a look at the Oracle Guide: How to Develop a Custom Role Mapping Provider
The process is very similiar to creating an authentication Provider, the only difference are the interfaces you have to implement.
Now for my Implementation (I assume knowledge about MBean Provider Creation using the WebLogicMBeanMaker, since you already created an Authentication Provider):
You need 3 Files, a XML File with the configuration, the Provider and the Implementation of a Role.
The Config File:
<?xml version="1.0" ?>
<!DOCTYPE MBeanType SYSTEM "commo.dtd">
<MBeanType
Name = "MYRoleMapper"
DisplayName = "MYRoleMapper"
Package = "MY.security"
Extends = "weblogic.management.security. authorization.RoleMapper"
PersistPolicy = "OnUpdate"
>
<MBeanAttribute
Name = "ProviderClassName"
Type = "java.lang.String"
Writeable = "false"
Preprocessor = "weblogic.management.configuration.LegalHelper.checkClassName(value)"
Default = ""MY.security.MYRoleMapperProviderImpl""
/>
<MBeanAttribute
Name = "Description"
Type = "java.lang.String"
Writeable = "false"
Default = ""MY RM provider ""
/>
<MBeanAttribute
Name = "Version"
Type = "java.lang.String"
Writeable = "false"
Default = ""1.2""
/>
</MBeanType>
The Actual Provider MYRoleMapperProviderImpl.java:
public class MYRoleMapperProviderImpl implements RoleProvider, RoleMapper {
private String description;
private static final Map<String, SecurityRole> NO_ROLES = Collections.unmodifiableMap(new HashMap<String, SecurityRole>(1));
private final static String RESSOURCE_URL = "<url>";
private final static String RESSOURCE_EJB = "<ejb>";
private enum rollen {
READER;
}
#Override
public void initialize(ProviderMBean mbean, SecurityServices services) {
description = mbean.getDescription() + "\n" + mbean.getVersion();
}
#Override
public String getDescription() {
return description;
}
#Override
public void shutdown() {
}
#Override
public RoleMapper getRoleMapper() {
return this;
}
#Override
public Map<String, SecurityRole> getRoles(Subject subject, Resource resource, ContextHandler handler) {
Map<String, SecurityRole> roles = new HashMap<String, SecurityRole>();
Set<Principal> principals = subject.getPrincipals();
for (Resource res = resource; res != null; res = res.getParentResource()) {
getRoles(res, principals, roles);
}
if (roles.isEmpty()) {
return NO_ROLES;
}
return roles;
}
private void getRoles(Resource resource, Set<Principal> principals, Map<String, SecurityRole> roles) {
if (resource.getType() == RESSOURCE_URL || resource.getType() == RESSOURCE_EJB) {
roles.put(rollen.READER.toString(), new MYSecurityRoleImpl(rollen.READER.toString(), "READER Rolle"));
}
}
}
And an absolute simple Role Implementation:
package MY.security;
import weblogic.security.service.SecurityRole;
public class MYSecurityRoleImpl implements SecurityRole {
private String _roleName;
private String _description;
private int _hashCode;
public MYSecurityRoleImpl(String roleName, String description)
{
_roleName = roleName;
_description = description;
_hashCode = roleName.hashCode() + 17;
}
public boolean equals(Object secRole)
{
if (secRole == null)
{
return false;
}
if (this == secRole)
{
return true;
}
if (!(secRole instanceof MYSecurityRoleImpl))
{
return false;
}
MYSecurityRoleImpl anotherSecRole = (MYSecurityRoleImpl)secRole;
if (!_roleName.equals(anotherSecRole.getName()))
{
return false;
}
return true;
}
public String toString () { return _roleName; }
public int hashCode () { return _hashCode; }
public String getName () { return _roleName; }
public String getDescription () { return _description; }
}