Apache Camel + JBoss Fuse - Маршрутизация между компонентом CXFRS и компонентом bean - поток IOException закрыт

Я написал службу CXF REST, которая возвращает приложение / 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());

    }
}

Я получаю следующее исключение при попытке выполнить маршрутизацию к определенному компоненту:

org.apache.camel.InvalidPayloadException: нет доступного тела типа: com.karthik.bo.Prospect, но имеет значение: org.apache.cxf.jaxrs.impl.ResponseImpl@54c96476 типа

Как мне исправить эту ошибку? Я не могу понять, как преобразовать ResponseImpl в мой POJO. Пришедший след показывает:

BodyType: org.apache.cxf.jaxrs.impl.ResponseImpl, Body: {"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 closed 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");

Я не мог отметить это как решение, не подтвердив, что это единственный способ сделать это. Мое решение было реализовано после того, как я обнаружил старую проблему JIRA в camel-cxf

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

Для этого мне пришлось написать FallBackConvertor.


person Karthik R    schedule 17.04.2015    source источник
comment
Мой несколько подходов к преобразованию ResponseImpl, который имел InputStream в качестве тела, не удался. Я пробовал следующее: 1) В методе процесса извлек ResponseImpl и попытался преобразовать InputStream в String с помощью IOUtils.toString () .. - не удалось, поскольку выбросил IOException - поток закрыт 2) Пытался вызвать компонент bean-компонента, который принимает ResponseImpl как тело и попробовал такое же преобразование, как указано выше - не удалось 3) Пытался написать TypeConvertor для верблюда для этого преобразования - не удалось с тем же исключением - поток закрыт Все вышеперечисленные подходы терпели неудачу, и я понятия не имел, почему поток был закрыт.   -  person Karthik R    schedule 20.04.2015
comment
Единственный обходной путь, который мне помог, - это написание преобразователя Fallbacktype, который выполнял бы ту же операцию преобразования ResponseImpl в InputStream и из InputStream в String и из String (Json) в мой BO с помощью ObjectMapper Джексона. Хотя это работает, я не мог принять это как действительное решение, так как мне пришлось сделать так много преобразований для простого JSON, возвращенного из cxfrs: uri   -  person Karthik R    schedule 20.04.2015


Ответы (1)


Похоже, ваш звонок не был принят из-за отражения класса обслуживания. Я не совсем уверен, что пытается сделать ваш класс преобразования, но пробовали ли вы просто добавить поставщика в конец вашего клиента / сервера для обработки сопоставления Json с POJO? Позвольте мне опубликовать простой пример, который сработал для меня (я сделал это в blueprint, но он должен работать аналогично для 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;
}

Надеюсь, это даст вам хотя бы несколько идей о решениях, которые вы можете попробовать!

person Kendall Rogers    schedule 13.05.2015
comment
Привет, @Kendall Rogers, Спасибо. Ваше решение, вероятно, сработает, потому что это своего рода другое преобразование, которое мы оба пытаемся сделать, преобразовывая его в простой требуемый объект. Поскольку этот подход потерпел неудачу и длился дольше, мы пробовали использовать другие параметры cxf: dataFormats и т. Д. Когда мы начали новый проект, мы больше никогда не сталкивались с этой ошибкой. Я не мог использовать cxfrs: provider, поскольку я использовал java DSL с компонентом пристани, а не компонент cxfrs в camel. Да, мы использовали компонент cxfrs и продолжили работу без этой ошибки. Я ценю вашу помощь. Спасибо :) - person Karthik R; 15.05.2015