Apache Camel + JBoss Fuse - CXFRS组件和bean组件之间的路由 - IOException流已关闭

时间:2015-04-17 11:17:24

标签: apache-camel cxf cxfrs

我编写了一个CXF REST服务,它返回如下配置的application / json:

ProspectGenerator:

@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("/consume")
public Prospect convertProspectToJson(Prospect prospect,@QueryParam("customerType") String customerType){

    //DTO logic goes here
    System.out.println("Prospect obj received.."+prospect.getProspectName());

    prospect.setCustomerType(customerType);

    return prospect;

}

此服务在部署到JBoss Fuse时有效。

现在我有一个Camel Route构建器,如下所示:

public class ServiceRouter extends RouteBuilder {

/* (non-Javadoc)
 * @see org.apache.camel.builder.RouteBuilder#configure()
 */
@Override
public void configure() throws Exception {

    from("jetty://http://localhost:8182/route/prospect?bindingStyle=SimpleConsumer")
    //The Raw type to String conversion is done here. Refer the class for details
    //Processor converts the Message from byte[] to String
    .process(new ResponseToStringProcessor())
    .unmarshal("jsonDataformat") //jsonDataformat - bean in blueprint XML for converting to requested object type
    .to("cxfrs://http://localhost:8181/cxf/prospect/consume")
    /*1) The above line has the BodyType as Cxf's ResponseImpl instead of Prospect Object
    * 2) The below bean accepts type only as Prospect object and hence I get exception */
    .to("bean:prospectService?method=doSomething");

}

ProspectService:

public class ProspectService {
    public void doSomething(@Body Prospect prospect){

    System.out.println("Prospect Service got the request.."+prospect.getCustomerType());

    }
}

在尝试路由到定义的bean时遇到以下异常:

org.apache.camel.InvalidPayloadException:没有可用的类型:com.karthik.bo.Prospect,但其值为:org.apache.cxf.jaxrs.impl.ResponseImpl@54c96476类型为

如何解决此错误?我无法弄清楚如何将ResponseImpl转换为我的POJO。跟踪显示:

BodyType:org.apache.cxf.jaxrs.impl.ResponseImpl,正文:{“prospect”:{“customerType”:“VIP”,“prospectId”:999,“prospectName”:“karthik”} }

我尝试将ResponseImpl转换为String(将包含JSON有效负载),但我总是在IOException流中关闭。 - 下面的转换代码:

public void convert(Exchange exchange){

InputStream is =  (InputStream)exchange.getIn().getBody(ResponseImpl.class).getEntity();

String s="";
try {
    s = org.apache.commons.io.IOUtils.toString(is);
    is.close();
} catch (IOException e) {
    e.printStackTrace();
}
System.out.println("in convertor..."+s);

}

经过大量的失败尝试(在评论中提到),最终的解决方案是编写一个FallBackCoverter,如下所示 - 这没有抛出Stream关闭的IOException,这无法向自己解释。

@FallbackConverter
    public static <T> T convertTo(Class<T> type, Exchange exchange, Object value, TypeConverterRegistry registry){

        System.out.println("Checking fall backconverter");
        if (ResponseImpl.class.isAssignableFrom(value.getClass())) {
            TypeConverter tc = registry.lookup(type, ResponseImpl.class);
            if (tc == null && type.getName().equals("com.karthik.bo.Prospect")) {
                Prospect prospect=new Prospect();
                try(InputStream is =  (InputStream)((ResponseImpl)value).getEntity()) {
                    if(is!=null){
                        String s = org.apache.commons.io.IOUtils.toString(is);
                        if(s!=null && s.length()>0){
                            ObjectMapper mapper = new ObjectMapper();
                            prospect = mapper.readValue(s, Prospect.class);

                        }
                        is.close();
                    }
                } catch (IOException e) {
                    System.out.println("Exception occured"+e.getMessage());
                }
                return type.cast(prospect);
            }
        }

        return (T) Void.TYPE;
    }

,我的路由器被修改为:

from("jetty://http://localhost:8182/route/prospect?bindingStyle=SimpleConsumer")
        //The Raw type to String conversion is done here. Refer the class for details
        .process(new ResponseToStringProcessor())
        .unmarshal("jsonDataformat")
        .to("cxfrs://http://localhost:8181/cxf/prospect/consume")
        .convertBodyTo(Prospect.class)
        .to("bean:prospectService?method=doSomething")
        .marshal("jsonDataformat");

如果不确认这是否是唯一的方法,我无法将此标记为解决方案。我在camel-cxf

中发现旧的JIRA问题后实现了我的解决方案

https://issues.apache.org/jira/browse/CAMEL-3208

我必须为此目的编写一个FallBackConvertor。

1 个答案:

答案 0 :(得分:1)

看起来您的通话可能无法通过服务类的反射来获取。我不确定您的转换类正在尝试做什么,但您是否尝试过将一个提供程序添加到客户端/服务器的末尾以处理Json到POJO映射?让我发布一个对我有用的简单示例(我在蓝图中做了这个,但它应该类似于DSL)。请原谅代码状态,我仍在进行大量测试,以找出哪些有效且无效。

这里我有一个cxf rs客户端和服务器:

<cxf:rsClient id="rsClient" address="${CXFclient}" serviceClass="com.limelight.esb.servicedb.services.ServiceDBExternalRestService"
    loggingFeatureEnabled="true" loggingSizeLimit="500">
    <cxf:providers>
        <bean class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider" />
    </cxf:providers>
</cxf:rsClient>

<cxf:rsServer id="rsServer" address="${CXFserver}${serverService}" serviceClass="com.limelight.esb.servicedb.services.ServiceDBRestService"
    loggingFeatureEnabled="true" loggingSizeLimit="500">
    <cxf:providers>
        <bean class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider" />
    </cxf:providers>
</cxf:rsServer>

我的路线如下:

