Wildfly throws "Unable to find a constructor that takes a String param or a valueOf() or fromString() method for javax.ws.rs.QueryParam" error - rest

Im using wildfly 9.0 to deploy my war file. I have java LocalDateTime, Java Money types defined in my REST GET endpoints.
When i deploy my war file, i get following error[1]. Based on this answer [2] I have written "ParamConverterProvider" implementations for both types.
It was working fine( I haven't seen same issue again till now) and now i get same issue.
Any clue?
[1]
Caused by: java.lang.RuntimeException: Unable to find a constructor that takes a String param or a valueOf() or fromString() method for javax.ws.rs.QueryParam(\"totalMoneyVolumeForPeriod\") on public javax.ws.rs.core.Response com.test.rest.StockEndpoint.getItems(java.lang.Integer,java.lang.Integer,java.lang.String,java.lang.String,java.lang.Long,org.javamoney.moneta.Money,java.util.Set,java.lang.String) for basetype: org.javamoney.moneta.Money"}}}}
[2]
jaxrs could not find my custom (de)serializers for joda.money type
Sample code
package com.test;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import javax.money.Monetary;
import javax.ws.rs.ext.ParamConverter;
import javax.ws.rs.ext.ParamConverterProvider;
import javax.ws.rs.ext.Provider;
import org.javamoney.moneta.Money;
#Provider
public class MoneyConverterProvider implements ParamConverterProvider {
private final MoneyConverter converter = new MoneyConverter();
#Override
public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) {
if (!rawType.equals(Money.class)) return null;
return (ParamConverter<T>) converter;
}
public class MoneyConverter implements ParamConverter<Money> {
public Money fromString(String value) {
if (value == null ||value.isEmpty()) return null; // change this for production
return Money.of(new BigDecimal(value), Monetary.getCurrency("AUD"));
}
public String toString(Money value) {
if (value == null) return "";
return value.toString(); // change this for production
}
}
}
Application claas
package com.test;
import javax.ws.rs.core.Application;
import com.test.autogen*;
import io.swagger.jaxrs.config.BeanConfig;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.ApplicationPath;
#ApplicationPath("/rest")
public class RestApplication extends Application {
public RestApplication() {
BeanConfig beanConfig = new BeanConfig();
//beanConfig.setVersion("1.0");
beanConfig.setSchemes(new String[] { "http" });
beanConfig.setTitle("My API");
beanConfig.setBasePath("/rest");
beanConfig.setResourcePackage("com.test.autogen");
beanConfig.setScan(true);
}
#Override
public Set<Class<?>> getClasses() {
HashSet<Class<?>> set = new HashSet<Class<?>>();
set.add(EmailEndpoint.class);
set.add(StockEndpoint.class);
set.add(io.swagger.jaxrs.listing.ApiListingResource.class);
set.add(io.swagger.jaxrs.listing.SwaggerSerializers.class);
return set;
}
}

When you are using classpath scanning, JAX-RS components annotated with #Path or #Provider will get picked up and registered. There are a couple way to use classpath scanning. The most common way is to just have an empty Application class annotated with #ApplicationPath
#ApplicationPath("/api")
public class MyApplication extends Application {}
This is enough for a JAX-RS application to be loaded, and to have the application's classpath scanned to components to register.
But, per the specification, once we override any of the Set<Object> getSingletons or Set<Class> getClasses methods of the Application class, and return a non-empty set, this automatically disables classpath scanning, as it is assumed we want to register everything ourselves.
So in previous cases, you were probably just using classpath scanning. In this case, you need to explicitly add the provider to the set of classes in your getClasses method, since you overrode the method to add other component classes.

Related

Micronauts Failed to inject value for parameter [entityManager] of class: UserRepository

I'm trying to inject EntityManager in the repository.
compilation was successful but when i run the application and send a post request i got this error:
Unexpected error occurred: Failed to inject value for parameter [entityManager] of class: aututor.apps.DAO.Repository.RepositoryImp.UserRepository
Message: No bean of type [javax.persistence.EntityManager] exists. Make sure the bean is not disabled by bean requirements (enable trace logging for 'io.micronaut.context.condition' to check) and if the bean is enabled then ensure the class is declared a bean and annotation processing is enabled (for Java and Kotlin the 'micronaut-inject-java' dependency should be configured as an annotation processor).
Path Taken: new UserController([UserService service]) --> new UserServiceImpl([IUserRepository userRepository]) --> new UserRepository([EntityManager entityManager])
io.micronaut.context.exceptions.DependencyInjectionException: Failed to inject value for parameter [entityManager] of class: aututor.apps.DAO.Repository.RepositoryImp.UserRepository
this is the repository :
package aututor.apps.DAO.Repository.IRepository;
import aututor.apps.DAO.Model.*;
public interface IUserRepository {
public User save(User User);
public User findByEmail(String Email);
}
package aututor.apps.DAO.Repository.RepositoryImp;
import aututor.apps.DAO.Model.User;
import aututor.apps.DAO.Repository.IRepository.IUserRepository;
import io.micronaut.configuration.hibernate.jpa.scope.CurrentSession;
import javax.inject.Singleton;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
#Singleton
public class UserRepository implements IUserRepository {
#PersistenceContext
protected EntityManager entityManager;
public UserRepository(#CurrentSession EntityManager entityManager) {
this.entityManager = entityManager;
}
#Override
public User save(User User) {
return entityManager.merge(User);
}
#Override
public User findByEmail(String Email) {
return entityManager.createQuery("SELECT user FROM User user WHERE user.Email = :email", User.class)
.setParameter("email", Email)
.getSingleResult();
}
}
this is the service :
package aututor.apps.Services.ServicesImpl;
import aututor.apps.DAO.Model.User;
import aututor.apps.DAO.Repository.IRepository.IUserRepository;
import aututor.apps.DTO.Mapper.UserMapper;
import aututor.apps.DTO.UserDTO;
import aututor.apps.Services.IServices.UserService;
import aututor.apps.Util.Response;
import javax.inject.Singleton;
#Singleton
public class UserServiceImpl implements UserService {
protected final IUserRepository userRepository;
protected final UserMapper userMapper= new UserMapper();
public UserServiceImpl(IUserRepository userRepository) {
this.userRepository = userRepository;
}
#Override
public Response CreateUser(UserDTO User) {
if(userRepository.findByEmail(User.getEmail())==null) {
User u = userRepository.save(userMapper.ToUser(User));
return new Response("request has succeeded",userMapper.ToUserDTO(u),200);
}
else {
return new Response("Not Acceptable : Adress email already exist",null,406);
}
}
#Override
public Response UpdateUser(UserDTO User) {
return null;
}
#Override
public Response DeleteUser(UserDTO User) {
return null;
}
#Override
public Response FindUserByID(Long Id) {
return null;
}
}
Controller :
package aututor.apps.Controllers;
import aututor.apps.DTO.UserDTO;
import aututor.apps.Services.IServices.UserService;
import aututor.apps.Util.Response;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.*;
#Controller("/rest/User")
public class UserController {
protected final UserService service ;
public UserController(UserService service) {
this.service = service;
}
#Post("/")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response CreateUser(#Body UserDTO user) {
return service.CreateUser(user);
}
}
build.gradle :
dependencies {
annotationProcessor platform("io.micronaut:micronaut-bom:$micronautVersion")
annotationProcessor "io.micronaut:micronaut-inject-java"
annotationProcessor "io.micronaut:micronaut-validation"
annotationProcessor "io.micronaut.data:micronaut-data-processor:1.0.0"
implementation platform("io.micronaut:micronaut-bom:$micronautVersion")
implementation "io.micronaut:micronaut-security-jwt"
implementation "io.micronaut:micronaut-http-client"
implementation "io.micronaut:micronaut-validation"
implementation "io.micronaut:micronaut-http-server-netty"
implementation "io.micronaut.configuration:micronaut-hibernate-jpa"
implementation("io.micronaut:micronaut-spring")
compile "io.micronaut.configuration:micronaut-jdbc-tomcat"
compile "io.micronaut:micronaut-runtime"
compile "io.micronaut:micronaut-inject"
compile("io.micronaut.data:micronaut-data-hibernate-jpa")
compile("io.micronaut.data:micronaut-data-jdbc")
compile("org.postgresql:postgresql:42.2.1")
runtimeOnly "ch.qos.logback:logback-classic:1.2.3"
testAnnotationProcessor platform("io.micronaut:micronaut-bom:$micronautVersion")
testAnnotationProcessor "io.micronaut:micronaut-inject-java"
testImplementation platform("io.micronaut:micronaut-bom:$micronautVersion")
testImplementation "org.junit.jupiter:junit-jupiter-api"
testImplementation "io.micronaut.test:micronaut-test-junit5"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine"
}
application.yml:
---
micronaut:
application:
name: apps
server:
port: 8089
---
datasources:
default:
url: jdbc:postgresql://localhost:5432/db
username: root
password: ''
schema-generate: CREATE_DROP
jpa:
default:
properties:
hibernate:
hbm2ddl:
auto: update
dialect: org.hibernate.dialect.PostgreSQL95Dialect
entity-scan:
packages: 'apps.model'
i have tried to work with CrudRepository and there was a problem there also. Everything looks correct i don't know if i miss something.
I started working with Micronaut JPA and Hibernate and came across the same kind of issue that you have. Whatever scans the application.yml is not good at all in telling you if you didn't define your jpa settings properly. After getting experience from beating my head over for days trying to get Micronaut to work with data (and on the brink of giving up on using Micronaut from accessing data at all) from looking at your setup, my best bet is you defined your entity-scan packages wrong. If you specify an entity scan package it must be a package that encapsulates or is a parent encapsulating package of your classes that are annotated with #Entity. In your case you specified apps.model as your package, but I see no where in the code you provided where you are referencing anything that follows that package format. Not sure why you have a aututor.apps.DAO.Model.User package. Model should be separate from your DAO. The easiest thing would be to just delete entity-scan packages and let all your classes get scanned. Specifying the package is supposed to improve performance, but if you don't have a lot of files I don't think it really makes a difference. You could have specified aututor or aututor.apps or aututor.apps.DAO or aututor.apps.DAO.Model as your scan package and that should work if your classes with #Entity are in aututor.apps.DAO.Model package.
apply annotation #Repository on your repository class.

