Cannot serialize an Enum to GWT - gwt

I am unable to serialize an Enum to GWT if it implements java.io.Serializable. It will GWT compile successfully, but at runtime, I get the dreaded:
Type 'com....security..AdminPrivilege' was not assignable to 'com.google.gwt.user.client.rpc.IsSerializable' and did not have a custom field serializer.For security purposes, this type will not be serialized.: instance = Login to Console
If I implement com.google.gwt.user.client.rpc.IsSerializable it compiles and runs fine. I am trying to avoid IsSerializable, since this Enum is persisted in our DB and is referenced in non-GWT servlets. I do not want to introduce a GWT dependancy, even for that single class.
I've read most of the discussions on this topic here. I have:
added a serialVersionUid (which should not be necessary)
added a no-arg constructor (but this is an Enum, so it must be private - I suspect this may be the problem)
added a callable RPC method that returns the Enum and takes a Set of the Enum as an input argument (trying to get this Enum into the whitelist) -
For all other Enums, I generated a GWT version which implements IsSerializable. But this new Enum is too complex to generate and I need the methods from the Enum in the GWT code.
Thanks for any help on this.
My Enum is below. Notice it has an embedded Enum:
public enum AdminPrivilege implements java.io.Serializable {
// Privileges
MANAGE_XX("Manage XX", PrivilegeCategory.XX),
IMPORT_LICENSE("Import a License", PrivilegeCategory.XX),
SUBMIT_BUG("Submit a Bug", PrivilegeCategory.XX),
TEST_AD("Test AD", PrivilegeCategory.XX),
// Administrator Privileges
LOGIN("Login to XX", PrivilegeCategory.ADMIN),
MANAGE_ADMIN("Manage Administrators", PrivilegeCategory.ADMIN),
MANAGE_ROLE("Manage Roles", PrivilegeCategory.ADMIN),
// Task Privileges
CANCEL_TASK("Cancel Tasks", PrivilegeCategory.TASK), ;
private static final long serialVersionUID = 1L;
/**
* Defines the privilege categories.
*
*/
public enum PrivilegeCategory implements java.io.Serializable {
XX("XX"),
ADMIN("Administrator"),
TASK("Task"), ;
private static final long serialVersionUID = 2L;
private String displayValue;
// This constructor is required for GWT serialization
private PrivilegeCategory() {
}
private PrivilegeCategory(String displayValue) {
this.displayValue = displayValue;
}
#Override
public String toString() {
return displayValue;
}
}
private String displayValue;
private AdminPrivilege parentPrivilege;
private PrivilegeCategory privilegeCategory;
// This constructor is required for GWT serialization
private AdminPrivilege() {
}
private AdminPrivilege(String displayValue, PrivilegeCategory category) {
this.displayValue = displayValue;
this.privilegeCategory = category;
}
private AdminPrivilege(String displayValue, PrivilegeCategory category, AdminPrivilege parent) {
this(displayValue, category);
this.parentPrivilege = parent;
}
/**
* Return the parent privilege for this privilege.
*
* #return
*/
public AdminPrivilege getParentPrivilege() {
return parentPrivilege;
}
/**
* Return the category for this privilege.
*
* #return
*/
public PrivilegeCategory getPrivilegeCategory() {
return privilegeCategory;
}
/**
* Return the set of categories.
*
* #return
*/
public static Set<PrivilegeCategory> getPrivilegeCategories() {
Set<PrivilegeCategory> category = new HashSet<PrivilegeCategory>();
for (PrivilegeCategory c : PrivilegeCategory.values()) {
category.add(c);
}
return category;
}
/**
* Return the set of privileges for a category.
*
* #return
*/
public static Set<AdminPrivilege> getPrivileges(PrivilegeCategory category) {
Set<AdminPrivilege> privileges = new HashSet<AdminPrivilege>();
for (AdminPrivilege p : AdminPrivilege.values()) {
if (category.equals(p.getPrivilegeCategory())) {
privileges.add(p);
}
}
return privileges;
}
/**
* Return the set of child privileges for a specific privilege
*
* #param parent
* #return
*/
public static Set<AdminPrivilege> getChildPrivileges(AdminPrivilege parent) {
Set<AdminPrivilege> children = new HashSet<AdminPrivilege>();
for (AdminPrivilege priv : values()) {
if (parent.equals(priv.getParentPrivilege())) {
children.add(priv);
}
}
return children;
}
/**
* Return the set of privileges that are parent privileges
*
* #return
*/
public static Set<AdminPrivilege> getParentPrivileges() {
Set<AdminPrivilege> parents = new HashSet<AdminPrivilege>();
for (AdminPrivilege priv : values()) {
if (priv.getParentPrivilege() == null) {
parents.add(priv);
}
}
return parents;
}
}
}

Have you specified a parameterized constructor in your enum? If you have, and it has parameters, you need to remember to add a no-parameters constructor as well, even if you don't use it, because GWT will need it. Adding a parameterized constructor and forgetting to add a parameterless one gets me every time, at least with non-enum classes.

