Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

net.greghaines.jesque.utils.NoSuchConstructorException #38

Closed
jml6m opened this issue Dec 20, 2013 · 5 comments
Closed

net.greghaines.jesque.utils.NoSuchConstructorException #38

jml6m opened this issue Dec 20, 2013 · 5 comments
Assignees

Comments

@jml6m
Copy link

jml6m commented Dec 20, 2013

Can't find much information about this...it's being thrown in the ReflectionUtils.findConstructor() method...

My job has a proper constructor which takes one argument to fill its one attribute. I also added a blank, default constructor and that still didn't fix the problem. This Object's attribute is an object that also has properly defined constructors.

@jbcpollak
Copy link

I work with @jml6m, we are doing something roughly like this to enqueue a job:

// Add a job to the delayed queue
final Job job = new Job("FooAction", new Foo("a", "b", "c"));

This seems to work ok. Our action is defined like this:

public class FooAction implements Runnable {
    FooAction(Foo f) {
        // ....
    }

    // ....
}

and:

// Start a worker to run jobs from the queue
final Worker worker = new WorkerImpl(config, 
    Arrays.asList("foo"), map(entry("FooAction", FooAction.class)));
final Thread workerThread = new Thread(worker);
workerThread.start();

When we try and deserialize our job into the action, we get the NoSuchConstructorException.

It appears Jesque is attempting to call a constructor with an Object[] array of the fields in Foo, rather than with a deserialized Foo.

I suppose this is rather obvious behavior since the API doesn't provide a way to tell Jesque which class to deserialize into.

We are wondering if there is a supported use-case and if so, how do we do it?

@ghost ghost assigned gresrun Dec 23, 2013
@gresrun
Copy link
Owner

gresrun commented Dec 23, 2013

Behind the scenes, Jesque uses Jackson to serialize and de-serialize objects into and out of JSON. Jackson transparently handles most common objects (primitives & wrappers, Strings, collections), however, if you wish to send an object of your creation, you need to tell Jackson how to convert it back and forth from JSON.

The easiest solution is to refactor to not use custom types but I know that is not always possible. Jesque leverages Jackson Data-Bind library which allows for registering custom serializers.

Jesque has a singleton, ObjectMapperFactory that creates the shared Jackson ObjectMapper that is used throughout Jesque. You can see Jesque already registers custom serializers for Jesque types using a Feature Module on this ObjectMapper. Below is an example of how you would plug in your serializers:

ObjectMapper mapper = ObjectMapperFactory.get()
mapper.registerModule(new SimpleModule("com.acme.jesque.project", Version.unknownVersion())
    .addSerializer(Foo.class, new FooJsonSerializer())
    .addDeserializer(Foo.class, new FooJsonDeserializer());

You can find examples of custom serializers in the net.greghaines.jesque.json package.

@gresrun
Copy link
Owner

gresrun commented Jan 3, 2014

@jml6m @jbcpollak Did this help? Do you have any other questions?

@gresrun gresrun closed this as completed Jan 9, 2014
@danieldk
Copy link
Contributor

A simpler workaround is to call:

ObjectMapper objectMapper = ObjectMapperFactory.get();
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT);

Since the arguments are referred to by the Object superclass, Jackson will add the class name to the serialized JSON, this is used as a hint during deserialization to instantiate the correct class.

Though, I still have a question about this. To me, it seems that there are two approaches to constructor matching:

  1. First deserialize the JSON. Then look for a constructor with the correct number of arguments and types which match the objects that were deserialized from JSON.
  2. Find the constructors with the right number of arguments. Then attempt to deserialize the JSON objects to the types of the arguments.

Reading the source code of Jesque, it seems to follow approach (1). The advantage is that you only parse each JSON object once and it works well with supertypes.

Approach (2) may be less efficient, since there may be multiple constructors with the correct number of arguments, and the first constructor may not be the correct one. Also, there may a problem when the constructor arguments are supertypes of the objects that were serialized.

But in many cases the actual class will only have a small number of constructors (e.g. in our application they currently have one), with the actual types. The advantage of approach (2) is then that the deserialization is guided by the constructor arguments and does not require extra work on the part of the user to write custom serializers.

Would it be a possibility to provide (2) als an alternative strategy to (1)?

@gresrun
Copy link
Owner

gresrun commented Jan 31, 2014

You are correct about that feature of Jackson. Just be aware that additional type information in the JSON is a Jackson-specific feature; if multiple languages/environments are involved, that feature may need to be handled in the other environments. (Jesque has striven to remain compatible with the original Resque and Coffee-Resque implementations so, that is why this is not enabled by default)

Regarding your question, my preference is to take the simplest, most efficient approach here (Option 1), especially in light of the numerous options that Jackson provides. Any of the features of Jacskon (Jackson/JAXB annotations, polymorphic type info, custom serializers/de-serializers, etc.) may be used with Jesque to facilitate the conversion between types and JSON. Jackson is the industry standard in JSON marshaling for Java; I prefer to rely on their well-tested and documented capabilities rather than rolling my own solution.

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants