I have to use an interface in my REST web service. Here is the Interface Specs.java :
#XmlJavaTypeAdapter(MyAdapter.class)
public interface Specs {
public BaseProperties getBaseProps();
public void setBaseProps(BaseProperties baseProps);
}
MyAdapter.java :
public class MyAdapter extends XmlAdapter<Object,Object>
{
public Object unmarshal(Object v)
{
return v;
}
public Object marshal(Object v)
{
return v;
}
}
RegSpecs.java
#XmlType
public class RegSpecs implements Specs{
private BaseProperties baseProps;
public BaseProperties getBaseProps()
{
return baseProps;
}
public void setBaseProps(BaseProperties baseProps)
{
this.baseProps = baseProps;
}
}
MapSpecs.java
#XmlType
public class MagSpecs implements Specs {
private BaseProperties baseProps;
private Features features;
public BaseProperties getBaseProps()
{
return baseProps;
}
public void setBaseProps(BaseProperties baseProps)
{
this.baseProps = baseProps;
}
public Features getFeatures() {
return features;
}
public void setFeatures(Features features) {
this.features = features;
}
}
Accessing this service throws the following error :
javax.xml.bind.MarshalException
- with linked exception:
[javax.xml.bind.JAXBException: class entities.MagSpecs nor any of its super class is known to this context.]
How to modify my context ? I am using JAXB bundled with Jersey 1.5
Thanks !
EDIT : In an attempt to update my context, I added this code to my client (resource) class :
public class BookService implements ContextResolver<JAXBContext>
{
private JAXBContext jaxbContext;
public BookService() {
try {
// Bootstrap your JAXBContext will all necessary classes
jaxbContext = JAXBContext.newInstance(Specs.class,MagSpecs.class, RegSpecs.class);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
public JAXBContext getContext(Class<?> clazz) {
if(BookService.class == clazz) {
return jaxbContext;
}
return null;
}
In this case I get error :
entities.Specs is an interface, and JAXB can't handle interfaces.
this problem is related to the following location:
at entities.Specs
entities.Specs does not have a no-arg default constructor.
this problem is related to the following location:
at entities.Specs
The client of the Specs interface needs to know that MagSpecs can be an instance of it so that it knows to look at it for tooling purposes. The easiest way of doing this is to put an #XmlSeeAlso annotation on the Specs interface:
#XmlSeeAlso({ MagSpecs.class, RegSpecs.class })
#XmlJavaTypeAdapter(MyAdapter.class) // Never needed this annotation myself...
public interface Specs {
public BaseProperties getBaseProps();
public void setBaseProps(BaseProperties baseProps);
}
In general, whenever I'm working with JAXB annotations I make sure I write plenty of tests to check that an XML schema can be generated from the classes in question, checking that from each (sane) entry point into the web of classes and interfaces I can generate a sensible schema without exceptions. For example (and I apologize for this being a bit long):
private SchemaOutputResolver sink;
StringWriter schema;
#Before
public void init() {
schema = new StringWriter();
sink = new SchemaOutputResolver() {
#Override
public Result createOutput(String namespaceUri,
String suggestedFileName) throws IOException {
StreamResult sr = new StreamResult(schema);
sr.setSystemId("/dev/null");
return sr;
}
};
Assert.assertTrue(schema.toString().isEmpty());
}
private void testJAXB(Class<?>... classes) throws Exception {
JAXBContext.newInstance(classes).generateSchema(sink);
Assert.assertTrue(schema.toString().length() > 0);
}
#Test
public void testJAXBForSpecs() throws Exception {
testJAXB(Specs.class);
}
[EDIT]: You also need to change the Specs interface into a class and have the current implementations inherit from it. It can be a fully abstract class if you want. As long as you're not putting serious functionality in the classes, it should work.
EclipseLink JAXB (MOXy) can map interfaces to XML (Note I'm the tech lead). You need to be sure to have a create method on the corresponding object factory to return a concrete impl:
http://bdoughan.blogspot.com/2010/07/moxy-jaxb-map-interfaces-to-xml.html
MOXy integrates cleaning into REST environments:
http://bdoughan.blogspot.com/2010/08/creating-restful-web-service-part-35.html
Related
We have used Google Guice framework for dependency injection. I need to create multiple insatnce of an interface in java.
The execution starts from here: KfhRecordValidator.java class in the below code:
public class KfhRecordValidator implements RequestHandler<Request, Response> {
public Response handleRequest(Request request, Context context)
{
// Resolve the necessary dependencies, and process the request.
Injector injector = Guice.createInjector(new
DependencyModule());
Processor processor =
injector.getInstance(Processor.class);
return processor.process(request, context);
}}
The process class is having reference of RecordValidationHelper class and the injection is through constructor.
IRecordValidationService.java is an interface that is having validate method.
public interface IRecordValidationService {
void validate(Record record) throws ValidationException;}
class processor is having one method called process that is being called in RecordValidationHelper class.
class Processor {
private final RecordValidationHelper recordValidationHelper;
#Inject
#SuppressWarnings({"WeakerAccess"})
public Processor(IRecordValidationService recordValidationService,
IRecordService<ErrorRecord> recordService,
S3UtilsInterface s3Utils, IEnvironmentVariableReader
environmentVariableReader) {
this.recordValidationHelper = new
RecordValidationHelper(recordValidationService);
this.errorRecordHelper = new
ErrorRecordHelper(recordService, environmentVariableReader);
}
public Response process(Request request, #SuppressWarnings("unused") Context context) {
// Validate records
List<LambdaRecord> records = recordValidationHelper.processRecords(request.getRecords());}
Class DependencyModule.java extneds AbstractModule class of Guice injection that is having configure method.
class DependencyModule extends AbstractModule {
#Override
protected void configure() {
String validationType = System.getenv("ValidationType");
validationType= validationType.toLowerCase(Locale.ENGLISH);
String valType[]= validationType.split(",");
int length= valType.length;
for(int i=0;i<length;i++){
switch(valType[i]){
case "json":
bind(IRecordValidationService.class).to(JsonValidationService.class);
break;
case "avro":
bind(IRecordValidationService.class).to(AvroSchemaValidationService.class);
break;
case "clientlogging":
bind(IRecordValidationService.class).to(ClientLoggingValidationService.class);
break;
case "servicelogs":
bind(IRecordValidationService.class).to(ServiceLoggingValidationService.class);
break;
default:
throw new UnsupportedOperationException(String.format("Encountered an unsupported ValidationType of '%s'.", valType[i]));
}
} } }
SO the issue is if I am getting validation type as AVRO, JSON then it will bind IRecordValidationService to respective JsonValidationService/AvroSchemaValidationService class. I need to create multiple instance for that but it supports only once instance at a time.
Below is the RecordValidationHelper.java class
public class RecordValidationHelper extends AbstractModule {
private final IRecordValidationService recordValidationService;
#Inject
public RecordValidationHelper(IRecordValidationService recordValidationService) {
this.recordValidationService = recordValidationService;
}
public List processRecords(List requestRecords) {
List records = new ArrayList<>();
for (RequestRecord record : requestRecords) {
try {
Record domainRecord = new Record();
domainRecord.setKey(record.getRecordId());
domainRecord.setValue(new String(Base64.getDecoder().decode(record.getData())));
// Use the injected logic to validate the record.
((IRecordValidationService)
recordValidationService).validate(domainRecord);}
catch (ValidationException ex) {}}}
return records;}
Anyone having any idea about how it should be implemented to get multiple instance suitable for this.
Use #Named bindings
In your DependencyModule, bind using names:
bind(IRecordValidationService.class)
.annotatedWith(Names.named("json"))
.to(JsonValidationService.class);
bind(IRecordValidationService.class)
.annotatedWith(Names.named("avro"))
.to(AvroSchemaValidationService.class);
bind(IRecordValidationService.class)
.annotatedWith(Names.named("clientlogging"))
.to(ClientLoggingValidationService.class);
bind(IRecordValidationService.class)
.annotatedWith(Names.named("servicelogs"))
.to(ServiceLoggingValidationService.class);
Then in your injectee:
#Inject
public RecordValidationHelper(
#Named("json") IRecordValidationService jsonValidation,
#Named("avro") IRecordValidationService avroValidation,
#Named("clientlogging") IRecordValidationService clientLoggingValidation,
#Named("servicelogs") IRecordValidationService serviceLogsValidation,
) {
this.jsonValidation = jsonValidation;
this.avroValidation = avroValidation;
this.clientLoggingValidation = clientLoggingValidation;
this.serviceLogsValidation = serviceLogsValidation;
}
See Guice's BindingAnnotation wiki page for more info.
I have a working "request factory" example and i want to refactor it, so that i can move the generic methods like "persist()" and "remove()" out of the domain object into a generic locator. Currently i have the following (working) code:
A generic super class that holds the id and the version for all domain objects:
#MappedSuperclass
public class EntityBase {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Version
#Column(name = "version")
private Integer version;
// setter & getter
}
A domain object. It has the persist() and remove()-methods, which i want to refactore out of the class:
#Entity
#Table(name = "article")
public class Article extends EntityBase{
public static Article findArticle(Long id) {
//find article
}
public void persist() {
// persist
}
public void remove() {
// remove
}
}
A proxy object for the domain object:
#ProxyFor(value = Article.class)
public interface ArticleProxy extends EntityProxy {
// some getter and setters
}
The request object for my domain object:
#Service(value = Article.class)
public interface ArticleRequest extends RequestContext {
Request<ArticleProxy> findArticle(Long id);
InstanceRequest<ArticleProxy, Void> persist();
InstanceRequest<ArticleProxy, Void> remove();
}
My request factory:
public interface MyRequestFactory extends RequestFactory {
ArticleRequest articleRequest();
}
---------------------------------------
Now my refactored code that is not working anymore:
I removed the persist() and remove()-method out of my domain object:
#Entity
#Table(name = "article")
public class Article extends EntityBase{
public static Article findArticle(Long id) {
//find article
}
}
I created my locator like this and added the methods "remove()" and "persist()" here (alongside the other default methods):
public class EntityLocator extends Locator<EntityBase, Long> {
#Override
public EntityBase create(Class<? extends EntityBase> clazz) {
try {
return clazz.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
#Override
public EntityBase find(Class<? extends EntityBase> clazz, Long id) {
return null;
}
#Override
public Class<EntityBase> getDomainType() {
return null;
}
#Override
public Long getId(EntityBase domainObject) {
return null;
}
#Override
public Class<Long> getIdType() {
return null;
}
#Override
public Object getVersion(EntityBase domainObject) {
return null;
}
public void persist(EntityBase domainObject){
// persist something
}
public void remove(EntityBase domainObject){
// remove
}
}
My proxy object is linked to the locator (locator=EntityLocator.class):
#ProxyFor(value = Article.class, locator=EntityLocator.class)
public interface ArticleProxy extends EntityProxy {
// getter and setters here
}
My new Request object looks like this. I made the "InstanceRequests" to "Requests", changed return types and parameter according to my new methods in the locator:
#Service(value = Article.class)
public interface ArticleRequest extends RequestContext {
Request<ArticleProxy> findArticle(Long id);
Request<Void> persist(ArticleProxy article);
Request<Void> remove(ArticleProxy article);
}
But now i get the error "Could not find domain method similar to java.lang.Void persist()" for the persist() and remove()-method. Why doesn't the lookup in the EntityLocator work? Do i need a ServiceLocator? I did not fully understand the google tutorial and the linked example is not available anymore.
I had the same question as you. The guide on GWTProject.org (http://www.gwtproject.org/doc/latest/DevGuideRequestFactory.html) is not very clear on how to correctly implement this, although it is written between the lines.
The following tutorial made the solution clear to me: http://cleancodematters.com/2011/06/04/tutorial-gwt-request-factory-part-i/
For me the use of the term DAO obfuscated things. I'm not going to use the DAO pattern. That's what my transparent persistence layer is for. However, the use of the Locator requires an extra class to put the persist, remove and findX methods in. They call it a Data Access Object (which it is, actually), I'd rather call it the Manager.
tl;dr
The methods you're trying to put in the Locator don't go there. You need an extra class (call it a DAO or a Manager).
Use the DAO/Manager as service in your RequestContext
I don't think you can place the persist and remove methods in the locator. The documentation doesn't suggest you can add arbitrary methods to the locator interface and reference them from the client. If you just want to avoid duplicating the persist and remove methods in every entity class then you can put them in your EntityBase class. I've done this and it works nicely.
If you also want to avoid repeating the functions in each of your request interfaces, you can make a generic base class Request like so:
#SkipInterfaceValidation
public interface BaseEntityRequest<P extends EntityProxy> extends RequestContext {
InstanceRequest<P, Void> persist();
InstanceRequest<P, Void> remove();
}
and use it like so:
public interface ArticleRequest extends BaseEntityRequest<ArticleRequest> {
...
}
Although it makes sense that persist() and remove() were in the Locator, so as the entity was completely agnostic about the persistence layer, this is not supported by current RF api. As consequence you have to deal with that adding those methods to your BaseEntity and figuring out a way to call the persist method in your locator.
I think you could open a gwt issue requiring this feature though.
Another way to avoid having certain methods in your entities, is to use ValueProxy insteadof EntityProxy, but in this case you have to provide methods to save/delete those objects from the client.
Your interface ArticleRequest isn't configured properly. You need correct it like this.
#Service(value = SentenceServiceImpl.class, locator = SpringServiceLocator.class)
public interface SentenceServiceRequest extends RequestContext {
Request<List<SentenceProxy>> getSentences();
Request<Void> saveOrUpdate(SentenceProxy sentence);
}
Locator:
public class SpringServiceLocator implements ServiceLocator {
public Object getInstance(Class<?> clazz) {
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(RequestFactoryServlet.getThreadLocalServletContext());
return context.getBean(clazz);
}
}
I'm having trouble where the afterUnmarshal() methods on my classes are not called if the class is a member of a collection.
Beyond declaring the method on a class which is created via unmarshalling, is there any other steps I'm required to perform? (I can't see anything else in the docs)
Here's a test which shows the problem I'm having:
Given these two domain classes:
#XmlRootElement(name="Parent")
public class Parent {
public boolean unmarshalCalled = false;
#XmlPath("Children/Child")
List<Child> children;
void afterUnmarshal(Unmarshaller u, Object parent)
{
unmarshalCalled = true;
}
}
#XmlAccessorType(XmlAccessType.FIELD)
public class Child {
public boolean unmarshalCalled = false;
#Getter #Setter
#XmlPath("#name")
private String name;
void afterUnmarshal(Unmarshaller u, Object parent)
{
unmarshalCalled = true;
}
}
This test fails:
public class UnmarshalTest {
#Test
#SneakyThrows
public void testUnmarshal()
{
String xml = "<Parent><Children><Child name='Jack' /><Child name='Jill' /></Children></Parent>";
JAXBContext context = getContext();
Parent parent = (Parent) context.createUnmarshaller().unmarshal(new StringReader(xml));
assertTrue(parent.unmarshalCalled);
for (Child child : parent.children)
{
assertThat(child.getName(),notNullValue());
assertTrue(child.unmarshalCalled); // This assertion fails
}
}
#SneakyThrows
public static JAXBContext getContext()
{
JAXBContext context;
context = org.eclipse.persistence.jaxb.JAXBContext.newInstance(Parent.class);
return context;
}
}
Is this a bug, or have I missed some steps to get this to work correctly?
The issue you are seeing is due to the following EclipseLink MOXy bug:
https://bugs.eclipse.org/364410
This bug has already been fixed in the EclipseLink 2.3.3 stream, a nightly download can be obtained from:
http://www.eclipse.org/eclipselink/downloads/nightly.php
Workaround
You can workaround the issue that you are seeing by ensuring that all of the classes with event methods are included in the array of classes passed in to create the JAXBContext. I have modified you code below to do this:
#SneakyThrows
public static JAXBContext getContext()
{
JAXBContext context;
context = org.eclipse.persistence.jaxb.JAXBContext.newInstance(Parent.class, Child.class);
return context;
}
I find using JAXB together with Guice possible, but challenging: Both libraries "fight" for control over object creation, you have to be careful to avoid cyclic dependencies, and it can get messy with all the JAXB Adapters and Guice Providers and stuff. My questions are:
How do you deal with this configuration? What general strategies / rules of thumb can be applied?
Can you point me to a good tutorial or well written sample code?
How to visualize the dependencies (including the Adapters and Providers)?
For some sample code, some example work was done here: http://jersey.576304.n2.nabble.com/Injecting-JAXBContextProvider-Contextprovider-lt-JAXBContext-gt-with-Guice-td5183058.html
At the line that says "Wrong?", put in the recommended line.
I looks like this:
#Provider
public class JAXBContextResolver implements ContextResolver<JAXBContext> {
private JAXBContext context;
private Class[] types = { UserBasic.class, UserBasicInformation.class };
public JAXBContextResolver() throws Exception {
this.context =
new JSONJAXBContext(
JSONConfiguration.natural().build(), types);
}
public JAXBContext getContext(Class<?> objectType) {
/*
for (Class type : types) {
if (type == objectType) {
return context;
}
} // There should be some kind of exception for the wrong type.
*/
return context;
}
}
//My resource method:
#GET
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public JAXBElement<UserBasic> get(#QueryParam("userName") String userName) {
ObjectFactory ob = new ObjectFactory();
UserDTO dto = getUserService().getByUsername(userName);
if(dto==null) throw new NotFoundException();
UserBasic ub = new UserBasic();
ub.setId(dto.getId());
ub.setEmailAddress(dto.getEmailAddress());
ub.setName(dto.getName());
ub.setPhoneNumber(dto.getPhoneNumber());
return ob.createUserBasic(ub);
}
//My Guice configuration module:
public class MyServletModule extends ServletModule {
public static Module[] getRequiredModules() {
return new Module[] {
new MyServletModule(),
new ServiceModule(),
new CaptchaModule()
};
}
#Override
protected void configureServlets() {
bind(UserHttpResource.class);
bind(JAXBContextResolver.class);
serve("/*").with(GuiceContainer.class);
}
}
Please consider the following example:
There is a ClassA and a ClassB which extends it. My problem is now that I have to unmarshall a ClassB from an xml file. Please note that ClassA can not be changed as it is not under my control.
Several problems are noted in this example:
The main problem is that ClassA does not have a default no-arg constructor which is required by JAXB without Adapter. Therefore I implemented MyAdapter which maps ClassB to the simple class ValB which can be processed by JAXB without any problems.
The main problem is how to make JAXB use this adapter? Neither defining the #XmlJavaTypeAdapter on class level nor registering the Adapter to the unmarshaller does it.
Does anybody know how to make JAXB use MyAdapter so that the unmarshaller returns an object that is an instance of ClassA?
public class JaxbTest {
public static abstract class ClassA {
public ClassA(String id) {
}
}
#XmlRootElement
#XmlJavaTypeAdapter(MyAdapter.class) // does not have an effect
public static class ClassB extends ClassA {
public String text;
public ClassB() {
super("");
}
}
public static class ValB {
public String text;
}
public static class MyAdapter extends XmlAdapter<ValB, ClassB> {
#Override
public ClassB unmarshal(ValB v) throws Exception {
ClassB b = new ClassB();
b.text = v.text;
return b;
}
#Override
public ValB marshal(ClassB v) throws Exception {
ValB b = new ValB();
b.text = v.text;
return b;
}
}
public static void main(String[] args) {
try {
JAXBContext context = JAXBContext.newInstance(ClassB.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.setAdapter(new MyAdapter()); // does not have an effect
ClassA a = (ClassA) unmarshaller.unmarshal(new File("test.xml"));
// do somthing with a
} catch (Exception e) {
e.printStackTrace();
}
}
}
BTW: Don't take the code too serious - it is just an example demonstrating the problem. I know that the definition of ClassA and ClassB are not really useful.
UPDATE
We have addressed this issue in the upcoming EclipseLink JAXB (MOXy) 2.2.0 release (see bug #332742). In this release abstract classes will not be checked for a no-arg constructor.
Pre-release versions with this fix can be obtained here starting December 18th:
http://www.eclipse.org/eclipselink/downloads/nightly.php
Workaround
This is what the #XmlTransient annotation is for. If possible do the following:
#XmlTransient
public static abstract class ClassA {
public ClassA(String id) {
}
}
If it is not possible to annotate ClassA directly, you could leverage an EclipseLink JAXB (MOXy) extension to do this. MOXy allows you to specify JAXB metadata as an XML file. This is useful when you can't modify a model class:
http://bdoughan.blogspot.com/2010/12/extending-jaxb-representing-annotations.html
Below are some articles explaining #XmlAdapter:
http://bdoughan.blogspot.com/2010/12/jaxb-and-immutable-objects.html
http://bdoughan.blogspot.com/2010/07/xmladapter-jaxbs-secret-weapon.html