Invalid lambda deserialization with Infinispan Cache and computeIfAbsent

I’m toying with an basic infinispan cluster and I came across a puzzling error.
I’m basically implementing a shared map, holding just one Integer
Here is the code of my service
package sandbox.infinispan.test.service;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.inject.Named;
import org.infinispan.Cache;
#Named("useThisOne")
#ApplicationScoped
public class CounterService implements ICounterService {
private static final String KEY = "key";
#Inject
private Cache<String, Integer> cache;
#Override
public void inc(final int amount) {
this.cache.put(KEY, Integer.valueOf(this.get() + amount));
}
#Override
public int get() {
return this.cache.computeIfAbsent(KEY, k -> Integer.valueOf(0)).intValue();
}
}
Cache is produced with the following:
package sandbox.infinispan.test.config;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Produces;
import org.infinispan.configuration.cache.CacheMode;
import org.infinispan.configuration.cache.Configuration;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.configuration.global.GlobalConfiguration;
import org.infinispan.configuration.global.GlobalConfigurationBuilder;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.manager.EmbeddedCacheManager;
#Dependent
class CacheProvider {
#Produces
#ApplicationScoped
private EmbeddedCacheManager defaultClusteredCacheManager() {
final GlobalConfiguration g = new GlobalConfigurationBuilder() //
.clusteredDefault() //
.transport() //
.nodeName(this.getNodeName()) //
.clusterName("infinispanTestCluster") //
.build();
final Configuration cfg = new ConfigurationBuilder() //
.clustering() //
.cacheMode(CacheMode.REPL_SYNC) ///
.build();
return new DefaultCacheManager(g, cfg);
}
}
When there are at least two servers in the cluster, computeIfAbsent fails with
15:48:50,253 ERROR [org.infinispan.interceptors.impl.InvocationContextInterceptor] (jgroups-7,myhostname-14393) ISPN000136: Error executing command ComputeIfAbsentCommand, writing keys [key]: org.infinispan.remoting.RemoteException: ISPN000217: Received exception from otherhostname-44445, see cause for remote stack trace
which drills down to:
Caused by: java.lang.NoSuchMethodException: sandbox.infinispan.test.service.CounterService.$deserializeLambda$(java.lang.invoke.SerializedLambda)
and finally to:
Caused by: java.lang.IllegalArgumentException: Invalid lambda deserialization
at sandbox.infinispan.test.service.CounterService.$deserializeLambda$(CounterService.java:10)
... 68 more
Caused by: an exception which occurred:
in object of type java.lang.invoke.SerializedLambda
If I rewrite my pretty nice fashionable code to the ugly following, it works.
#Override
public int get() {
Integer value = this.cache.get(KEY);
if (value == null) {
value = Integer.valueOf(0);
this.cache.put(KEY, value);
}
return value.intValue();
}
How can I use the pretty computeIfAbsent way of doing things nowadays ?
Eclipse 2018-12, WildFly 14, java 10 on of the dev member of the cluster, CentOs 7, OpenJdk 10, WildFly 14 on the remote cluster member.
Thanks for your help
Solved (kinda)
Thanks to the help I received here, I transformed the lambda into an inner class :
static class OhWell implements Serializable {
static Integer zero(final String t) {
return Integer.valueOf(0);
}
}
#Override
public int get() {
return this.cache.computeIfAbsent(KEY, OhWell::zero).intValue();
}
It works now, but it’s lots less nice than the neat lambda. So I’ll stick to the old-fashioned way – unless someone can think of a better way to do it.
Further results:
The following static inner class with a static method works
static class StaticOhWell implements Serializable {
static Integer apply(final String t) {
return Integer.valueOf(0);
}
}
#Override
public int get() {
return this.cache.computeIfAbsent(KEY, StaticOhWell::apply).intValue();
}
The following non static inner class with a non static method fails :
class NotStaticOhWell implements SerializableFunction<String, Integer> {
#Override
public Integer apply(final String t) {
return Integer.valueOf(0);
}
}
#Override
public int get() {
return this.cache.computeIfAbsent(KEY, new NotStaticOhWell()::apply).intValue();
}
It fails with this error message NotSerializableException: org.infinispan.cache.impl.EncoderCache:
13:41:29,221 ERROR [org.infinispan.interceptors.impl.InvocationContextInterceptor] (default task-1) ISPN000136: Error executing command ComputeIfAbsentCommand, writing keys [value]: org.infinispan.commons.marshall.NotSerializableException: org.infinispan.cache.impl.EncoderCache
Caused by: an exception which occurred:
in field sandbox.infinispan.test.service.CounterService.cache
in object sandbox.infinispan.test.service.CounterService#4612a6c3
in field sandbox.infinispan.test.service.CounterService$NotStaticOhWell.this$0
in object sandbox.infinispan.test.service.CounterService$NotStaticOhWell#4effd362
in field java.lang.invoke.SerializedLambda.capturedArgs
in object java.lang.invoke.SerializedLambda#e62f08a
in object sandbox.infinispan.test.service.CounterService$$Lambda$1195/1060417313#174a143b
Final words (?)
Using a “static lambda” (a static inner class implementing the SerializableFunction interface) worked too
static class StaticSerializableFunction implements SerializableFunction<String, Integer> {
#Override
public Integer apply(final String t) {
return Integer.valueOf(0);
}
}
#Override
public int get() {
return this.cache.computeIfAbsent(KEY, new StaticSerializableFunction()::apply).intValue();
}
And the winner is…
Making the class actually serializable by “transienting” the Cache allows to simply use a method of this class. No need to create an inner class!
package sandbox.infinispan.test.service;
import java.io.Serializable;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.inject.Named;
import org.infinispan.Cache;
#Named("useThisOne")
#ApplicationScoped
public class CounterService implements ICounterService, Serializable {
private static final String KEY = "value";
#SuppressWarnings("cdi-ambiguous-dependency")
#Inject
private transient Cache<String, Integer> cache;
#Override
public void inc(final int amount) {
this.cache.put(KEY, Integer.valueOf(this.get() + amount));
}
#Override
public int get() {
return this.cache.computeIfAbsent(KEY, this::zero).intValue();
}
private Integer zero(#SuppressWarnings("unused") final String unused) {
return Integer.valueOf(0);
}
#Override
public void reset() {
this.cache.clear();
}
}
Thanks all!
According to Unable to deserialize lambda the deserializer needs the actual code to be available. Are you sure that your application is already started on all other nodes in the cluster (the exact same version, including your lambda)?
The computeIfAbsent() sends the lambda directly to the data (and therefore handles the operation using one RPC, instead of first fetching the value and then writing it as the 'ugly code'). In WF, your application lives in a different classloader (module) than Infinispan and that might cause an issue.
Could you try to refactor your lambda into a class and see if you get similar problem? I am not that knowledgeable about WF, so there might be a mitigation for regular classes that is missing for lambdas.