Related

Unable to autorun sql scripts via the #Sql annotation

I absolutely need to use non-static methods with annotations #BeforeAll and #AfterAll. But these annotations work on non-static methods only if we have the
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#ActiveProfiles("test")
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public abstract class ApplicationTests extends ContainerConfig{
#Autowired
protected TestRestTemplate testRestTemplate;
#Autowired
protected BasicUserRepository basicUserRepository;
}
/**
* If you prefer JUnit Jupiter
* performed all testing methods in a single test instance,
* annotate your test class with #testInstance(Lifecycle.PER_CLASS) .
* When using this mode, a new test instance is created
* will be created once for each test class, that is, all nodes of the test class
* of a tree (that is, of this class) they will use the same object, that is,
* an object of this class, and methods, will be called on this object and use the state,
* which this object has.
* Therefore, be careful if your test nodes will change the properties of this
* an object during the processing of its tests.
* In this case, you can use the #BeforeAll and #AfterAll annotations over non-static
* methods
*/
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#Sql(scripts = "/sql/sql-data.sql")
class UserControllerTest extends ApplicationTests {
private String NAME_USER;
#BeforeAll
void setUp() {
List<BasicUser> userList = super.basicUserRepository.findAll();
NAME_USER = userList
.stream()
.map(BasicUser::getUsername)
.findFirst()
.orElse("");
if (NAME_USER.isEmpty()) {
throw new RuntimeException("Not Found users.");
}
}
#Test
void findOne() {
String uri = "/users/{username}";
DetailedUserDto userDto =
super.testRestTemplate
.getForObject(uri,
DetailedUserDto.class,
NAME_USER);
Long id = userDto.getId();
String name = userDto.getUsername();
Set<String> permissions = userDto.getPermissions();
int sizeEmptyCollection = 0;
assertTrue(permissions.size() > sizeEmptyCollection);
int emptyId = 0;
assertTrue(id > emptyId);
}
#AfterAll
void clearDb() {
basicUserRepository.deleteAll();
}
}
But when using #TestInstance(testInstance.Lifecycle.PER_CLASS), autorun of sql scripts does not work, via annotation #Sql.
Why does it occure and how can i correct that ?
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
class UserControllerTest extends ApplicationTests {
private boolean isInitDatabase = false;
private String NAME_USER;
#BeforeEach
void setUp() {
if(isInitDatabase) return;
isInitDatabase = true;
.............
}
#ActiveProfiles("test")
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#Sql(scripts={"/sql/sql-data.sql"}, executionPhase = BEFORE_TEST_METHOD)
public abstract class ApplicationTests extends ContainerConfig{
#Autowired
protected TestRestTemplate testRestTemplate;
#Autowired
protected BasicUserRepository basicUserRepository;
}

How to Use NoSql Postgres with Spring Boot

in a post http://blog.endpoint.com/2013/06/postgresql-as-nosql-with-data-validation.html I learnt some basic things about postgres' nosql feature. I'm still wondering about how to use this feature in Spring boot. Is there any documents on this? Thank you so much!
We wrote 4 Java classes to support noSQL with postgreSQL in our Spring project.
JsonString.java is our central class to support noSQL.
JsonStringDbMapper.java maps our JsonString in Hibernate to JDBC type
PGobject(type="jsonb"). This type is defined in postgreSQL JDBC driver.
JsonbH2Dialect.java maps PGobject to the embedded H2 database which we are using in our JUnit tests only. We define this hibernate dialect in our application-test.yml.
JsonStringDeserializer.java to use Jackson in our REST services. The JsonString raw value is integrated in the Beans which uses JsonString.
In our entity classes we are using the type JsonString like:
#Type(type = "de.project.config.JsonStringDbMapper")
private JsonString jsonData = new JsonString(null);
JsonString has methods to read and write any Java beans using Jackson.
JsonString maps to the database and JsonString maps to the REST service facade.
Here are the files:
package de.project.config;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonRawValue;
import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import java.io.Serializable;
import java.util.Objects;
#JsonDeserialize(using = JsonStringDeserializer.class)
public class JsonString implements Serializable {
#JsonRawValue
#JsonValue
private String json;
public JsonString() { }
public JsonString(String json){
this.json= json;
}
public JsonString(Object value){
this.json= MapperSingleton.INSTANCE.writeValue(value);
}
protected enum MapperSingleton {
INSTANCE;
private final ObjectMapper objectMapper;
private MapperSingleton(){
this.objectMapper= new ObjectMapper();
this.objectMapper.registerModule(new JavaTimeModule());
this.objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
this.objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
}
public String writeValue(Object value){
try {
return this.objectMapper.writeValueAsString(value);
} catch (JsonProcessingException ex) {
throw new WriteException("Write Java object value to json string failed!", ex);
}
}
public <T> T readValue(String jsonString, Class<T> valueType){
try {
return this.objectMapper.readValue(jsonString, valueType);
} catch (JsonProcessingException ex) {
throw new ReadException("Java object value from json string not found!", ex);
}
}
}
public String get(){
return this.json;
}
public <T> T read(Class<T> valueType){
return MapperSingleton.INSTANCE.readValue(this.json, valueType);
}
public void write(Object valueType){
this.json= MapperSingleton.INSTANCE.writeValue(valueType);
}
#Override
public boolean equals(Object obj){
if(obj instanceof JsonString){
var json2= (JsonString)obj;
return Objects.equals(this.json, json2.json);
}
return false;
}
#Override
public int hashCode() {
if(this.json == null){
return 42;
}
return this.json.hashCode();
}
public static class WriteException extends RuntimeException {
public WriteException(String message, Throwable throwable){
super(message, throwable);
}
}
public static class ReadException extends RuntimeException {
public ReadException(String message, Throwable throwable){
super(message, throwable);
}
}
}
package de.project.config;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import java.io.IOException;
public class JsonStringDeserializer extends StdDeserializer<JsonString> {
public JsonStringDeserializer() {
this(null);
}
public JsonStringDeserializer(Class<?> vc) {
super(vc);
}
#Override
public JsonString deserialize(JsonParser jsonparser, DeserializationContext context)
throws IOException {
String jsonData= jsonparser.getCodec().readTree(jsonparser).toString();
return new JsonString(jsonData);
}
}
package de.project.config;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Objects;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.usertype.UserType;
import org.postgresql.util.PGobject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class JsonStringDbMapper implements UserType
{
private static final Logger LOG = LoggerFactory.getLogger(JsonStringDbMapper.class);
/**
* Return the SQL type codes for the columns mapped by this type. The
* codes are defined on <tt>java.sql.Types</tt>.
* #see java.sql.Types
* #return int[] the typecodes
*/
#Override
public int[] sqlTypes() {
return new int[]{Types.OTHER}; // We use only one column for our type JsonString
}
/**
* The class returned by <tt>nullSafeGet()</tt>.
*
* #return Class
*/
#Override
public Class returnedClass() {
return JsonString.class;
}
/**
* Compare two instances of the class mapped by this type for persistence "equality".
* Equality of the persistent state.
*
* #param x
* #param y
* #return boolean
*/
#Override
public boolean equals(Object o, Object o1) throws HibernateException {
return Objects.equals(o, o1);
}
/**
* Get a hashcode for the instance, consistent with persistence "equality"
*/
#Override
public int hashCode(Object o) throws HibernateException {
return Objects.hashCode(o);
}
/**
* Retrieve an instance of the mapped class from a JDBC resultset. Implementors
* should handle possibility of null values.
*
*
* #param rs a JDBC result set
* #param names the column names
* #param session
*#param owner the containing entity #return Object
* #throws HibernateException
* #throws SQLException
*/
#Override
public Object nullSafeGet(ResultSet rs, String[] names,
SharedSessionContractImplementor session, Object owner)
throws HibernateException, SQLException {
final var dbValue0 = rs.getObject(names[0]);
if(dbValue0 == null || rs.wasNull()){
return null;
}
if(dbValue0 instanceof PGobject){
var pgObj= (PGobject)dbValue0;
if("jsonb".equals(pgObj.getType())){
return new JsonString(pgObj.getValue());
}
throw new IllegalArgumentException("Unable to convert pgObj.type " + pgObj.getType()
+ " into JsonString");
}else{
throw new ClassCastException("Failed to convert " + dbValue0.getClass().getName()
+ " PGobject");
}
}
/**
* Write an instance of the mapped class to a prepared statement. Implementors
* should handle possibility of null values. A multi-column type should be written
* to parameters starting from <tt>index</tt>.
*
*
* #param st a JDBC prepared statement
* #param value the object to write
* #param index statement parameter index
* #param session
* #throws HibernateException
* #throws SQLException
*/
#Override
public void nullSafeSet(PreparedStatement st, Object value, int index,
SharedSessionContractImplementor session)
throws HibernateException, SQLException {
if(Objects.isNull(value)){
st.setNull(index, Types.OTHER);
}else{
var pgObj = new PGobject();
pgObj.setType("jsonb");
try {
var jsonString= (JsonString)value;
pgObj.setValue(jsonString.get());
st.setObject(index, pgObj);
} catch (Exception ex) {
LOG.error("value='{}'", value);
throw new IllegalArgumentException("Unable to convert JsonString into PGobject", ex);
}
}
}
/**
* Return a deep copy of the persistent state, stopping at entities and at
* collections. It is not necessary to copy immutable objects, or null
* values, in which case it is safe to simply return the argument.
*
* #param value the object to be cloned, which may be null
* #return Object a copy
*/
#Override
public Object deepCopy(Object o) throws HibernateException {
return o;
}
/**
* Are objects of this type mutable?
*
* #return boolean
*/
#Override
public boolean isMutable() {
return false;
}
/**
* Transform the object into its cacheable representation. At the very least this
* method should perform a deep copy if the type is mutable. That may not be enough
* for some implementations, however; for example, associations must be cached as
* identifier values. (optional operation)
*
* #param value the object to be cached
* #return a cacheable representation of the object
* #throws HibernateException
*/
#Override
public Serializable disassemble(Object value) throws HibernateException {
return new JsonString(((JsonString)value).get());
}
/**
* Reconstruct an object from the cacheable representation. At the very least this
* method should perform a deep copy if the type is mutable. (optional operation)
*
* #param cached the object to be cached
* #param owner the owner of the cached object
* #return a reconstructed object from the cacheable representation
* #throws HibernateException
*/
#Override
public Object assemble(Serializable cached, Object owner) throws HibernateException {
return new JsonString(((JsonString)cached).get());
}
/**
* During merge, replace the existing (target) value in the entity we are merging to
* with a new (original) value from the detached entity we are merging. For immutable
* objects, or null values, it is safe to simply return the first parameter. For
* mutable objects, it is safe to return a copy of the first parameter. For objects
* with component values, it might make sense to recursively replace component values.
*
* #param original the value from the detached entity being merged
* #param target the value in the managed entity
* #return the value to be merged
*/
#Override
public Object replace(Object original, Object target, Object o2) throws HibernateException {
return original;
}
}
package de.project.config;
import java.sql.Types;
import org.hibernate.dialect.H2Dialect;
public class JsonbH2Dialect extends H2Dialect {
public JsonbH2Dialect(){
super();
// Das Datenbanksystem H2 kennt den passenden Datentyp OTHER für Java-Objekte.
// Im HibernateDialect H2Dialect fehlt das Mapping, welches wir hier ergänzen.
// Aktuell verwenden wir Types.OTHER für PGObject (jsonb von PostgreSQL).
this.registerColumnType(Types.OTHER, "OTHER");
}
}

OSGi: service binding without lifecycle management

I am building a Java application on the Equinox OSGi framework and I have been using DS (declarative services) to declare referenced and provided services. So far all the service consumers I have implemented happened to be service providers as well, so it was natural for me to make them stateless (so that they can be reused by multiple consumers, rather than being attached to one consumer) and let them be instantiated by the framework (default constructor, invoked nowhere in my code).
Now I have a different situation: I have a class MyClass that references a service MyService but is not itself a service provider. I need to be able to instantiate MyClass myself, rather than letting the OSGi framework instantiate it. I would then want the framework to pass the existing MyService instance to the MyClass instance(s). Something like this:
public class MyClass {
private String myString;
private int myInt;
private MyService myService;
public MyClass(String myString, int myInt) {
this.myString = myString;
this.myInt= myInt;
}
// bind
private void setMyService(MyService myService) {
this.myService = myService;
}
// unbind
private void unsetMyService(MyService myService) {
this.myService = null;
}
public void doStuff() {
if (myService != null) {
myService.doTheStuff();
} else {
// Some fallback mechanism
}
}
}
public class AnotherClass {
public void doSomething(String myString, int myInt) {
MyClass myClass = new MyClass(myString, myInt);
// At this point I would want the OSGi framework to invoke
// the setMyService method of myClass with an instance of
// MyService, if available.
myClass.doStuff();
}
}
My first attempt was to use DS to create a component definition for MyClass and reference MyService from there:
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="My Class">
<implementation class="my.package.MyClass"/>
<reference bind="setMyService" cardinality="0..1" interface="my.other.package.MyService" name="MyService" policy="static" unbind="unsetMyService"/>
</scr:component>
However, MyClass is not really a component, since I don't want its lifecycle to be managed -- I want to take care of instantiation myself. As Neil Bartlett points out here:
For example you could say that your component "depends on" a
particular service, in which case the component will only be created
and activated when that service is available -- and also it will be
destroyed when the service becomes unavailable.
This is not what I want. I want the binding without the lifecycle management.
[Note: Even if I set the cardinality to 0..1 (optional and unary), the framework will still try instantiate MyClass (and fail because of the lack of no-args constructor)]
So, my question: is there a way to use DS to have this "binding-only, no lifecycle management" functionality I'm looking for? If this is not possible with DS, what are the alternatives, and what would you recommend?
Update: use ServiceTracker (suggested by Neil Bartlett)
IMPORTANT: I've posted an improved version of this below as an answer. I'm just keeping this here for "historic" purposes.
I'm not sure how to apply ServiceTracker in this case. Would you use a static registry as shown below?
public class Activator implements BundleActivator {
private ServiceTracker<MyService, MyService> tracker;
#Override
public void start(BundleContext bundleContext) throws Exception {
MyServiceTrackerCustomizer customizer = new MyServiceTrackerCustomizer(bundleContext);
tracker = new ServiceTracker<MyService, MyService>(bundleContext, MyService.class, customizer);
tracker.open();
}
#Override
public void stop(BundleContext bundleContext) throws Exception {
tracker.close();
}
}
public class MyServiceTrackerCustomizer implements ServiceTrackerCustomizer<MyService, MyService> {
private BundleContext bundleContext;
public MyServiceTrackerCustomizer(BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
#Override
public MyService addingService(ServiceReference<MyService> reference) {
MyService myService = bundleContext.getService(reference);
MyServiceRegistry.register(myService); // any better suggestion?
return myService;
}
#Override
public void modifiedService(ServiceReference<MyService> reference, MyService service) {
}
#Override
public void removedService(ServiceReference<MyService> reference, MyService service) {
bundleContext.ungetService(reference);
MyServiceRegistry.unregister(service); // any better suggestion?
}
}
public class MyServiceRegistry {
// I'm not sure about using a Set here... What if the MyService instances
// don't have proper equals and hashCode methods? But I need some way to
// compare services in isActive(MyService). Should I just express this
// need to implement equals and hashCode in the javadoc of the MyService
// interface? And if MyService is not defined by me, but is 3rd-party?
private static Set<MyService> myServices = new HashSet<MyService>();
public static void register(MyService service) {
myServices.add(service);
}
public static void unregister(MyService service) {
myServices.remove(service);
}
public static MyService getService() {
// Return whatever service the iterator returns first.
for (MyService service : myServices) {
return service;
}
return null;
}
public static boolean isActive(MyService service) {
return myServices.contains(service);
}
}
public class MyClass {
private String myString;
private int myInt;
private MyService myService;
public MyClass(String myString, int myInt) {
this.myString = myString;
this.myInt= myInt;
}
public void doStuff() {
// There's a race condition here: what if the service becomes
// inactive after I get it?
MyService myService = getMyService();
if (myService != null) {
myService.doTheStuff();
} else {
// Some fallback mechanism
}
}
protected MyService getMyService() {
if (myService != null && !MyServiceRegistry.isActive(myService)) {
myService = null;
}
if (myService == null) {
myService = MyServiceRegistry.getService();
}
return myService;
}
}
Is this how you would do it?
And could you comment on the questions I wrote in the comments above? That is:
Problems with Set if the service implementations don't properly implement equals and hashCode.
Race condition: the service may become inactive after my isActive check.
No this falls outside the scope of DS. If you want to directly instantiate the class yourself then you will have to use OSGi APIs like ServiceTracker to obtain the service references.
Update:
See the following suggested code. Obviously there are a lot of different ways to do this, depending on what you actually want to achieve.
public interface MyServiceProvider {
MyService getService();
}
...
public class MyClass {
private final MyServiceProvider serviceProvider;
public MyClass(MyServiceProvider serviceProvider) {
this.serviceProvider = serviceProvider;
}
void doStuff() {
MyService service = serviceProvider.getService();
if (service != null) {
// do stuff with service
}
}
}
...
public class ExampleActivator implements BundleActivator {
private MyServiceTracker tracker;
static class MyServiceTracker extends ServiceTracker<MyService,MyService> implements MyServiceProvider {
public MyServiceTracker(BundleContext context) {
super(context, MyService.class, null);
}
};
#Override
public void start(BundleContext context) throws Exception {
tracker = new MyServiceTracker(context);
tracker.open();
MyClass myClass = new MyClass(tracker);
// whatever you wanted to do with myClass
}
#Override
public void stop(BundleContext context) throws Exception {
tracker.close();
}
}
Solution: use ServiceTracker (as suggested by Neil Bartlett)
Note: if you want to see the reason for the downvote please see Neil's answer and our back-and-forth in its comments.
In the end I've solved it using ServiceTracker and a static registry (MyServiceRegistry), as shown below.
public class Activator implements BundleActivator {
private ServiceTracker<MyService, MyService> tracker;
#Override
public void start(BundleContext bundleContext) throws Exception {
MyServiceTrackerCustomizer customizer = new MyServiceTrackerCustomizer(bundleContext);
tracker = new ServiceTracker<MyService, MyService>(bundleContext, MyService.class, customizer);
tracker.open();
}
#Override
public void stop(BundleContext bundleContext) throws Exception {
tracker.close();
}
}
public class MyServiceTrackerCustomizer implements ServiceTrackerCustomizer<MyService, MyService> {
private BundleContext bundleContext;
public MyServiceTrackerCustomizer(BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
#Override
public MyService addingService(ServiceReference<MyService> reference) {
MyService myService = bundleContext.getService(reference);
MyServiceRegistry.getInstance().register(myService);
return myService;
}
#Override
public void modifiedService(ServiceReference<MyService> reference, MyService service) {
}
#Override
public void removedService(ServiceReference<MyService> reference, MyService service) {
bundleContext.ungetService(reference);
MyServiceRegistry.getInstance().unregister(service);
}
}
/**
* A registry for services of type {#code <S>}.
*
* #param <S> Type of the services registered in this {#code ServiceRegistry}.<br>
* <strong>Important:</strong> implementations of {#code <S>} must implement
* {#link #equals(Object)} and {#link #hashCode()}
*/
public interface ServiceRegistry<S> {
/**
* Register service {#code service}.<br>
* If the service is already registered this method has no effect.
*
* #param service the service to register
*/
void register(S service);
/**
* Unregister service {#code service}.<br>
* If the service is not currently registered this method has no effect.
*
* #param service the service to unregister
*/
void unregister(S service);
/**
* Get an arbitrary service registered in the registry, or {#code null} if none are available.
* <p/>
* <strong>Important:</strong> note that a service may become inactive <i>after</i> it has been retrieved
* from the registry. To check whether a service is still active, use {#link #isActive(Object)}. Better
* still, if possible don't store a reference to the service but rather ask for a new one every time you
* need to use the service. Of course, the service may still become inactive between its retrieval from
* the registry and its use, but the likelihood of this is reduced and this way we also avoid holding
* references to inactive services, which would prevent them from being garbage-collected.
*
* #return an arbitrary service registered in the registry, or {#code null} if none are available.
*/
S getService();
/**
* Is {#code service} currently active (i.e., running, available for use)?
* <p/>
* <strong>Important:</strong> it is recommended <em>not</em> to store references to services, but rather
* to get a new one from the registry every time the service is needed -- please read more details in
* {#link #getService()}.
*
* #param service the service to check
* #return {#code true} if {#code service} is currently active; {#code false} otherwise
*/
boolean isActive(S service);
}
/**
* Implementation of {#link ServiceRegistry}.
*/
public class ServiceRegistryImpl<S> implements ServiceRegistry<S> {
/**
* Services that are currently registered.<br>
* <strong>Important:</strong> as noted in {#link ServiceRegistry}, implementations of {#code <S>} must
* implement {#link #equals(Object)} and {#link #hashCode()}; otherwise the {#link Set} will not work
* properly.
*/
private Set<S> myServices = new HashSet<S>();
#Override
public void register(S service) {
myServices.add(service);
}
#Override
public void unregister(S service) {
myServices.remove(service);
}
#Override
public S getService() {
// Return whatever service the iterator returns first.
for (S service : myServices) {
return service;
}
return null;
}
#Override
public boolean isActive(S service) {
return myServices.contains(service);
}
}
public class MyServiceRegistry extends ServiceRegistryImpl<MyService> {
private static final MyServiceRegistry instance = new MyServiceRegistry();
private MyServiceRegistry() {
// Singleton
}
public static MyServiceRegistry getInstance() {
return instance;
}
}
public class MyClass {
private String myString;
private int myInt;
public MyClass(String myString, int myInt) {
this.myString = myString;
this.myInt= myInt;
}
public void doStuff() {
MyService myService = MyServiceRegistry.getInstance().getService();
if (myService != null) {
myService.doTheStuff();
} else {
// Some fallback mechanism
}
}
}
If anyone wants to use this code for whatever purpose, go ahead.

GWT polymorphic lists with #ExtraTypes

I have a little problem with a list that contains different types of elements and i would like to see if anyone of you have met the problem before. The issue should be solved with the use of #ExtraTypes, but it is not working for me, so i guess i am not using it correctly. So, the scenario is (bean names are changed for clarity):
GENERAL:
I am using GWT 2.5 with RequestFactory.
SERVER-SIDE:
I have a RootBean that contains, among other stuff, a List <ChildBean>.
This ChildBean contains some primitive attributes.
ChildBean is also extended by a MoreSpecificChildBean that inherits all the parent attributes and adds a few more.
The RootBean gets its list filled up with elements of type ChildBean and MoreSpecificChildBean depending on some logic.
CLIENT-SIDE:
IRootBeanProxy is a ValueProxy with these annotations:
#ProxyFor (value = RootBean.class)
#ExtraTypes ({IMoreSpecificChildBeanProxy.class})
and contains a list
List <IChildBeanProxy> getChildren ();
IChildBeanProxy is a ValueProxy:
#ProxyFor (value=ChildBean)
public interface IChildBeanProxy extends ValueProxy
IMoreSpecificChildBeanProxy is a ValueProxy:
#ProxyFor (value=MoreSpecificChildBean)
public interface IMoreSpecificChildBeanProxy extends IChildBeanProxy
the Request context has a method that returns Request and i added the #ExtraTypes annotation here too:
#Service (value = CompareService.class, locator = SpringServiceLocator.class)
#ExtraTypes ({IChildBeanProxy.class, IMoreSpecificChildBeanProxy.class})
public interface ICompareRequestContext extends RequestContext {
Request <IRootBeanProxy> compare (Integer id1, Integer id2);
Question
Supposedly with those annotations, RF should be aware of the existence of polymorphic inherited classes, but all i get in the client is an IRootBeanProxy with a list of IChildBeanProxy elements. This list includes the MoreSpecificChildBean, but in the shape of a IChildBeanProxy, so that i cannot tell it from the others.
So i am wondering what i am doing wrong, if i am setting the ExtraTypes annotation at the wrong place or something.
Anyone?
Thx for all the help!!
I do the exact same thing for quite a few classes but it will always return me the base type which I can iterate through and test for instanceof if needed. You will probably have to cast the object to the subclass. If you do not add the #ExtraTypes you will know because on the server side you will get a message stating that MoreSpecificChildBean cannot be sent to the client.
I only annotate the service and not the proxy, I ran into some quirks with 2.4 adding #ExtraTypes to the proxy.
/**
* Base proxy that all other metric proxies extend. It is used mainly for it's
* inheritence with the RequestFactory. It's concrete implementation is
* {#link MetricNumber}.
*
* #author chinshaw
*/
#ProxyFor(value = Metric.class, locator = IMetricEntityLocator.class)
public interface MetricProxy extends DatastoreObjectProxy {
/**
* Name of this object in the ui. This will commonly be extended by
* subclasses.
*/
public String NAME = "Generic Metric";
/**
* This is a list of types of outputs that the ui can support. This is
* typically used for listing types of supported Metrics in the operation
* output screen.
*
* #author chinshaw
*/
public enum MetricOutputType {
MetricNumber, MetricString, MetricCollection, MetricStaticChart, MetricDynamicChart
}
/**
* See {#link MetricNumber#setName(String)}
*
* #param name
*/
public void setName(String name);
/**
* See {#link MetricNumber#setContext(String)}
*
* #return name of the metric.
*/
public String getName();
/**
* Get the list of violations attached to this metric.
*
* #return
*/
public List<ViolationProxy> getViolations();
}
#ProxyFor(value = MetricNumber.class, locator = IMetricEntityLocator.class)
public interface MetricNumberProxy extends MetricProxy {
public List<NumberRangeProxy> getRanges();
public void setRanges(List<NumberRangeProxy> ranges);
}
...
#ProxyFor(value = MetricDouble.class, locator = IMetricEntityLocator.class)
public interface MetricDoubleProxy extends MetricNumberProxy {
/* Properties when fetching the object for with clause */
public static String[] PROPERTIES = {"ranges"};
public Double getValue();
}
...
#ProxyFor(value = MetricPlot.class, locator = IMetricEntityLocator.class)
public interface MetricPlotProxy extends MetricProxy {
/**
* UI Name of the object.
*/
public String NAME = "Static Plot";
public String getPlotUrl();
}
This is a made up method from because I usually always return composite classes that may contain a list of metrics. That being said this will return me the base type of metrics, and then I can cast them.
#ExtraTypes({ MetricProxy.class, MetricNumberProxy.class, MetricDoubleProxy.class, MetricIntegerProxy.class})
#Service(value = AnalyticsOperationDao.class, locator = DaoServiceLocator.class)
public interface AnalyticsOperationRequest extends DaoRequest<AnalyticsOperationProxy> {
Request<List<<MetricProxy>> getSomeMetrics();
}
Not an exact method I use but will work for getting a proxy of type.
context.getSomeMetrics().with(MetricNumber.PROPERTIES).fire(new Receiver<List<MetricProxy>>() {
public void onSuccess(List<MetricProxy> metrics) {
for (MetricProxy metric : metrics) {
if (metric instanceof MetricDoubleProxy) {
logger.info("Got a class of double " + metric.getValue());
}
}
}
}
You will know if you are missing an #ExtraTypes annotation when you get the error stated above.
Hope that helps

NullPointerException when tyring to create table for mapped-superclass

I traced it down to the getDatastoreClass returning a null datastore class to the createPerImplementationColumnsForReferenceField.
I have tried both the 3.1.1 and now using 3.2.0-m4 release hoping that would fix my problem.
RDBMSStoreManager#getDatastoreClass(String className, ClassLoaderResolver clr);
It is returning a null datastore class to the
ReferenceMapping#createPerImplementationColumnsForReferenceField(boolean pk, boolean nullable, boolean serialised, boolean embedded, int fieldRole, ColumnMetaData[] columnMetaData, ClassLoaderResolver clr)
I am using the orm to annotate a mapped-superclass and this mapped-superclass does not have a table definition and both of my mapped-superclass throwing this exception.
499170 [http-bio-8080-exec-8] DEBUG DataNucleus.Datastore.Schema - Field [com.hp.vf.server.domain.AlertDefinition.isPublic] -> Column(s) ["ALERTDEFINITION"."ISPUBLIC"] using mapping of type "org.datanucleus.store.rdbms.mapping.java.BooleanMapping" (org.datanucleus.store.rdbms.mapping.datastore.SmallIntRDBMSMapping)
551964 [http-bio-8080-exec-8] DEBUG DataNucleus.Persistence - Managing Persistence of Class : com.hp.vf.analytics.shared.metric.Metric [Table : (none), InheritanceStrategy : subclass-table]
561964 [http-bio-8080-exec-8] ERROR DataNucleus.Datastore.Schema - An exception was thrown while adding/validating class(es) : null
java.lang.NullPointerException
at org.datanucleus.store.rdbms.mapping.java.ReferenceMapping.createPerImplementationColumnsForReferenceField(ReferenceMapping.java:452)
at org.datanucleus.store.rdbms.mapping.java.ReferenceMapping.prepareDatastoreMapping(ReferenceMapping.java:214)
at org.datanucleus.store.rdbms.mapping.java.ReferenceMapping.initialize(ReferenceMapping.java:110)
at org.datanucleus.store.rdbms.mapping.java.InterfaceMapping.initialize(InterfaceMapping.java:54)
In the Reference mapping, dc is null when trying to execute getIdMapping(), I have verified this in the debugger.
try
{
DatastoreClass dc = storeMgr.getDatastoreClass(implClass.getName(), clr);
m = dc.getIdMapping(); // DC is null
}
catch (NoTableManagedException ex)
{
// TODO Localise this message
throw new NucleusUserException("Cannot define columns for " + mmd.getFullFieldName() +
" due to " + ex.getMessage(), ex);
}
Here is the problematic class file.
public abstract class Metric implements IMetric {
/**
* Serialization ID
*/
private static final long serialVersionUID = 3806479436166940035L;
private Long id;
/**
* The name of the metric, this is not mandatory we have some metrics that
* may come back without names.
*/
protected String name;
/**
* This is an optional metric value that can be set by the script in order
* to add context to the execution of the metric.
*/
protected String context;
/**
* The list of violations associated with this metric.
*/
protected List<Violation> violations = null;
public Metric() {
violations = new ArrayList<Violation>();
}
/**
* Constructor that takes the name of the object and the value that it
* represents.
*
* #param name
* #param value
*/
public Metric(String name) {
this();
this.name = name;
}
/* (non-Javadoc)
* #see com.hp.vf.taskengine.shared.metric.IMetric#getName()
*/
#Override
public String getName() {
return name;
}
/* (non-Javadoc)
* #see com.hp.vf.taskengine.shared.metric.IMetric#setName(java.lang.String)
*/
#Override
public void setName(String name) {
this.name = name;
}
/**
* This is the context that represents the metric. This could have come from
* R and would be a Key:Value; pair of values used to calculate the value.
* For example if a metric was calculated for a product in houston for ISS
* the context may look like "ProdNum:1234;Factory:Houston;BUnit:ISS". This
* context is useful when chaining together tasks.
*
* #return String context used when chaining tasks together.
*/
public String getContext() {
return context;
}
/* (non-Javadoc)
* #see com.hp.vf.taskengine.shared.metric.IMetric#toString()
*/
public String toString() {
String debugString = "";
debugString += "Metric: " + name;
return debugString;
}
/* (non-Javadoc)
* #see com.hp.vf.taskengine.shared.metric.IMetric#hasMetricViolations()
*/
#Override
public boolean hasMetricViolations() {
return (violations != null && violations.size() > 0) ? true : false;
}
/* (non-Javadoc)
* #see com.hp.vf.taskengine.shared.metric.IMetric#getViolations()
*/
#Override
public List<IViolation> getViolations() {
return violations;
}
/* (non-Javadoc)
* #see com.hp.vf.taskengine.shared.metric.IMetric#setViolations(java.util.List)
*/
#Override
public void setViolations(List<IViolation> violations) {
this.violations = violations;
}
#Override
public Long getId() {
return id;
}
}
Here is an excert from my orm.xml file
<access>FIELD</access>
<mapped-superclass class="com.hp.vf.analytics.shared.metric.Metric" access="FIELD">
<attributes>
<basic name="name" />
<basic name="context" />
<one-to-many name="violations">
<cascade>
<cascade-all />
</cascade>
</one-to-many>
</attributes>
</mapped-superclass>
Am I doing something wrong or is this a bug?
I was able to resolve the problem using the tips from Datanucleus. I had multiple problems
Problems
The most obvious and silly on my part
If you have an one-to-many or one-to-one that uses an interface you must specify the target-entity.
/**
* The list of violations associated with this metric.
*/
protected List<IViolation> violations = null;
...
<one-to-many name="violations"
target-entity="com.vf.analytics.shared.metric.Violation">
<cascade>
<cascade-all />
</cascade>
</one-to-many>
Don't use generics, the jpa spec does not suppport them
Not sure if datanucleus supports generics but it but probably not recommended.
public MetricNumber< T extends Number> extends Metric implements IMetric {
T value;
}
If you extend an entity that is not annotated make sure that you add it to your orm.xml
I wasn't testing some of the objects so I assumed that datanucleus would ignore them but this appears not to be the case. I had extended third party classes that were not annotated but had not yet added them to the orm.xml, this was also causing null the null pointer exceptions I was getting.