Search code examples
javarestapache-camelcxfrs

Camel cxf REST request not separating query parameters


I am trying to send some messages through a local RESTful web service. The URL is allowed to contain query parameters. My processor for the REST method is not seeing those query parameters. Does anyone see what I am doing wrong?

I am using Camel 2.16.0.

My Blueprint routes XML is:

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:camel-cxf="http://camel.apache.org/schema/blueprint/cxf"
       xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0
                           http://www.osgi.org/xmlns/1.0.0/blueprint.xsd
                           http://camel.apache.org/schema/blueprint/cxf
                           http://camel.apache.org/schema/cxf/camel-cxf-blueprint.xsd">

  <bean id="issPreprocessor" class="com.rockwellcollins.railwaynet.messagerouter.routing.IssPreprocessor"/>
  <bean id="webServiceProcessor" class="com.rockwellcollins.railwaynet.messagerouter.routing.WebServiceProcessor"/>
  <bean id="packageWebServiceReplyForIss" class="com.rockwellcollins.railwaynet.messagerouter.routing.PackageWebServiceReplyForIss"/>

  <!-- These two cooperate to send web service requests to the mocked
       out web service substitute. The crewMock bean handles requests sent
       to the jetty service stand-in; the webService rsClient sends the
       request.
  -->
  <bean id="steve" class="messagerouter.EmployeeInfo">
    <argument value="Stephen"/>
    <argument value="D."/>
    <argument value="Huston"/>
    <argument value="1234"/>
  </bean>
  <bean id="crewMock" class="messagerouter.CrewServiceMock">
    <property name="info">
      <map>
        <entry key="rraa">
          <map>
            <entry key="steve" value-ref="steve"/>
          </map>
        </entry>
      </map>
    </property>
  </bean>
  <camel-cxf:rsClient id="webService" address="http://localhost:9000" serviceClass="com.rockwellcollins.railwaynet.messagerouter.routing.WebService" loggingFeatureEnabled="true"/>

  <camelContext id="rraaCamelContext" xmlns="http://camel.apache.org/schema/blueprint">
    <dataFormats>

      <json id="IssRequest" library="Jackson"/>
    </dataFormats>

    <!-- Mocked out web service -->
    <route id="CrewMock">
      <from uri="jetty:http://localhost:9000?matchOnUriPrefix=true"/>
      <to uri="cxfbean:crewMock"/>
    </route>

    <route id="rraaIss">
      <from uri="seda:from_rraa"/>
      <process ref="issPreprocessor"/>
      <unmarshal ref="IssRequest"/>
      <process ref="webServiceProcessor"/>
      <to uri="cxfrs:bean:webService"/>
      <process ref="packageWebServiceReplyForIss"/>
      <to uri="seda:to_rraa"/>
    </route>
  </camelContext>

</blueprint>

My WebService class to set up the request is:

public class WebServiceProcessor implements Processor {

    @Override
    public void process(Exchange exchange) throws Exception {
        exchange.setPattern(ExchangePattern.InOut);
        Message in = exchange.getIn();
        // Using proxy client API; needs the op name.
        in.setHeader(CxfConstants.CAMEL_CXF_RS_USING_HTTP_API, Boolean.FALSE);
        in.setHeader(CxfConstants.OPERATION_NAME, "verifyEmployee");
        VerifyEmployeeRequest req = in.getBody(VerifyEmployeeRequest.class);
        MessageContentsList params = new MessageContentsList();
        params.add(req.getHeader().getScac());
        params.add(req.getBody().getEmployeeID());
        params.add(req.getBody().getPin());
        params.add(req.getBody().getReason());
        in.setBody(params);
    }
}

and my interface class for the service invocation is:

@Path(value="/rest")
public interface WebService {

    @GET
    @Path(value="/authentication/{scac}/employees/{id}?pin={pin}&reason={reason}")
    @Produces({ MediaType.APPLICATION_JSON })
    public String verifyEmployee(@PathParam("scac") String scac,
                                 @PathParam("id") String id,
                                 @PathParam("pin") String pin,
                                 @PathParam("reason") String reason);
}

The class to process the request is:

@Path("/rest")
public class CrewServiceMock {

    // scac -> { id -> employeeInfo }
    private Map<String, Map<String, EmployeeInfo> > info;

    public Map<String, Map<String, EmployeeInfo> > getInfo()
    { return info; }
    public void setInfo(Map<String, Map<String, EmployeeInfo> > info)
    { this.info = info; }

    @GET
    @Path("/authentication/{scac}/employees/{id}")
        public Response getAuth(@PathParam("scac") String scac,
                                @PathParam("id") String id,
                                @QueryParam("reason") String reason,
                                @QueryParam("pin") String pin) {
        Map<String, EmployeeInfo> employees = info.get(scac);
        if (employees == null) {
            return Response.status(404).build();
        }
        System.out.println(employees);
        System.out.println("id is " + id);
        System.out.println("scac is " + scac);
        System.out.println("reason is " + reason);
        EmployeeInfo emp = (EmployeeInfo)employees.get(id);
        if (emp == null) {
            return Response.status(404).entity("{ message: \"id not found\" }").build();
        }
        return Response.status(200).entity("{ \"employeeID\": " + id + ", \"employeeName\": { \"first\": \"" + emp.getFirstName() + "\", \"middle\": \"" + emp.getMiddleName() + "\", \"last\": \"" + emp.getLastName() + "\" } }").build();
    }
}

When I run this, I see the following in the test output:

[Camel (rraaCamelContext) thread #0 - seda://from_rraa] INFO org.apache.cxf.interceptor.LoggingOutInterceptor - Outbound Message

ID: 1 Address: http://localhost:9000/rest/authentication/rraa/employees/steve%3Fpin=1234&reason=INIT Http-Method: GET Content-Type: application/xml

Headers: {OriginalHeader=[{name=VerifyEmployeeRequest, version=1, scac=rraa, timeSent=null, uuid=abcd-1234}], Content-Type=[application/xml], Accept=[application/json]}

{steve=messagerouter.EmployeeInfo@1dd62581} id is steve?pin=1234&reason=INIT scac is rraa reason is null

So, the query parameters are apparently not being parsed out - they're attached to the last path parameter. Am I doing something wrong?


Solution

  • Your interface has 4 path parameters instead of 2 path and 2 query parameters, it should be:

    @Path(value="/rest")
    public interface WebService {
    
        @GET
        @Path(value="/authentication/{scac}/employees/{id}")
        @Produces({ MediaType.APPLICATION_JSON })
        public String verifyEmployee(@PathParam("scac") String scac,
                                 @PathParam("id") String id,
                                 @QueryParam("pin") String pin,
                                 @QueryParam("reason") String reason);
    }