Returning JSON from RESTful Java server code?

I've inherited a web project that a contractor started. I and my coworkers are unfamiliar with the technology used, and have a number of questions. From what we can tell, this appears to be some sort of RESTful Java server code, but my understanding is there are lots of different types of Java RESTful services. Which one is this? Specific questions:
1) Where can we read more (particularly introductory information) about this specific service?
2) The code creates and returns a JSON through some kind of "magic"... I merely return a model class (code below) that has getter and setter methods for its fields, and it's automagically converted into a JSON. I'd like to learn more about how this is done automagically.
3) We already have some code that creates a JSON. We need to return this using this framework. If I already have a JSON, how do I return that? I tried something like this:
String testJSON = "{\"menu\": {\"id\": \"file\", \"value\": \"Hello there\"}}";
return testJSON;
instead of returning a model object with getters/setters, but this returns a literal text string, not a JSON. Is there a way to return an actual JSON that's already a JSON string, and have it be sent as a JSON?
You don't have to be able to answer all of the questions above. Any/all pointers in a helpful direction appreciated!
CODE
First, the view controller that returns the JSON:
package com.aimcloud.server;
import com.aimcloud.util.MySqlConnection;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
import javax.ws.rs.FormParam;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import com.aimcloud.models.SubscriptionTierModel;
#Path("subscription_tier")
public class SubscriptionTierController
{
// this method will return a list of subscription_tier table entries that are currently active
#GET
#Produces({ MediaType.APPLICATION_JSON })
public String/*ArrayList<SubscriptionTierModel>*/ getSubscriptionTiers(#QueryParam("includeActiveOnly") Boolean includeActiveOnly)
{
MySqlConnection mysql = MySqlConnection.getConnection();
ArrayList<SubscriptionTierModel> subscriptionTierArray = new ArrayList<SubscriptionTierModel>();
String queryString;
if (includeActiveOnly)
queryString = "SELECT * FROM subscription_tier WHERE active=1";
else
queryString = "SELECT * FROM subscription_tier";
List<Map<String, Object>> resultList = mysql.query(queryString, null);
for (Map<String, Object> subscriptionRow : resultList)
subscriptionTierArray.add( new SubscriptionTierModel(subscriptionRow) );
// String testJSON = "{\"menu\": {\"id\": \"file\", \"value\": \"Hello there\"}}";
// return testJSON;
return subscriptionTierArray;
}
}
Next, the model the code above returns:
package com.aimcloud.models;
// NOTE this does NOT import Globals
import java.sql.Types;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.json.JSONObject;
import com.aimcloud.util.LoggingUtils;
public class SubscriptionTierModel extends ModelPrototype
{
private String name;
private Integer num_studies;
private Integer cost_viewing;
private Integer cost_processing;
private Integer active;
protected void setupFields()
{
this.fields.add("name");
this.fields.add("num_studies");
this.fields.add("cost_viewing");
this.fields.add("cost_processing");
this.fields.add("active");
}
public SubscriptionTierModel()
{
super("subscription");
this.setupFields();
}
public SubscriptionTierModel(Map<String, Object> map)
{
super("subscription");
this.setupFields();
this.initFromMap(map);
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setNum_Studies(Integer num_studies) {
this.num_studies = num_studies;
}
public Integer getNum_studies() {
return this.num_studies;
}
public void setCost_viewing(Integer cost_viewing) {
this.cost_viewing = cost_viewing;
}
public Integer getCost_viewing() {
return this.cost_viewing;
}
public void setCost_processing(Integer cost_processing) {
this.cost_processing = cost_processing;
}
public Integer getCost_processing() {
return this.cost_processing;
}
public void setActive(Integer active) {
this.active = active;
}
public Integer getActive() {
return this.active;
}
}
public abstract class ModelPrototype {
protected MySqlConnection mysql;
protected ArrayList<String> fields;
protected String table;
protected Integer id = null;
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
abstract protected void setupFields();
public ModelPrototype() {
mysql = MySqlConnection.getConnection();
this.fields = new ArrayList<String>();
this.fields.add("id");
}
public void initFromDbResult(List<Map<String, Object>> result) {
if (result.size() >= 1)
{
Map<String, Object> userRow = result.get(0);
this.initFromMap(userRow);
if (result.size() > 1)
{
Thread.dumpStack();
}
}
else
{
throw new WebApplicationException(ServerUtils.generateResponse(Response.Status.NOT_FOUND, "resource not found"));
}
}
protected void initFromMap(Map<String, Object> map) {
for (Map.Entry<String, Object> entry : map.entrySet()) {
Object value = entry.getValue();
// LoggingUtils.log(entry.getKey() + " " + entry.getValue().toString());
if (value != null && this.fields.contains(entry.getKey())) {
this.setField(entry.getKey(), value);
}
}
}
....
1) Where can we read more (particularly introductory information)
about this specific service?
This is a RESTful service that uses basic jax-rs annotations to build the service. I suggest looking at a tutorial like "REST using jersey" or "REST using CXF".
2) The code creates and returns a JSON through some kind of "magic"...
The restful framework used usually takes care of this. #Produces({ MediaType.APPLICATION_JSON }) annotation indicates the framework to do this conversion.This will be defined somewhere in the configuration. Check the spring config files if you are using spring to define the beans. Usually a mapper or a provider will be defined that converts the object to json.
3) We already have some code that creates a JSON. We need to return this using this framework. If I already have a JSON, how do I return that? I tried something like this:
If you already have a json just return that json from the method. Remember to still have the #Produces({ MediaType.APPLICATION_JSON }) annotation on the method.
but this returns a literal text string, not a JSON
A json is a string. That is what you will see in the response, unless you deserialize it back to an object.
I suggest you read up on JAX-RS, the Java specification for RESTful web services. All of the "javax.ws.rs.*" classes/annotations come from JAX-RS
As JAX-RS, is just a specification, there needs to be something that implements the spec. There is probably a third-party, JAX-RS component that is used to run this service. Jersey in one popular implementation. Apache CXF is another.
Now back to JAX-RS. When you read up on this, you will see that the annotations on your class determine the REST characteristics of your service. For example,
#Path("subscription_tier")
defines your class as the resource with URI BASE_PATH/subscription_tier, where BASE_PATH is propbably defined in a configuration file for your web service framework.
As for how the objects are "automagically" converted into a JSON response: that is the role of the web service framework as well. It probably uses some kind of standard object-to-JSON mapping to accomplish this. (I have worked with CXF and XML resources. In that case JAXB was the mapping mechanism). This is a good thing, as the web service developer does not have to worry about this mapping, and can focus on coding just the implementation of service itself.

