I'm new to Java Web Services and I'm struggling with a basic problem.
After finding a bunch of outdated examples I managed to get something working with XML however the same code wont work when I ask it to return JSON.
Initially I thought it was a missing JSON formatter but JAXB should be taking care of the conversion from POJO to JSON so I don't believe that's the problem.
The error being thrown within Tomcat is:
javax.servlet.ServletException: org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyWriter not found for media type=application/json, type=class resttest.model.Todo, genericType=class resttest.model.Todo
WEB.XML
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>testtest</display-name>
<servlet>
<servlet-name>Jersey REST Service</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>resttest.jaxb;resttest.model</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
Todo.java
package resttest.model;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Todo {
public Todo(){};
private String summary;
private String description;
public String getSummary() {
return summary;
}
public void setSummary(String summary) {
this.summary = summary;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
TodoResource.Java
package resttest.jaxb;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import resttest.model.Todo;
#Path("/todo")
public class TodoResource {
#GET
#Produces("application/json")
public Todo getTodo() {
Todo todo = new Todo();
todo.setSummary("This is my first todo");
todo.setDescription("This is my first todo");
return todo;
}
}
Any ideas why the JSON isn't being returned and the error thrown?
I searched a lot of the responses myself and this is what I ended up doing. In addition to your TodoResource class, you need a class that extends Application, and class such as the MOXyJsonContextResolver class below to implement the ContextResolver interface. These help define the Jersey context along with a selected Json converter and optional customizations to the Json output. Put the classes in the same package as your resource class and Jersey will find it. MOXy is now the default for Jersey (I use 2.5.1) and the only json converter that I could get working without receiving your error. Also, make sure you have the MOXy jar included in your build or maven pom.xml (jersey-media-moxy-2.5.1.jar).
Note: nothing is in my application's web.xml. That was in the older documentation examples and not needed as of Jersey 2.5.1.
#javax.ws.rs.ApplicationPath("webresources")
public class ApplicationConfig extends Application {
public ApplicationConfig() {
this.initMethods();
}
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new java.util.HashSet<Class<?>>();
addRestResourceClasses(resources);
return resources;
}
private void initMethods() {
try {
...some classes you might need instantiated, etc, for your resource class
} catch (Exception e) {
e.printStackTrace();
}
}
private void addRestResourceClasses(Set<Class<?>> resources) {
resources.add(MOXyJsonContextResolver.class);
}
}
And here is the MOXyJsonContextResolver.class that I used to customize the Json response:
public class MOXyJsonContextResolver implements ContextResolver<MoxyJsonConfig> {
private final MoxyJsonConfig config;
public MOXyJsonContextResolver() {
config = new MoxyJsonConfig()
.setAttributePrefix("")
.setValueWrapper("value")
.property(JAXBContextProperties.JSON_WRAPPER_AS_ARRAY_NAME, true);
}
#Override
public MoxyJsonConfig getContext(Class<?> objectType) {
return config;
}
}
You forgot to add the attribute: #XmlAccessorType(XmlAccessType.FIELD)
Example:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Todo {
...
You have mentioned #XmlRootElement at class level in todo class. #XmlRootElement is only required if you want to produce your response in xml format, and also provide #Path at method level in TodoResource class, its a good practice.
mention #Produces(MediaType.APPLICATION_JSON) at method level. Hope this will work for you!
Related
I'm trying to run my LiquibaseProducer in WildFly 9.0.2. This is my Producer class:
package nl.itris.mjop.database;
import liquibase.integration.cdi.*;
import liquibase.integration.cdi.annotations.*;
import liquibase.resource.*;
import **********
#Singleton
#Startup
#Dependent
public class LiquibaseProcducer {
#Resource(lookup="java:jboss/datasources/PostgresDS")
private static DataSource myDataSource;
#Produces #LiquibaseType
public CDILiquibaseConfig createConfig() {
System.out.println("============= liquibase createConfig entry =============");
CDILiquibaseConfig config = new CDILiquibaseConfig();
config.setChangeLog("liquibase/parser/core/xml/simpleChangeLog.xml");
System.out.println("============= liquibase createConfig exit =============");
return config;
}
#Produces #LiquibaseType
public DataSource createDataSource() throws SQLException {
System.out.println("============= liquibase createDataSource entry =============");
return getDataSource();
}
#Produces #LiquibaseType
public ResourceAccessor create() {
System.out.println("============= liquibase create entry =============");
return new ClassLoaderResourceAccessor(getClass().getClassLoader());
}
public static DataSource getDataSource() {
if (myDataSource == null) {
/* Workaround for failing #Resource(lookup="java:jboss/datasources/PostgresDS") */
try {
System.out.println("============= liquibase datasource lookup via initial context workaround =============");
InitialContext ctx = new InitialContext();
myDataSource = (DataSource) ctx.lookup("java:jboss/datasources/PostgresDS");
} catch(NamingException ne) {
ne.printStackTrace();
}
}
return myDataSource;
}
}
When I (re) deploy my WAR file in WildFly, this producer is not doing anything! I do not see any error messages in my log files, I do not see any system.out messages that I put in the producer methods.
The producer class seems to be loaded and recognized by WildFly hence this log fragment:
2016-11-30 10:58:53,662 INFO [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service thread 1-7) JNDI bindings for session bean named LiquibaseProcducer in deployment unit deployment "mjop-elements.war" are as follows:
java:global/mjop-elements/LiquibaseProcducer!nl.itris.mjop.database.LiquibaseProcducer
java:app/mjop-elements/LiquibaseProcducer!nl.itris.mjop.database.LiquibaseProcducer
java:module/LiquibaseProcducer!nl.itris.mjop.database.LiquibaseProcducer
java:global/mjop-elements/LiquibaseProcducer
java:app/mjop-elements/LiquibaseProcducer
java:module/LiquibaseProcducer
I'm out of options. Why is my producer not executed? What am I doing wrong?
Any help is appreciated!
My beans.xml is like this:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
version="1.1" bean-discovery-mode="all">
</beans>
I have an EAR file with the following structure:
lib
jar.jar
Test1
Test2
ejb.jar
Test1Impl
war.war
Test2Impl
TestServlet
The jar.jar contains two interfaces
Test1
Test2
The TestServlet injects Test1 which resolves to Test1Impl only if I have a manifest Class-Path entry in war.war to ejb.jar.
Test1Impl injects Test2 which resolves to Test2Impl only if I have a manifest Class-Path entry in ejb.jar to war.war.
The tip entry Matching the classloader structure for the deployment of the Weld documentation explains why I need the manifest entries.
How is this cross BDA injection supposed to work normally? Adding Class-Path manifest entries seems a bit stupid because actually I don't want the implementations to be visible. I only want that the beans from other subdeployments to be visible. Is there any way to do that?
Here the implementations
public class Test1Impl implements Test1 {
#Inject
private Test2 test2;
public void hello() {
System.out.println(test2.getString());
}
}
public class Test2Impl implements Test2 {
public String getString() {
return "Hello";
}
}
#WebServlet(urlPatterns = "/test")
public class TestServlet implements Servlet {
#Inject
private Test1 test;
public void init(ServletConfig config) throws ServletException {
}
public ServletConfig getServletConfig() {
return null;
}
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
test.hello();
}
public String getServletInfo() {
return null;
}
public void destroy() {
}
}
And here the application.xml
<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/application_7.xsd"
version="7">
<description>The EAR</description>
<display-name>ear</display-name>
<module>
<ejb>ejb.jar</ejb>
</module>
<module>
<web>
<web-uri>war.war</web-uri>
<context-root>/</context-root>
</web>
</module>
<library-directory>lib</library-directory>
</application>
As described in the CDI reference in the section Using CDI Beans from outside the deployment a jboss-deployment-structure.xml with the appropriate dependencies is required.
Although doing this fixed my problems, I think that the CDI spec should define a portable way for doing this for enterprise applications.
I am using FullAjaxExceptionHandler to handle timeout issue in ajax request. The problem that I am facing is to handle javax.faces.view.facelets.FaceletException. If i have an error in the xhtml page, i dont want to show the stack trace instead show an error page. This i have achieved by specifying error page in web.xml. The problem is that i want to log this error. I am using log4j for other exceptions, but how to write handler for FaceletException. If write another Exception handler is there an order in which i should specify the handler class because i am already using FullAjaxExceptionHandler.
faces-config.xml:
<application>
<el-resolver>
org.springframework.web.jsf.el.SpringBeanFacesELResolver
</el-resolver>
</application>
<factory>
<exception-handler-factory>org.omnifaces.exceptionhandler.FullAjaxExceptionHandlerFactory</exception-handler-factory>
</factory>
<render-kit>
<renderer>
<component-family>org.primefaces</component-family>
<renderer-type>org.primefaces.component.ScheduleRenderer</renderer-type>
<renderer-class>com.example.domain.web.custom.MyScheduleRenderrer</renderer-class>
</renderer>
</render-kit>
Add a custom Exception handler factory in faces-config.xml
<application>
<el-resolver>
org.springframework.web.jsf.el.SpringBeanFacesELResolver
</el-resolver>
</application>
<factory>
<exception-handler-factory>com.fetchinglife.domain.web.permissions.CustomExceptionHandlerFactory</exception-handler-factory>
</factory>
Create custom exception handler factory
public class CustomExceptionHandlerFactory extends ExceptionHandlerFactory{
private ExceptionHandlerFactory wrapped;
public CustomExceptionHandlerFactory(ExceptionHandlerFactory wrapped) {
this.wrapped = wrapped;
}
#Override
public ExceptionHandler getExceptionHandler() {
return new CustomExceptionHandler(wrapped.getExceptionHandler());
}
#Override
public ExceptionHandlerFactory getWrapped() {
return wrapped;
}
}
Create custom exception handler
public class CustomExceptionHandler extends FullAjaxExceptionHandler{
static Logger log = Logger
.getLogger(CustomExceptionHandler.class);
public CustomExceptionHandler(ExceptionHandler wrapped) {
super(wrapped);
}
#Override
public void handle() throws FacesException {
Iterator<ExceptionQueuedEvent> unhandledExceptionQueuedEvents = getUnhandledExceptionQueuedEvents().iterator();
if(unhandledExceptionQueuedEvents.hasNext()){
Throwable exception = unhandledExceptionQueuedEvents.next().getContext().getException();
log.error("FL Error",exception);
}
super.handle();
}
}
Unfortunately, it appears that you can't have both.
From The JSF Spec, the ExceptionHandlerFactory is expecting a single type to be configured
From the source of the FullAjaxExceptionHandler, if the exception is non-ajax, it's rethrown, to be handled by the <error-page> mechanism.
You'll either have to extend or wrap the FullAjaxExceptionHandler to provide both Ajax and Non-Ajax exception handling
I've put wro.xml in src/main/resources as there are some other resources and it's easier to access them in unit tests.
I need to extend some wro classes now to be able to read the model from another place, but can't get it working.
Necessary code
web.xml:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<filter>
<filter-name>WebResourceOptimizer</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>wroFilter</param-value>
</init-param>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>WebResourceOptimizer</filter-name>
<url-pattern>/resources/*</url-pattern>
</filter-mapping>
applicationContext.xml:
<bean id="wroFilter" class="ro.isdc.wro.http.ConfigurableWroFilter">
<property name="properties" ref="wroProperties" />
</bean>
<bean id="wroProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="classpath:wro.properties" />
</bean>
wro.properties:
managerFactoryClassName=com.example.web.wro.manager.factory.MyWroManagerFactory;
preProcessors=cssUrlRewriting,cssImport,semicolonAppender,lessCss
postProcessors=cssMin,jsMin
debug=true
MyWroManagerFactory:
public class MyWroManagerFactory extends CopyrightKeeperConfigurableWroManagerFactory {
private static final Logger LOG = LoggerFactory.getLogger(MyWroManagerFactory.class);
#Override
protected WroModelFactory newModelFactory() {
LOG.debug("Load wro.xml directly from classpath");
return new XmlModelFactory() {
#Override
protected InputStream getModelResourceAsStream() throws IOException {
final String resourceLocation = getDefaultModelFilename();
final InputStream stream = getClass().getResourceAsStream(resourceLocation);
if (stream == null) {
throw new IOException("Invalid resource requested: " + resourceLocation);
}
return stream;
}
};
}
}
CopyrightKeeperConfigurableWroManagerFactory:
public class CopyrightKeeperConfigurableWroManagerFactory extends ConfigurableWroManagerFactory {
private static final Logger LOG = LoggerFactory.getLogger(CopyrightKeeperConfigurableWroManagerFactory.class);
private static final String[] PROCESSORS = {
CssImportPreProcessor.ALIAS,
JawrCssMinifierProcessor.ALIAS,
CssMinProcessor.ALIAS,
JSMinProcessor.ALIAS
};
#Override
protected void contributePreProcessors(final Map<String, ResourcePreProcessor> map) {
for (String processor : PROCESSORS) {
if (map.containsKey(processor)) {
LOG.debug("Apply CopyrightKeeperProcessorDecorator on " + processor);
map.put(processor, CopyrightKeeperProcessorDecorator.decorate(map.get(processor)));
}
}
}
}
Why it can't find classes/wro.xml / How to use a custom location for wro.xml?
EDIT
Here's the full log output: http://pastebin.com/NeNy1NH4
The problem is that you are loading the model relative to the MyWroManagerFactory class:
final InputStream stream = getClass().getResourceAsStream(resourceLocation);
That means that it will look for the model in the folder where the class is located. Since your wro.xml is located in classes folder (which is a root for classpath), you should use the following:
ClassLoader.getSystemResourceAsStream(resourceLocation);
Alternatively you could use ClasspathUriLocator:
new ClasspathUriLocator().locate("classpath:" + resourceLocation)
EDITED:
Apparently this example discovered a problem which is described in the following issue:
Until the fix is ready, the following options are available:
Option 1
Option 2
I have a web server responding with xml data and a client consuming it.
Both share the same domain code. One of the domain objects looks like this:
#XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
#XmlRootElement(name = "image")
public class Image {
private String filename;
private ImageTypeEnum type;
#XmlElement(name = "imageUri")
public String getAbsoluteUri() {
// some complex computation
return uri;
}
}
When I try to unmarshal the response from the server into this object, since there's no setter for absoluteUri, I don't have the imageUri in the class. So I extend it like this:
public class FEImage extends Image{
private String imageUri;
public String getAbsoluteUri() {
return imageUri;
}
public void setAbsoluteUri(String imageUri) {
this.imageUri = imageUri;
}
}
My ObjectFactory
#XmlRegistry
public class ObjectFactory {
public Image createImage(){
return new FEImage();
}
}
My code to unmarshal is here:
JAXBContext context = JAXBContext.newInstance(ObjectFactory.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.setProperty("com.sun.xml.bind.ObjectFactory",new ObjectFactory());
((JAXBElement)unmarshaller.unmarshal((InputStream) response.getEntity())).getValue();
However, the setAbsoluteUri doesn't seem to be getting called in FEImage while unmarshalling. When I add a dummy setAbsoluteUri in Image.java, everything works as expected.
Can someone tell me how can I cleanly extend from Image.java?
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
A JAXB implementation is not required to use the ObjectFactory class when instantiating an object. You can configure instantiation to be done via a factory class using the #XmlType annotation:
#XmlType(factoryClass=ObjectFactory.class, factoryMethod="createImage")
public class Image {
private String filename;
private ImageTypeEnum type;
#XmlElement(name = "imageUri")
public String getAbsoluteUri() {
// some complex computation
return uri;
}
}
http://blog.bdoughan.com/2011/06/jaxb-and-factory-methods.html
If you do the above, then your JAXB implementation will still use the Image class to derive the metadata so it will not solve your problem. An alternate approach would be to use an XmlAdapter for this use case:
http://blog.bdoughan.com/2010/12/jaxb-and-immutable-objects.html
Better still, when a property on your domain object does not have a setter, you can tell your
JAXB implementation (EclipseLink MOXy, Metro, Apache JaxMe, etc) to use field (instance variable) access instead using #XmlAccessorType(XmlAccessType.FIELD):
#XmlAccessorType(XmlAccessType.FIELD)
public class Image {
}
http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html
UPDATE #1
If you are not able to modify the domain objects, then you may be interested in MOXy's externalized metadata. This extension provides a means via XML to provide JAXB metadata for classes where you cannot modify the source.
For More Information
http://blog.bdoughan.com/2010/12/extending-jaxb-representing-annotations.html
http://wiki.eclipse.org/EclipseLink/UserGuide/MOXy/Runtime/XML_Bindings
UPDATE #2 - Based on results of chat
Image
Below is the implementation of the Image class that I will use for this example. For the complex computation of getAbsoluteUri() I simply add the prefix "CDN" to the filename:
package forum7552310;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
#XmlRootElement(name = "image")
public class Image {
private String filename;
private ImageTypeEnum type;
#XmlElement(name = "imageUri")
public String getAbsoluteUri() {
return "CDN" + filename;
}
}
binding.xml
Below is the MOXy binding document I put together. In this file I do a few things:
Set XmlAccessorType to FIELD
Mark the absoluteURI property to be XmlTransient since we will be mapping the filename field instead.
Specify that an XmlAdapter will be used with the filename field. This is to apply the logic that is done in the getAbsoluteUri() method.
<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="forum7552310">
<java-types>
<java-type name="Image" xml-accessor-type="FIELD">
<java-attributes>
<xml-element java-attribute="filename" name="imageUri">
<xml-java-type-adapter value="forum7552310.FileNameAdapter"/>
</xml-element>
<xml-transient java-attribute="absoluteUri"/>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
FileNameAdapter
Below is the implementation of the XmlAdapter that applies the same name algorithm as the getAbsoluteUri() method:
package forum7552310;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class FileNameAdapter extends XmlAdapter<String, String> {
#Override
public String marshal(String string) throws Exception {
return "CDN" + string;
}
#Override
public String unmarshal(String adaptedString) throws Exception {
return adaptedString.substring(3);
}
}
Demo
Below is the demo code demonstrating how to apply the binding file when creating the JAXBContext:
package forum7552310;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String, Object>(1);
properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "forum7552310/binding.xml");
JAXBContext jc = JAXBContext.newInstance(new Class[] {Image.class}, properties);
File xml = new File("src/forum7552310/input.xml");
Unmarshaller unmarshaller = jc.createUnmarshaller();
Image image = (Image) unmarshaller.unmarshal(xml);
System.out.println(image.getAbsoluteUri());
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(image, System.out);
}
}
jaxb.properties
You need to include a file named jaxb.properties with the following contents in the same package as your Image class:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
input.xml
Here is the XML input I used:
<?xml version="1.0" encoding="UTF-8"?>
<image>
<imageUri>CDNURI</imageUri>
</image>
Output
And here is the output from running the demo code:
CDNURI
<?xml version="1.0" encoding="UTF-8"?>
<image>
<imageUri>CDNURI</imageUri>
</image>