    <route id="servicedb.rest.company.get.route">
        <from uri="{{servicedb.rest.company.get}}" />
        <log message="Rest getCompany called." />
        <to uri="{{servicedb.company.get}}" />
        <removeHeader headerName="Content-Length" />
    </route>

最后我的服务类:

@Path("/servicedb/")
public class ServiceDBRestService {
    @GET
    @Path("company/{id}")
    @Produces("application/json")
    public ServiceDBCompanyResponse getCompanyRest(@PathParam("id") String id) {
        //just an interface
        return null;
    }
}

这也可能与预期响应对象的标头有关。为此,您可以使用自定义预处理器对其进行修改,然后在发出呼叫之前将其添加到路由中。

public class ServiceDBProcessors implements Processor {

@Override
public void process(Exchange exchng) throws Exception {        
    exchng.getIn().setHeader("CamelCxfRsResponseClass", your.class.here.class);
}

} 现在,我偶然发现这个项目的原因是我在通过肥皂打电话时遇到了这个问题,我必须玩很多,直到我找到你的解决方案。如果上述方法没有帮助,我至少可以提供一个稍微好一点的答案来消除一点转换。我发现你可以在返回之后放置另一个处理程序进行转换,如果你的输入参数是一个字符串,它应该用反射来拾取。我不得不写一个如下的类:

    public ServiceDBCompany convertCompany(String companyResponseBody) {
        ServiceDBCompany newCompany = new ServiceDBCompany();
        if(companyResponseBody!=null && companyResponseBody.length()>0){
            ObjectMapper mapper = new ObjectMapper();
            try {
                ServiceDBCompanyResponse company = mapper.readValue(companyResponseBody, ServiceDBCompanyResponse.class);
                newCompany.setCompanyId(company.getCompanyId());
                newCompany.setName(company.getCompanyName());
            } catch(IOException e) {
                System.out.println("Exception reading body, returning null " + ServiceDBCompany.class);
            }
        }
        return newCompany;
}

希望这至少能为您提供一些可以尝试的解决方案的想法!