Getting a reference to a Jersey REST resource method from the URL

I'm doing some performance testing, and I want to be able to call a resource method without going through the network. I already have a framework for generating URLs, and I'd like to be able to reuse it.
For example, given the URL: www.example.com:8080/resource/method, I want to get a reference to the resource method that it calls, so that I can run it without making a network-level HTTP request. I.e., in the example below, I want to use the URL "www.frimastudio.com:8080/time" to get a reference to the method getServerTime() that I can then call directly.
Does Jersey (or something else?) provide a way to do this, or do I have to import the specific Resource class I want to call, instantiate it, etc.? Thanks in advance!
Yes jersey is RESTful API that allow routes configuration (only with annotations)
Example :
package com.frimastudio.webservice.controller.route;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.joda.time.DateTime;
import com.frimastudio.webservice.controller.representation.Time;
#Path("/time")
#Produces(MediaType.APPLICATION_JSON)
public class TimeResource
{
public TimeResource()
{
}
#GET
public Time getServerDate()
{
return new Time(new DateTime());
}
}
with Time being a Jackson representation :
package com.frimastudio.webservice.controller.representation;
import org.hibernate.validator.constraints.NotEmpty;
import org.joda.time.DateTime;
import com.fasterxml.jackson.annotation.JsonProperty;
public class Time
{
#NotEmpty
#JsonProperty
private String date;
public Time()
{
// Jackson deserialization
}
public Time(String date)
{
super();
this.date = date;
}
public Time(DateTime date)
{
super();
this.date = date.toString();
}
}
This doesn't seem to be possible, based on looking at the Jersey code. The lookup is performed by HttpMethodRule.Matcher, which is a private class used only to implement HttpMethodRule.accept.
Seems to me everything in accept up to if (s == MatchStatus.MATCH) { could be pulled into its own method and exposed to the user.

How do I request a subset of XMLElements using MOXy?

I have a RESTful service that needs to return only a few of the XmlElements if "selectors" are submitted with the request. The URL will take the form of:
/merchants/{merchantId}/profile?selectors=<field1|field2|....|fieldN>
The selectors are optional, and so far I have implemented the service for the full set of elements to be returned for {merchantId} without selectors specified. Now I'm trying to figure out how to add in this added functionality. I'm sure this is covered in documentation but I can't find where. Any RTFM pointers would be appreciated. Thanks.
EclipseLink JAXB (MOXy) does not currently offer a mechanism to selectively indicate which fields/properties are included on a per marshal operation. This sounds like an interesting use case. I would appreciate if you could enter this as an enhancement request using the following link:
https://bugs.eclipse.org/bugs/enter_bug.cgi?product=EclipseLink
Below is an example of how you could use a stateful XmlAdapter to implement this use case by exploiting the fact that a JAXB (JSR-222) will not marshal an element when the value is null (see: http://blog.bdoughan.com/2012/04/binding-to-json-xml-handling-null.html).
FieldAdapter
Since we are going to leverage stateful XmlAdapters we're going to need one per field. Since all our XmlAdapters will perform the same logic we can create a super class that the others can extend from.
package forum13094195;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class FieldAdapter<T> extends XmlAdapter<T, T> {
private boolean include;
public FieldAdapter() {
this.include = true;
}
public FieldAdapter(boolean include) {
this.include = include;
}
#Override
public T marshal(T value) throws Exception {
if(include) {
return value;
}
return null;
}
#Override
public T unmarshal(T value) throws Exception {
return value;
}
}
Field1Adapter
package forum13094195;
public class Field1Adapter extends FieldAdapter<String> {
public Field1Adapter() {}
public Field1Adapter(boolean include) {
super(include);
}
}
Field2Adapter
package forum13094195;
public class Field2Adapter extends FieldAdapter<Integer>{
public Field2Adapter() {}
public Field2Adapter(boolean include) {
super(include);
}
}
Field3Adapter
package forum13094195;
public class Field3Adapter extends FieldAdapter<String> {
public Field3Adapter() {}
public Field3Adapter(boolean include) {
super(include);
}
}
Merchant
The #XmlJavaTypeAdapter annotation is used to specify an XmlAdapter on a field/property.
package forum13094195;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Merchant {
#XmlJavaTypeAdapter(Field1Adapter.class)
String field1;
#XmlJavaTypeAdapter(Field2Adapter.class)
int field2;
#XmlJavaTypeAdapter(Field3Adapter.class)
String field3;
}
Demo
The demo code below demonstrates how to set a stateful XmlAdapter on the Marshaller.
package forum13094195;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Merchant.class);
Merchant merchant = new Merchant();
merchant.field1 = "A";
merchant.field2 = 2;
merchant.field3 = "C";
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(merchant, System.out);
marshaller.setAdapter(new Field1Adapter(false));
marshaller.setAdapter(new Field2Adapter(false));
marshaller.setAdapter(new Field3Adapter(true));
marshaller.marshal(merchant, System.out);
}
}
Output
Below is the output from running the demo code. By default the entire object is marshalled out. The second document marshalled does not contain the fields we excluded.
<?xml version="1.0" encoding="UTF-8"?>
<merchant>
<field1>A</field1>
<field2>2</field2>
<field3>C</field3>
</merchant>
<?xml version="1.0" encoding="UTF-8"?>
<merchant>
<field3>C</field3>
</merchant>
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
In EclipseLink 2.5.0 we released a new feature called Object Graphs that enables you to marshal/unmarshal a subset of mapped fields/properties.
// Create the Object Graph
ObjectGraph subset = JAXBHelper.getJAXBContext(jc).createObjectGraph(Merchant.class);
subset.addAttributeNodes("field1", "field1", "fieldN");
// Output XML - Based on Object Graph
marshaller.setProperty(MarshallerProperties.OBJECT_GRAPH, subset);
marshaller.marshal(customer, System.out);
For More Information
http://blog.bdoughan.com/2013/03/moxys-object-graphs-partial-models-on.html