Trying to run JUnit test but getting an ERROR: Implementing class - testing

I'm trying to run a simple test with following class (DataStructureTest) code:
public class DataStructureTest extends DatabaseTestCase {
private static final Log log = (Log) LogFactory.getLog(DataStructureTest.class);
#Test
public void testProductData(){
//Creating a product data
log.debug("Creating new product data");
ProductData productData = new ProductData("Öljy");
//Setting basic information of product
log.debug("Setting basic information like name, price, description "
+ "and amount.");
productData.setProductPrice(4.13);
productData.setProductDescription("Öljy rekoille.");
productData.setProductAmount(20);
//Saving entity
EntityManager em = getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
em.persist(productData);
Integer primarykey = productData.getId();
tx.commit();
//Reading entity from database
log.debug("Reading entity data from database.");
tx = em.getTransaction();
tx.begin();
productData = em.find(ProductData.class, primarykey);
tx.commit();
//Asserting that the product data was correct in database
Assert.assertEquals("Product name was correct", "Öljy",
productData.getProductName());
Assert.assertEquals("Product price was correct", 4.13,
productData.getProductPrice());
Assert.assertEquals("Product description was correct", "Öljy rekoille",
productData.getProductDescription());
Assert.assertEquals("Product amount was incorrect", 21,
productData.getProductAmount());
}
}
DatabaseTestCase class code is following:
public class DatabaseTestCase {
/**
* This method establishes Entity Managerin before every test
* and writes information into log.
*/
private static final Log log = LogFactory.getLog(DatabaseTestCase.class);
private EntityManager entityManager;
private EntityManagerFactory entityManagerFactory;
#Before
public void establishEntityManager(){
log.debug("Establishing database connection!");
entityManagerFactory = Persistence.createEntityManagerFactory("warehouseTestPersistence", null);
entityManager = entityManagerFactory.createEntityManager();
}
#After
public void closeEntityManager(){
if(entityManager != null){
entityManager.close();
} else {
log.warn("Entity was empty (null) in tests.");
}
}
public EntityManager getEntityManager(){
return entityManager;
}
/**
* #param entityManager the entityManager to set
*/
public void setEntityManager(final EntityManager entityManager) {
this.entityManager = entityManager;
}
/**
* #return the entityManagerFactory
*/
public EntityManagerFactory getEntityManagerFactory() {
return entityManagerFactory;
}
/**
* #param entityManagerFactory the entityManagerFactory to set
*/
public void setEntityManagerFactory(final EntityManagerFactory entityManagerFactory) {
this.entityManagerFactory = entityManagerFactory;
}
/**
* Save entity to database. Used for assisting in tests.
* #return primary key for specific entity.
*/
protected Integer saveEntity(Entityclass entity){
EntityManager em = getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
em.persist(entity);
tx.commit();
return entity.getId();
}
/**
* Load entity from save location.
* #param <T>
* Generic class.
* #param entityclass
* Entity class of entity that is going to be loaded.
* #param primarykey
* Entity class primary key.
* #return correspondant entity.
*/
protected <T extends Entityclass> T loadEntity(final Class<T> entitysclass, final Integer primarykey){
EntityManager em = getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
T entity = em.find(entitysclass, primarykey);
tx.commit();
return entity;
}
}
I use NetBeans 7.0.1. All imports for both class are done properly. I have also added all required dependencies. But whenever I run a test I get following result:
-- error field --
No test passed, 1 test caused an error.(0,075 s)
com.mysite.warehouseapp.DataStructureTest FAILED
testProductData caused an ERROR: Implementing class
Implementing class
java.lang.IncompatibleClassChangeError
at....
at com.mysite.warehouseapp.test.DatabaseTestCase.establishEntityManager(DatabaseTestCase.java:36)
-- test result field --
Establishing database connection!
Entity was empty (null) in tests.
if you see the test result image at top I don't know how it got there but it supposed to be in this location.
So briefly said whenever I try to run the test there's no entity. Can anybody tell me the reason? I have tried to find solution for this problem for four days but still with no luck. Any help is appreciated.
Thanks

I found the solution my ownself, which is quiet rewarding :). Anyway the solution for this problem was that the persistence.xml was in wrong folder. And for newbies, I would advice you to put persistence.xml under following folder in your project src/main/resources/META-INF/persistence.xml.
If you don't have resources/META-INF folder created under src/test, then create one and place your persistence.xml file there. I hope this helps somebody :)
Another thing which I found out was that you might have different jars that are "against" each other and thus you need to take out those jars then it will work.

Related

BinaryInvalidTypeException in Ignite Remote Filter

The following code is based on a combination of Ingite's CacheQueryExample and CacheContinuousQueryExample.
The code starts a fat Ignite client. Three organizations are created in the cache and we are listening to the updates to the cache. The remote filter is set to trigger the continuous query if the organization name is "Google". Peer class loading is enabled by the default examples xml config file (example-ignite.xml), so the expectation is that the remote node is aware of the Organization class.
However the following exceptions are shown in the Ignite server's console (one for each cache entry) and all three records are returned to the client in the continuous query's event handler instead of just the "Google" record. If the filter is changed to check on the key instead of the value, the correct behavior is observed and a single record is returned to the local listener.
[08:28:43,302][SEVERE][sys-stripe-1-#2][query] CacheEntryEventFilter failed: class o.a.i.binary.BinaryInvalidTypeException: o.a.i.examples.model.Organization
[08:28:51,819][SEVERE][sys-stripe-2-#3][query] CacheEntryEventFilter failed: class o.a.i.binary.BinaryInvalidTypeException: o.a.i.examples.model.Organization
[08:28:52,692][SEVERE][sys-stripe-3-#4][query] CacheEntryEventFilter failed: class o.a.i.binary.BinaryInvalidTypeException: o.a.i.examples.model.Organization
To run the code
Start an ignite server using examples/config/example-ignite.xml as the configuration file.
Replace the content of ignite's CacheContinuousQueryExample.java with the following code. You may have to change the path to the configuration file to an absolute path.
package org.apache.ignite.examples.datagrid;
import javax.cache.Cache;
import javax.cache.configuration.Factory;
import javax.cache.event.CacheEntryEvent;
import javax.cache.event.CacheEntryEventFilter;
import javax.cache.event.CacheEntryUpdatedListener;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.Ignition;
import org.apache.ignite.cache.CacheMode;
import org.apache.ignite.cache.affinity.AffinityKey;
import org.apache.ignite.cache.query.ContinuousQuery;
import org.apache.ignite.cache.query.QueryCursor;
import org.apache.ignite.cache.query.ScanQuery;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.examples.ExampleNodeStartup;
import org.apache.ignite.examples.model.Organization;
import org.apache.ignite.examples.model.Person;
import org.apache.ignite.lang.IgniteBiPredicate;
import java.util.Collection;
/**
* This examples demonstrates continuous query API.
* <p>
* Remote nodes should always be started with special configuration file which
* enables P2P class loading: {#code 'ignite.{sh|bat} examples/config/example-ignite.xml'}.
* <p>
* Alternatively you can run {#link ExampleNodeStartup} in another JVM which will
* start node with {#code examples/config/example-ignite.xml} configuration.
*/
public class CacheContinuousQueryExample {
/** Organizations cache name. */
private static final String ORG_CACHE = CacheQueryExample.class.getSimpleName() + "Organizations";
/**
* Executes example.
*
* #param args Command line arguments, none required.
* #throws Exception If example execution failed.
*/
public static void main(String[] args) throws Exception {
Ignition.setClientMode(true);
try (Ignite ignite = Ignition.start("examples/config/example-ignite.xml")) {
System.out.println();
System.out.println(">>> Cache continuous query example started.");
CacheConfiguration<Long, Organization> orgCacheCfg = new CacheConfiguration<>(ORG_CACHE);
orgCacheCfg.setCacheMode(CacheMode.PARTITIONED); // Default.
orgCacheCfg.setIndexedTypes(Long.class, Organization.class);
// Auto-close cache at the end of the example.
try {
ignite.getOrCreateCache(orgCacheCfg);
// Create new continuous query.
ContinuousQuery<Long, Organization> qry = new ContinuousQuery<>();
// Callback that is called locally when update notifications are received.
qry.setLocalListener(new CacheEntryUpdatedListener<Long, Organization>() {
#Override public void onUpdated(Iterable<CacheEntryEvent<? extends Long, ? extends Organization>> evts) {
for (CacheEntryEvent<? extends Long, ? extends Organization> e : evts)
System.out.println("Updated entry [key=" + e.getKey() + ", val=" + e.getValue() + ']');
}
});
// This filter will be evaluated remotely on all nodes.
// Entry that pass this filter will be sent to the caller.
qry.setRemoteFilterFactory(new Factory<CacheEntryEventFilter<Long, Organization>>() {
#Override public CacheEntryEventFilter<Long, Organization> create() {
return new CacheEntryEventFilter<Long, Organization>() {
#Override public boolean evaluate(CacheEntryEvent<? extends Long, ? extends Organization> e) {
//return e.getKey() == 3;
return e.getValue().name().equals("Google");
}
};
}
});
ignite.getOrCreateCache(ORG_CACHE).query(qry);
// Populate caches.
initialize();
Thread.sleep(2000);
}
finally {
// Distributed cache could be removed from cluster only by #destroyCache() call.
ignite.destroyCache(ORG_CACHE);
}
}
}
/**
* Populate cache with test data.
*/
private static void initialize() {
IgniteCache<Long, Organization> orgCache = Ignition.ignite().cache(ORG_CACHE);
// Clear cache before running the example.
orgCache.clear();
// Organizations.
Organization org1 = new Organization("ApacheIgnite");
Organization org2 = new Organization("Apple");
Organization org3 = new Organization("Google");
orgCache.put(org1.id(), org1);
orgCache.put(org2.id(), org2);
orgCache.put(org3.id(), org3);
}
}
Here is an interim workaround that involves using and deserializing binary objects. Hopefully, someone can post a proper solution.
Here is the main() function modified to work with BinaryObjects instead of the Organization object:
public static void main(String[] args) throws Exception {
Ignition.setClientMode(true);
try (Ignite ignite = Ignition.start("examples/config/example-ignite.xml")) {
System.out.println();
System.out.println(">>> Cache continuous query example started.");
CacheConfiguration<Long, Organization> orgCacheCfg = new CacheConfiguration<>(ORG_CACHE);
orgCacheCfg.setCacheMode(CacheMode.PARTITIONED); // Default.
orgCacheCfg.setIndexedTypes(Long.class, Organization.class);
// Auto-close cache at the end of the example.
try {
ignite.getOrCreateCache(orgCacheCfg);
// Create new continuous query.
ContinuousQuery<Long, BinaryObject> qry = new ContinuousQuery<>();
// Callback that is called locally when update notifications are received.
qry.setLocalListener(new CacheEntryUpdatedListener<Long, BinaryObject>() {
#Override public void onUpdated(Iterable<CacheEntryEvent<? extends Long, ? extends BinaryObject>> evts) {
for (CacheEntryEvent<? extends Long, ? extends BinaryObject> e : evts) {
Organization org = e.getValue().deserialize();
System.out.println("Updated entry [key=" + e.getKey() + ", val=" + org + ']');
}
}
});
// This filter will be evaluated remotely on all nodes.
// Entry that pass this filter will be sent to the caller.
qry.setRemoteFilterFactory(new Factory<CacheEntryEventFilter<Long, BinaryObject>>() {
#Override public CacheEntryEventFilter<Long, BinaryObject> create() {
return new CacheEntryEventFilter<Long, BinaryObject>() {
#Override public boolean evaluate(CacheEntryEvent<? extends Long, ? extends BinaryObject> e) {
//return e.getKey() == 3;
//return e.getValue().name().equals("Google");
return e.getValue().field("name").equals("Google");
}
};
}
});
ignite.getOrCreateCache(ORG_CACHE).withKeepBinary().query(qry);
// Populate caches.
initialize();
Thread.sleep(2000);
}
finally {
// Distributed cache could be removed from cluster only by #destroyCache() call.
ignite.destroyCache(ORG_CACHE);
}
}
}
Peer class loading is enabled ... so the expectation is that the remote node is aware of the Organization class.
This is the problem. You can't peer class load "model" objects, i.e., objects used to create the table.
Two solutions:
Deploy the model class(es) to the server ahead of time. The rest of the code -- the filters -- can be peer class loaded
As #rgb1380 demonstrates, you can use BinaryObjects, which is the underlying data format
Another small point, to use "autoclose" you need to structure your code like this:
// Auto-close cache at the end of the example.
try (var cache = ignite.getOrCreateCache(orgCacheCfg)) {
// do stuff
}

Spring data rest i18n domain exceptions

I have a sdr project where I do some basic validation in entity setters and throw a domain exception if model is invalid. I can not get a message source inside the exception so that I can localize the business exception message. Custom exception class I have tried is:
#ResponseStatus(org.springframework.http.HttpStatus.CONFLICT)
public class DoublePriceException extends Exception {
#Autowired
static ReloadableResourceBundleMessageSource messageSource;
private static final long serialVersionUID = 1L;
public DoublePriceException(OrderItem orderItem) {
super(String.format(
messageSource.getMessage("exception.doublePricedItem", null, LocaleContextHolder.getLocale()),
orderItem.name));
}
}
And how I try to throw this mofo is:
public void setPrices(List<Price> prices) throws DoublePriceException {
for (Price price : prices) {
List<Price> itemsPrices = prices.stream().filter(it -> price.item.equals(it.item)).collect(Collectors.toList());
if(itemsPrices.size() > 1)
throw new DoublePriceException(itemsPrices.get(0).item);
}
this.prices = prices;
}
messageSource is always null. Is what I am trying not achievable?
DoublePriceException is obviously not a Spring managed Bean so that is not going to work.
You can register a Spring ControllerAdvice in your application that handles the exception and generates a suitable response.
/**
* Spring MVC #link {#link ControllerAdvice} which
* is applied to all Controllers and which will handle
* conversion of exceptions to an appropriate JSON response.
*/
#ControllerAdvice
public class ErrorHandlingAdvice
{
/**
* Handles a #DoublePriceException
*
* #param ex the DoublePriceException
*
* #return JSON String with the error details.
*/
#ExceptionHandler(DoublePriceException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public Object processValidationError(DoublePriceException ex)
{
//return suitable representation of the error message
//e.g. return Collections.singletonMap("error", "my error message");
}
}
Placing the above in a package scanned by the Spring framework should be enough to have it detected and applied.
Best I could come up with is catching the HttpMessageNotReadableException and calling getMostSpecificCause() like the following:
#RestControllerAdvice
public class ExceptionHandlingAdvice {
#Autowired
private MessageSource messageSource;
#ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity<Object> onException(HttpMessageNotReadableException ex, WebRequest request) {
Locale locale = request.getLocale();
Throwable cause = ex.getMostSpecificCause();
String message = cause.getMessage();
if (cause instanceof MultiplePriceException) {
message = messageSource.getMessage("exception.multiple.price",
new Object[] { ((MultiplePriceException) cause).orderItem.name }, locale);
}
return new ResponseEntity(Collections.singletonMap("error", message), new HttpHeaders(),
HttpStatus.BAD_REQUEST);
}
}

Hibernate Search with AnalyzerDiscriminator - Analyzer called only when creating Entity?

can you help me?
I am implementing Hibernate Search, to retrieve results for a global search on a localized website (portuguese and english content)
To do this, I have followed the steps indicated on the Hibernate Search docs:
http://docs.jboss.org/hibernate/search/4.5/reference/en-US/html_single/#d0e4141
Along with the specific configuration in the entity itself, I have implemented a "LanguageDiscriminator" class, following the instructions in this doc.
Because I am not getting exactly the results I was expecting (e.g. my entity has the text "Capuchinho" stored, but when I search for "capucho" I get no hits), I have decided to try and debug the execution, and try to understand if the Analyzers which I have configured are being used at all.
When creating a new record for the entity in the database, I can see that the "getAnalyzerDefinitionName()" method from the "LanguageDiscriminator" gets called. Great. But the same does not happen when I execute a search. Can anyone explain me why?
I am posting the key parts of my code below. Thanks a lot for any feedback!
This is one entity I want to index
#Entity
#Table(name="NEWS_HEADER")
#Indexed
#AnalyzerDefs({
#AnalyzerDef(name = "en",
tokenizer = #TokenizerDef(factory = StandardTokenizerFactory.class),
filters = {
#TokenFilterDef(factory = LowerCaseFilterFactory.class),
#TokenFilterDef(factory = SnowballPorterFilterFactory.class,
params = {#Parameter(name="language", value="English")}
)
}
),
#AnalyzerDef(name = "pt",
tokenizer = #TokenizerDef(factory = StandardTokenizerFactory.class),
filters = {
#TokenFilterDef(factory = LowerCaseFilterFactory.class),
#TokenFilterDef(factory = SnowballPorterFilterFactory.class,
params = {#Parameter(name="language", value="Portuguese")}
)
}
)
})
public class NewsHeader implements Serializable {
static final long serialVersionUID = 20140301L;
private int id;
private String articleHeader;
private String language;
private Set<NewsParagraph> paragraphs = new HashSet<NewsParagraph>();
/**
* #return the id
*/
#Id
#Column(name="ID")
#GeneratedValue(strategy=GenerationType.AUTO)
#DocumentId
public int getId() {
return id;
}
/**
* #param id the id to set
*/
public void setId(int id) {
this.id = id;
}
/**
* #return the articleHeader
*/
#Column(name="ARTICLE_HEADER")
#Field(index=Index.YES, store=Store.NO)
public String getArticleHeader() {
return articleHeader;
}
/**
* #param articleHeader the articleHeader to set
*/
public void setArticleHeader(String articleHeader) {
this.articleHeader = articleHeader;
}
/**
* #return the language
*/
#Column(name="LANGUAGE")
#Field
#AnalyzerDiscriminator(impl=LanguageDiscriminator.class)
public String getLanguage() {
return language;
}
...
}
This is my LanguageDiscriminator class
public class LanguageDiscriminator implements Discriminator {
#Override
public String getAnalyzerDefinitionName(Object value, Object entity, String field) {
String result = null;
if (value != null) {
result = (String) value;
}
return result;
}
}
This is my search method present in my SearchDAO
public List<NewsHeader> searchParagraph(String patternStr) {
Session session = null;
Transaction tx;
List<NewsHeader> result = null;
try {
session = sessionFactory.getCurrentSession();
FullTextSession fullTextSession = Search.getFullTextSession(session);
tx = fullTextSession.beginTransaction();
// Create native Lucene query using the query DSL
QueryBuilder queryBuilder = fullTextSession.getSearchFactory()
.buildQueryBuilder().forEntity(NewsHeader.class).get();
org.apache.lucene.search.Query luceneSearchQuery = queryBuilder
.keyword()
.onFields("articleHeader", "paragraphs.content")
.matching(patternStr)
.createQuery();
// Wrap Lucene query in a org.hibernate.Query
org.hibernate.Query hibernateQuery =
fullTextSession.createFullTextQuery(luceneSearchQuery, NewsHeader.class, NewsParagraph.class);
// Execute search
result = hibernateQuery.list();
} catch (Exception xcp) {
logger.error(xcp);
} finally {
if ((session != null) && (session.isOpen())) {
session.close();
}
}
return result;
}
When creating a new record for the entity in the database, I can see that the "getAnalyzerDefinitionName()" method from the "LanguageDiscriminator" gets called. Great. But the same does not happen when I execute a search. Can anyone explain me why?
The selection of the analyzer is dependent on the state of a given entity, in your case NewsHeader. You are dealing with entity instances during indexing. While querying you don't have entities to start with, you are searching for them. Which analyzer would you Hibernate Search to select for your query?
That said, I think there is a shortcoming in the DSL. It does not allow you to explicitly specify the analyzer for a class. There is ignoreAnalyzer, but that's not what you want. I guess you could create a feature request in the Search issue tracker - https://hibernate.atlassian.net/browse/HSEARCH.
In the mean time you can build the query using the native Lucene query API. However, you will need to know which language you are targeting with your query (for example via the preferred language of the logged in user or whatever). This will depend on your use case. It might be you are looking at the wrong feature to start with.

JUnit Test against an interface without having the implementation yet

I try to write a test for a given interface like that with JUnit and have no idea how to do that:
public interface ShortMessageService {
/**
* Creates a message. A message is related to a topic
* Creates a date for the message
* #throws IllegalArgumentException, if the message is longer then 255 characters.
* #throws IllegalArgumentException, if the message ist shorter then 10 characters.
* #throws IllegalArgumentException, if the user doesn't exist
* #throws IllegalArgumentException, if the topic doesn't exist
* #throws NullPointerException, if one argument is null.
* #param userName
* #param message
* #return ID of the new created message
*/
Long createMessage(String userName, String message, String topic);
[...]
}
I tried to mock the interface after I realized that it doesn't make sense at all so I am a bit lost. Maybe someone can give me a good approach I can work with. I also heard about junit parameterized tests but I am not sure if that is what I am looking for.
Many thanks!
I use the following pattern to write abstract tests against my interface APIs without having any implementations available. You can write whatever tests you require in AbstractShortMessageServiceTest without having to implement them at that point in time.
public abstract class AbstractShortMessageServiceTest
{
/**
* #return A new empty instance of an implementation of FooManager.
*/
protected abstract ShortMessageService getNewShortMessageService();
private ShortMessageService testService;
#Before
public void setUp() throws Exception
{
testService = getNewShortMessageService();
}
#Test
public void testFooBar() throws Exception
{
assertEquals("question", testService.createMessage(
"DeepThought", "42", "everything"));
}
}
When you have an implementation, you can use the test simply by defining a new test class that overrides AbstractShortMessageServiceTest and implements the getNewShortMessageService method.
public class MyShortMessageServiceTest extends AbstractShortMessageServiceTest
{
protected ShortMessageService getNewShortMessageService()
{
return new MyShortMessageService();
}
}
In addition, if you need the test to be parameterized, you can do that in AbstractShortMessageServiceTest without doing it in each of the concrete tests.
Usually test is prepared for class that implements the interface and mocks are used for cooperating classes but you can test your test by mock if the class is not ready yet. It is unusual and you should use thenAnsfer with implemented logic of possible cases:
Better way is simply prepare tests for the implementation class and start to improve it till all test passes:
Implementing class can be in field and initialized before tests
private ShortMessageService testedClasOrMock;
//version with implementing class
#Before
public void setUp(){
testedClasOrMock = new ShortMessageServiceImpl0();
}
#Before
public void setUp(){
testedClasOrMock = mock(ShortMessageService.class);
when(testedClasOrMock).thenAnswer(new Answer<Long>(){
#Override
public Long answer(InvocationOnMock invocation) throws Throwable {
String message =(String) invocation.getArguments()[1];
if (message.length() > 256){
throw new IllegalArgumentException("msg is too long");
}
//other exception throwing cases
…...
return new Long(44);
}});
}
so you will have several test with expected exceptions like
#Test (expected= IllegalArgumentException.class)
public void testTooLongMsg(){
testedClasOrMock.createMessage(USER, TOO_LONG_MSG, TOPIC);
}
and one that simply should not throw exception and for instance check that msg ids are different
#Test
public void testTooLongMsg(){
long id0 = testedClasOrMock.createMessage(USER, TOO_LONG_MSG, TOPIC);
long id1 = testedClasOrMock.createMessage(USER, TOO_LONG_MSG, TOPIC);
assertTrue(id0 != id1);
}
If you insist on testing your test by mock let me know and I will add example for one test case.

Why doesn't #JsonUnwrapped work for Lists?

I am using Jackson 2.1.0. Given:
public static final class GetCompanies
{
private final List<URI> companies;
/**
* Creates a new GetCompanies.
* <p/>
* #param companies the list of available companies
* #throws NullPointerException if companies is null
*/
#JsonCreator
public GetCompanies(#JsonUnwrapped #NotNull List<URI> companies)
{
Preconditions.checkNotNull(companies, "companies");
this.companies = ImmutableList.copyOf(companies);
}
/**
* #return the list of available companies
*/
#JsonUnwrapped
#SuppressWarnings("ReturnOfCollectionOrArrayField")
public List<URI> getCompanies()
{
return companies;
}
}
When the input list contains http://test.com/, Jackson generates:
{"companies":["http://test.com/"]}
instead of:
["http://test.com/"]
Any ideas?
UPDATE: See https://github.com/FasterXML/jackson-core/issues/41 for a related discussion.
In this case, if this was to work, you'd end up trying to produce following:
{ "http://test.com" }
which is not legal JSON. #JsonUnwrapped really just removes one layer of wrapping. And although it theoretically could be made to work for "arrays in arrays" case, it does not.
And in fact I wonder if adding this feature was a mistake: mostly because it encourages use that is often against data-binding best practices (simplicity, one-to-one mapping).
But what would work instead is #JsonValue:
#JsonValue
private final List<URI> companies;
which means "use value of this property instead of serializing the object that contains it".
And the creator method would actually work as-is, no need for either #JsonUnwrapped or #JsonProperty.
Here is the corrected code:
public static final class GetCompanies
{
private final List<URI> companies;
/**
* Creates a new GetCompanies.
* <p/>
* #param companies the list of available companies
* #throws NullPointerException if companies is null
*/
#JsonCreator
public GetCompanies(#NotNull List<URI> companies)
{
Preconditions.checkNotNull(companies, "companies");
this.companies = ImmutableList.copyOf(companies);
}
/**
* #return the list of available companies
*/
#JsonValue
#SuppressWarnings("ReturnOfCollectionOrArrayField")
public List<URI> getCompanies()
{
return companies;
}
}