単純なデータ型(例:文字列、整数)に適したCXFレストクライアントを使用しています。ただし、カスタムオブジェクトを使用しようとすると、次のようになります。
Exception in thread "main" org.Apache.cxf.interceptor.Fault: .No message body writer found for class : class com.company.datatype.normal.MyObject.
at org.Apache.cxf.jaxrs.client.ClientProxyImpl$BodyWriter.handleMessage(ClientProxyImpl.Java:523)
at org.Apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.Java:263)
at org.Apache.cxf.jaxrs.client.ClientProxyImpl.doChainedInvocation(ClientProxyImpl.Java:438)
at org.Apache.cxf.jaxrs.client.ClientProxyImpl.invoke(ClientProxyImpl.Java:177)
at $Proxy13.execute(Unknown Source)
at com.company.JaxTestClient.main(JaxTestClient.Java:26)
Caused by: org.Apache.cxf.jaxrs.client.ClientWebApplicationException: .No message body writer found for class : class com.company.datatype.normal.MyObject.
at org.Apache.cxf.jaxrs.client.AbstractClient.reportMessageHandlerProblem(AbstractClient.Java:491)
at org.Apache.cxf.jaxrs.client.AbstractClient.writeBody(AbstractClient.Java:401)
at org.Apache.cxf.jaxrs.client.ClientProxyImpl$BodyWriter.handleMessage(ClientProxyImpl.Java:515)
... 5 more
私はこのように呼んでいます:
JaxExample jaxExample = JAXRSClientFactory.create( "http://localhost:8111/", JaxExample.class );
MyObject before = ...
MyObject after = jaxExample.execute( before );
インターフェイスのメソッドは次のとおりです。
@POST
@Path( "execute" )
@Produces( "application/json" )
MyObject execute( MyObject myObject );
Restletライブラリは、パスにXStream依存関係を追加することにより、これを非常に簡単に行います。 CXFは似たようなものですか?
編集#1:
これをCXF問題管理システムの機能改善として投稿しました here 。私はこれが出席されることを望むことができるだけです。
すぐに使用できるわけではありませんが、CXFはRESTサービスへのJSONバインディングをサポートしています。ここで cxf jax-rs json docsを参照してください。 プロバイダーを利用可能にするために、いくつかの最小限の構成を行う必要があります。 JSONの形成方法。
EDIT:コメント要求ごとに、いくつかのコードがあります。これについてはあまり経験がありませんが、次のコードはクイックテストシステムの例として機能しました。
//TestApi parts
@GET
@Path ( "test" )
@Produces ( "application/json" )
public Demo getDemo () {
Demo d = new Demo ();
d.id = 1;
d.name = "test";
return d;
}
//client config for a TestApi interface
List providers = new ArrayList ();
JSONProvider jsonProvider = new JSONProvider ();
Map<String, String> map = new HashMap<String, String> ();
map.put ( "http://www.myserviceapi.com", "myapi" );
jsonProvider.setNamespaceMap ( map );
providers.add ( jsonProvider );
TestApi proxy = JAXRSClientFactory.create ( url, TestApi.class,
providers, true );
Demo d = proxy.getDemo ();
if ( d != null ) {
System.out.println ( d.id + ":" + d.name );
}
//the Demo class
@XmlRootElement ( name = "demo", namespace = "http://www.myserviceapi.com" )
@XmlType ( name = "demo", namespace = "http://www.myserviceapi.com",
propOrder = { "name", "id" } )
@XmlAccessorType ( XmlAccessType.FIELD )
public class Demo {
public String name;
public int id;
}
ノート:
これは例として少し汚いですが、うまくいけばうまくいきます。
Edit2:jaxbを避けるためにxstreamに基づくメッセージ本文ライターの例。
@Produces ( "application/json" )
@Consumes ( "application/json" )
@Provider
public class XstreamJsonProvider implements MessageBodyReader<Object>,
MessageBodyWriter<Object> {
@Override
public boolean isWriteable ( Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType ) {
return MediaType.APPLICATION_JSON_TYPE.equals ( mediaType )
&& type.equals ( Demo.class );
}
@Override
public long getSize ( Object t, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType ) {
// I'm being lazy - should compute the actual size
return -1;
}
@Override
public void writeTo ( Object t, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream )
throws IOException, WebApplicationException {
// deal with thread safe use of xstream, etc.
XStream xstream = new XStream ( new JettisonMappedXmlDriver () );
xstream.setMode ( XStream.NO_REFERENCES );
// add safer encoding, error handling, etc.
xstream.toXML ( t, entityStream );
}
@Override
public boolean isReadable ( Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType ) {
return MediaType.APPLICATION_JSON_TYPE.equals ( mediaType )
&& type.equals ( Demo.class );
}
@Override
public Object readFrom ( Class<Object> type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, String> httpHeaders, InputStream entityStream )
throws IOException, WebApplicationException {
// add error handling, etc.
XStream xstream = new XStream ( new JettisonMappedXmlDriver () );
return xstream.fromXML ( entityStream );
}
}
//now your client just needs this
List providers = new ArrayList ();
XstreamJsonProvider jsonProvider = new XstreamJsonProvider ();
providers.add ( jsonProvider );
TestApi proxy = JAXRSClientFactory.create ( url, TestApi.class,
providers, true );
Demo d = proxy.getDemo ();
if ( d != null ) {
System.out.println ( d.id + ":" + d.name );
}
サンプルコードには、堅牢なメディアタイプのサポート、エラー処理、スレッドセーフなどの部分がありません。しかし、最小限のコードでjaxbの問題を回避する必要があります。
EDIT 3-サーバー側の構成のサンプル前述のように、サーバー側はスプリング構成です。プロバイダーに接続するために機能するサンプル構成を次に示します。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxrs="http://cxf.Apache.org/jaxrs"
xmlns:cxf="http://cxf.Apache.org/core"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://cxf.Apache.org/jaxrs http://cxf.Apache.org/schemas/jaxrs.xsd
http://cxf.Apache.org/core http://cxf.Apache.org/schemas/core.xsd">
<import resource="classpath:META-INF/cxf/cxf.xml" />
<jaxrs:server id="TestApi">
<jaxrs:serviceBeans>
<ref bean="testApi" />
</jaxrs:serviceBeans>
<jaxrs:providers>
<bean id="xstreamJsonProvider" class="webtests.rest.XstreamJsonProvider" />
</jaxrs:providers>
</jaxrs:server>
<bean id="testApi" class="webtests.rest.TestApi">
</bean>
</beans>
また、使用しているcxfの最新の改訂版ではメディアタイプに違いがあるため、上記のxstreamメッセージ本文リーダー/ライターの例では、isWritable/isReadableを次のように変更する必要があります。
return MediaType.APPLICATION_JSON_TYPE.getType ().equals ( mediaType.getType () )
&& MediaType.APPLICATION_JSON_TYPE.getSubtype ().equals ( mediaType.getSubtype () )
&& type.equals ( Demo.class );
EDIT 4-非スプリング構成選択したサーブレットコンテナを使用して、構成します
org.Apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet
以下の少なくとも2つの初期化パラメータ
jaxrs.serviceClasses
jaxrs.providers
ここで、serviceClassesは、上記のTestApiなど、バインドするサービス実装のスペース区切りリストであり、プロバイダーは、上記のXstreamJsonProviderなどのメッセージ本文プロバイダーのスペース区切りリストです。 Tomcatでは、web.xmlに次を追加できます。
<servlet>
<servlet-name>cxfservlet</servlet-name>
<servlet-class>org.Apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet</servlet-class>
<init-param>
<param-name>jaxrs.serviceClasses</param-name>
<param-value>webtests.rest.TestApi</param-value>
</init-param>
<init-param>
<param-name>jaxrs.providers</param-name>
<param-value>webtests.rest.XstreamJsonProvider</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
これは、スプリングなしで実行する最も簡単な方法です。サーブレットコンテナを使用していない場合は、XstreamJsonProviderのインスタンスでJAXRSServerFactoryBean.setProvidersを構成し、JAXRSServerFactoryBean.setResourceProviderメソッドを介してサービス実装を設定する必要があります。 CXFNonSpringJaxrsServlet.initメソッドをチェックして、サーブレットコンテナでセットアップする際の方法を確認します。
それはあなたのシナリオに関係なくあなたを行かせるべきです。
CXF 2.7.0から3.0.2へのアップグレード中にこの問題が発生しました。これは私がそれを解決するためにしたことです:
私のpom.xmlに以下を含めました
<dependency>
<groupId>org.Apache.cxf</groupId>
<artifactId>cxf-rt-rs-extension-providers</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<version>1.9.0</version>
</dependency>
次のプロバイダーを追加しました
<jaxrs:providers>
<bean class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider" />
</jaxrs:providers>
設定のjaxrs:clientルートを使用している場合は、JacksonJsonProviderを使用して提供することを選択できます。
<jaxrs:client id="serviceId"
serviceClass="classname"
address="">
<jaxrs:providers>
<bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider">
<property name="mapper" ref="jacksonMapper" />
</bean>
</jaxrs:providers>
</jaxrs:client>
<bean id="jacksonMapper" class="org.codehaus.jackson.map.ObjectMapper">
</bean>
クラスパスにjackson-mapper-aslおよびjackson-jaxrアーティファクトを含める必要があります
プログラムでサーバーを作成する場合、プロバイダーを設定してjson/xmlのメッセージ本文ライターを追加できます。
JAXRSServerFactoryBean bean = new JAXRSServerFactoryBean();
bean.setAddress("http://localhost:9000/");
List<Object> providers = new ArrayList<Object>();
providers.add(new JacksonJaxbJsonProvider());
providers.add(new JacksonJaxbXMLProvider());
bean.setProviders(providers);
List<Class< ? >> resourceClasses = new ArrayList<Class< ? >>();
resourceClasses.add(YourRestServiceImpl.class);
bean.setResourceClasses(resourceClasses);
bean.setResourceProvider(YourRestServiceImpl.class, new SingletonResourceProvider(new YourRestServiceImpl()));
BindingFactoryManager manager = bean.getBus().getExtension(BindingFactoryManager.class);
JAXRSBindingFactory restFactory = new JAXRSBindingFactory();
restFactory.setBus(bean.getBus());
manager.registerBindingFactory(JAXRSBindingFactory.JAXRS_BINDING_ID, restFactory);
bean.create();
CXFNonSpringJAXRSServletを構成することもできます(JSONProviderが使用されていると仮定):
<init-param>
<param-name>jaxrs.providers</param-name>
<param-value>
org.Apache.cxf.jaxrs.provider.JSONProvider
(writeXsiType=false)
</param-value>
</init-param>
「cxf-rt-rs-client」バージョン3.03を使用している場合。または、xml名前空間とschemaLocationが以下のように宣言されていることを確認してください
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxrs="http://cxf.Apache.org/jaxrs"
xmlns:jaxrs-client="http://cxf.Apache.org/jaxrs-client"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.Apache.org/jaxrs http://cxf.Apache.org/schemas/jaxrs.xsd http://cxf.Apache.org/jaxrs-client http://cxf.Apache.org/schemas/jaxrs-client.xsd">
そして、クライアントにJacksonJsonProviderまたはカスタムJsonProviderがあることを確認してください
<jaxrs-client:client id="serviceClient" address="${cxf.endpoint.service.address}" serviceClass="serviceClass">
<jaxrs-client:headers>
<entry key="Accept" value="application/json"></entry>
</jaxrs-client:headers>
<jaxrs-client:providers>
<bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider">
<property name="mapper" ref="jacksonMapper" />
</bean>
</jaxrs-client:providers>
</jaxrs-client:client>
応答でオブジェクトがJSONであると予想される場合は、残りのクライアントヘッダーに「Accept:application/json」と記載することもできます。
私のシナリオでは、ポート番号のない残りのURLが負荷分散用に適切に構成されていない場合、同様のエラーに直面しました。残りのURLをポート番号で確認しましたが、この問題は発生していませんでした。そのため、この問題を解決するには、負荷分散構成を更新する必要がありました。
上記の変更はどれもうまくいきませんでした。以下の私の作業構成をご覧ください:
依存関係:
<dependency>
<groupId>org.Apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.Apache.cxf</groupId>
<artifactId>cxf-rt-rs-extension-providers</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.codehaus.jettison</groupId>
<artifactId>jettison</artifactId>
<version>1.4.0</version>
</dependency>
web.xml:
<servlet>
<servlet-name>cfxServlet</servlet-name>
<servlet-class>org.Apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.MyApplication</param-value>
</init-param>
<init-param>
<param-name>jaxrs.providers</param-name>
<param-value>org.codehaus.jackson.jaxrs.JacksonJsonProvider</param-value>
</init-param>
<init-param>
<param-name>jaxrs.extensions</param-name>
<param-value>
json=application/json
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>cfxServlet</servlet-name>
<url-pattern>/v1/*</url-pattern>
</servlet-mapping>
コーディングをお楽しみください.. :)