You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@cxf.apache.org by Ryan Zoerner <ry...@gmail.com> on 2011/05/12 08:32:19 UTC

Basic Http Demo and the refactoring that I did, in the course of figuring out how it worked.

I've decided that I am not posting enough about my actual coding to the dev
list.

In an effort to set a better backdrop for conversations about my current
progress,
and level of learning, I've decided to try and fix that.

This email is the first of what will be numerous emails, talking, in a
little detail
about what actual coding I have worked on and to what effect. I also will
probably
make mention of some of the various pitfalls that I've encountered and what
I did
to fix the problem. I may also, at some times post some questions about
things that I
have not been able to figure out, as I think that it might aid the
discussion process.

That said, here is the first of what I intend to have be numerous emails to
the list.


JAX_RS/demo

At cxf/distribution/src/main/release/samples/jax_rs/basic
(
http://svn.apache.org/viewvc/cxf/trunk/distribution/src/main/release/samples/jax_rs/basic/)
is a basic http demo. I found the client code to be large and unwieldly, so
I refactored
it. I also found it hard to understand, at first, but the key, with the GET
request is
that the client opens a connection with the server, at the URI associated
with a given
resource. The client opens a printStream that is associated with the
connection, and
from that connection the client obtains the return information that would
normally be
associated with a GET request. It may be that since the only @Path in
CustomerService
with "/customerservice/customers/{id}" is the GET method, that the service
automatically
just queries that @Path and assumes a GET request, since that method is
annotated with
the @GET annotation. If you annotated it with more than one HTTP
RequestType, I am unsure
what would happen at the moment. That said, here is the refactoring part of
the code.

Client.java
-----------------------------------------------------------------------------------------------

/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership. The
 * ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the
 * License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unewlineess required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package demo.jaxrs.client;

import ...

public final class Client
{

 private Client()
 {}

 /**
  * This client should run batch processing on file input using the methods
  * of CustomerService. You shouldn't have to call the methods of any
  * ComplexEntity.
  *
  * @param args
  * @throws Exception
  */
 public static void main(String args[]) throws Exception
 {
     // sends a URL to "/customerservice/customers/123" and prints the
returned
     // xml to the screen.
  printGETCustomerFromID(123);
  newline();

  // sends a URL to "/customerservice/orders/223/products/323" and prints
the returned
  // xml to the screen.
  printGETProductFromID(223, 323);
  printGETProductFromID(224, 324);
  printGETProductFromID(225, 325);
  newline();


  updatePUTCustomerDataFile();
  newline();


  addPOSTCustomerDataFile();
  newline();


  System.exit(0);
 }

 private static void printGETCustomerFromID(long customerID)
 throws MalformedURLException, IOException, Exception
 {
  System.out.println("Sent HTTP GET request to query customer info");

  URL url =
   new URL("http://localhost:9000/customerservice/customers/"
    + customerID);
  System.out.println(url);

  // equivalent to url.openConnection().getInputStream();
  // it gets an InputStream from the java.net.URLConnection associated with
the
  // supplied URL.
  InputStream in = url.openStream();

  // this is equivalent to calling url.openConnection().getContent()
  // or, you can use shorthand by calling url.getContent(). The important
  // thing to note is that a connection has actually been opened; there is
  // no such thing as url content, that is not associated with you opening
  // a connection through that url, being stored on the server in the
absence
  // of a url request. This seems silly, but if you call url.getHeader(2);,
  // it makes one wonder how the url object or the server simply new what
  // item 2 of the headers would look like without generating all of them,
  // which it must do; and in the case it generates a full response to the
  // request, but the methods enable you to treat it as though it is stored
  // data, when in actuality, it is reprocessed on each method call.
  // ( .openConnection() )
  System.out.println(getStringFromInputStream(in));
  newline();
 }
 ...
}
-----------------------------------------------------------------------------------------------

The only additions are that I have changed the method signatures so as to
accept
a customer ID as a parameter. I also refactored the GETproduct method in the

following way:

Client.java
-----------------------------------------------------------------------------------------------
...
 private static void printGETProductFromID(long orderID, long productID)
 throws MalformedURLException, Exception
 {
  System.out
   .println("Sent HTTP GET request to query sub resource product info");

  URL url =
   new URL(
    "http://localhost:9000/customerservice/orders/" + orderID + "/products/"
     + productID);
  System.out.println(url);

  InputStream in = url.openStream();
  //if( in.equals(null) ) { System.out.println("inputStream comes back
null"); }
  System.out.println(getStringFromInputStream(in));
  newline();
 }
...
-----------------------------------------------------------------------------------------------

You can see what the added parameters do. This was to remove "magic numbers"
from the code,
a practice which we were taught to make use of, and it was to enable the
easy generation of
more customers, without having to type so much.

In order to make this code work, I had to refactor the
...server.CustomerService.java and
...server.Order.java files in the following way (please note the comments on
the init
methods.):

CustomerService.java
-----------------------------------------------------------------------------------------------
package demo.jaxrs.server;

...

@Path("/customerservice/")
public class CustomerService
{

 long currentId = 123;

 Map<Long,Customer> customersByID = new HashMap<Long,Customer>();
 Map<String,Customer> customersByName = new HashMap<String,Customer>();

 Map<Long,Order> orders = new HashMap<Long,Order>();


 public CustomerService()
 {
  init();
 }
...

 // The init method is called by the Constructor and instantiates the
customers
 // and product-orders in the system, when the Client is started.
 final void init()
 {
  initCustomer(123, "John");
  initOrder(223, 323);
  initOrder(224, 324);
  initOrder(225, 325);
 }

 // when a new Order is constructed, the init() method of Order is called,
 // which in turn, instantiates a Product. Therefore, both the orderID and
 // productID's are sent to this method.
 private void initOrder(long orderID, long productID)
 {
  Order o = new Order(orderID, productID);
  o.setDescription("order " + orderID);
  o.setId(orderID);
  orders.put(o.getId(), o);
 }

 private void initCustomer(long customerID, String customerName)
 {
  Customer c = new Customer();
  c.setName(customerName);
  c.setId(customerID);
  customersByID.put(c.getId(), c);
  customersByName.put(c.getName(), c);
 }
}
-----------------------------------------------------------------------------------------------


Order.java
-----------------------------------------------------------------------------------------------

package demo.jaxrs.server;

...

@XmlRootElement(name = "Order")
public class Order
{
 public Order()
 {
  init();
 }

 // This method enables the creation of multiple products each with an
individual
 // productID, whereas, before, the productID was hand coded into the init
method
 // below.
 public Order(long orderID, long productID)
 {
  init(productID);
 }
 @GET
 @Path("products/{productId}/")
 public Product getProduct(@PathParam("productId") int productId)
 {
  System.out.println("----invoking getProduct with id: " + productId);
  Product p = products.get(new Long(productId));
  return p;
 }

 final void init()
 {
  Product p = new Product();
  p.setId(323);
  p.setDescription("product 323");
  products.put(p.getId(), p);
 }

 // this is the method enables the addition of more than one product,
through the use of
 // parameters, rather than through hard-coding them into init.
 final void init(long productID)
 {
  Product p = new Product();
  long pID = productID;
  p.setId(pID);
  p.setDescription("product " + pID);
  System.out.println(p.getDescription());
  products.put(p.getId(), p);
 }
-----------------------------------------------------------------------------------------------

So, initially, these are the improvements that I made to the 'basic' http
demo, when I first
downloaded it and went over it.

As far as the @XmlRootElement annotations go, the Server returns an xml
element, when queried,
but the only times that the element is touched are when it is instantiated,
through the use
of its corresponding class, and when it is accessed, in the first case, via
the GET method
of the CustomerService class, upon receiving a URL from the client, locating
that class,
through means of the @Path annotation.

Here is the code from the CustomerService class, that I will discuss:

-----------------------------------------------------------------------------------------------
...
@Path("/customerservice/")
public class CustomerService {
...
    @GET
    @Path("/customers/{id}/")
    public Customer getCustomer(@PathParam("id") String id) {
        System.out.println("----invoking getCustomer, Customer id is: " +
id);
        long idNumber = Long.parseLong(id);
        Customer c = customers.get(idNumber);
        return c;
    }
...
-----------------------------------------------------------------------------------------------

As you saw, Customer is annotated with the @XmlRootElement annotation. In
the Java EE api
documentation, it states:

"When a top level class or an enum type is annotated with the
@XmlRootElement annotation,
then its value is represented as XML element in an XML document."

I'm not sure what mechanism java EE uses to ensure this, but I think that
this annotation
is the reason why that when the CustomerService object returns a Customer
object, that
the Service returns the Object in XML format, through the open URL
connection.

That is all for now. I intend to make small steps towards building upon this
in the future. I hope
that this will set a good backdrop for discussion and me to ask questions,
if necessary.

Ryan

Re: Basic Http Demo and the refactoring that I did, in the course of figuring out how it worked.

Posted by Sergey Beryozkin <sb...@gmail.com>.
Hi Ryan

This all very good and I hope the CXF dev community will be happy to
see your progress and help when needed,
please see some comments inline.

Please keep updating us with your progress as often as possible, just
one suggestion is to minimize the amount of copied code or traces,
etc, copy the most relevant parts and send the complete code to me
privately. That will help to keep messages shorter and for others to
follow them

so, more comments inline, I removed the actual code as it can be seen
in the prev message if needed

thanks, Sergey

On Thu, May 12, 2011 at 7:32 AM, Ryan Zoerner <ry...@gmail.com> wrote:


<snip/>
> JAX_RS/demo
>
> At cxf/distribution/src/main/release/samples/jax_rs/basic
> (
> http://svn.apache.org/viewvc/cxf/trunk/distribution/src/main/release/samples/jax_rs/basic/)
> is a basic http demo. I found the client code to be large and unwieldly, so
> I refactored
Good :-) That is the old code which needs to be replaced by CXF JAX-RS
WebClient related code and then in due time by JAX-RS 2.0 client api
code.


> it. I also found it hard to understand, at first, but the key, with the GET
> request is
> that the client opens a connection with the server, at the URI associated
> with a given
> resource. The client opens a printStream that is associated with the
> connection, and
> from that connection the client obtains the return information that would
> normally be
> associated with a GET request. It may be that since the only @Path in
> CustomerService
> with "/customerservice/customers/{id}" is the GET method, that the service
> automatically
> just queries that @Path and assumes a GET request, since that method is
> annotated with
> the @GET annotation. If you annotated it with more than one HTTP
> RequestType, I am unsure
> what would happen at the moment.

A given JAX-RS resource method can only have a single HTTP method
annotation, I think CXF will just use the 1st one on a method and
ignore others - should also validate and throw
ServiceCreationExceptions...

> That said, here is the refactoring part of
> the code.
>

All the code looked fined to me, good effort

> "When a top level class or an enum type is annotated with the
> @XmlRootElement annotation,
> then its value is represented as XML element in an XML document."
>
> I'm not sure what mechanism java EE uses to ensure this, but I think that
> this annotation
> is the reason why that when the CustomerService object returns a Customer
> object, that
> the Service returns the Object in XML format, through the open URL
> connection.
>

CXF JAX-RS provides JAXBElementProvider which implements JAX-RS
MessageBodyWriter and MessageBodyReader and uses JAXB databinding
technology to have @XmlRootElement annotated class instances written
to the output stream and initialized from the input stream containing
the XML sequence


> That is all for now. I intend to make small steps towards building upon this
> in the future. I hope
> that this will set a good backdrop for discussion and me to ask questions,
> if necessary.
>
That is good, thanks

Sergey

> Ryan
>



-- 
Sergey Beryozkin

Application Integration Division of Talend
http://sberyozkin.blogspot.com

Re: Basic Http Demo and the refactoring that I did, in the course of figuring out how it worked.

Posted by Ryan Zoerner <ry...@gmail.com>.
Cheers for the suggestion.

Ryan


On Thu, May 12, 2011 at 4:47 AM, Sergey Beryozkin <sb...@gmail.com>wrote:

> Hi Ryan
>
> On Thu, May 12, 2011 at 10:42 AM, Ryan Zoerner <ry...@gmail.com>
> wrote:
> > In the paragraph, with the hyperlink to the jaxrs demo, I said this:
> >
> >
> -------------------------------------------------------------------------------------------
> >
> > The client opens a printStream that is associated with the connection,
> and
> > from that connection the client obtains the return information that would
> > normally be
> > associated with a GET request. It may be that since the only @Path in
> > CustomerService
> > with "/customerservice/customers/{id}" is the GET method, that the
> service
> > automatically
> > just queries that @Path and assumes a GET request, since that method is
> > annotated with
> > the @GET annotation. If you annotated it with more than one HTTP
> > RequestType, I am unsure
> > what would happen at the moment. That said, here is the refactoring part
> of
> > the code.
> >
> -------------------------------------------------------------------------------------------
> >
> >
> > However, I found this, in a stack trace:
> >
> -------------------------------------------------------------------------------------------
> > May 12, 2011 4:29:04 AM org.apache.cxf.jaxrs.utils.JAXRSUtils
> > findTargetMethod
> > WARNING: No operation matching request path /customer/123 is found,
> > HTTP Method : GET, ContentType : */*,
> > Accept :
> text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,.
> >
> -------------------------------------------------------------------------------------------
> >
> > Apparently the findTargetMethod(?); method named above
> > takes care of selecting the HTTP method, at least in this case, based
> upon
> > what url is received. But I was right, no explicit GET method is sent,
> the
> > application simply selects, based upon input, which method to assume, or
> > use.
> >
> No, HTTP method is always sent by the client code, it is just in your
> case, when you do
> url.openStream() or similar, it's defaulted to GET.
>
> By the way, enabling the FINE logging level on the server side will
> let you see more details about the selection process
>
> Cheers, Sergey
>
> > Ryan
> >
>

Re: Basic Http Demo and the refactoring that I did, in the course of figuring out how it worked.

Posted by Sergey Beryozkin <sb...@gmail.com>.
Hi Ryan

On Thu, May 12, 2011 at 10:42 AM, Ryan Zoerner <ry...@gmail.com> wrote:
> In the paragraph, with the hyperlink to the jaxrs demo, I said this:
>
> -------------------------------------------------------------------------------------------
>
> The client opens a printStream that is associated with the connection, and
> from that connection the client obtains the return information that would
> normally be
> associated with a GET request. It may be that since the only @Path in
> CustomerService
> with "/customerservice/customers/{id}" is the GET method, that the service
> automatically
> just queries that @Path and assumes a GET request, since that method is
> annotated with
> the @GET annotation. If you annotated it with more than one HTTP
> RequestType, I am unsure
> what would happen at the moment. That said, here is the refactoring part of
> the code.
> -------------------------------------------------------------------------------------------
>
>
> However, I found this, in a stack trace:
> -------------------------------------------------------------------------------------------
> May 12, 2011 4:29:04 AM org.apache.cxf.jaxrs.utils.JAXRSUtils
> findTargetMethod
> WARNING: No operation matching request path /customer/123 is found,
> HTTP Method : GET, ContentType : */*,
> Accept : text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,.
> -------------------------------------------------------------------------------------------
>
> Apparently the findTargetMethod(?); method named above
> takes care of selecting the HTTP method, at least in this case, based upon
> what url is received. But I was right, no explicit GET method is sent, the
> application simply selects, based upon input, which method to assume, or
> use.
>
No, HTTP method is always sent by the client code, it is just in your
case, when you do
url.openStream() or similar, it's defaulted to GET.

By the way, enabling the FINE logging level on the server side will
let you see more details about the selection process

Cheers, Sergey

> Ryan
>

Re: Basic Http Demo and the refactoring that I did, in the course of figuring out how it worked.

Posted by Ryan Zoerner <ry...@gmail.com>.
In the paragraph, with the hyperlink to the jaxrs demo, I said this:

-------------------------------------------------------------------------------------------

The client opens a printStream that is associated with the connection, and
from that connection the client obtains the return information that would
normally be
associated with a GET request. It may be that since the only @Path in
CustomerService
with "/customerservice/customers/{id}" is the GET method, that the service
automatically
just queries that @Path and assumes a GET request, since that method is
annotated with
the @GET annotation. If you annotated it with more than one HTTP
RequestType, I am unsure
what would happen at the moment. That said, here is the refactoring part of
the code.
-------------------------------------------------------------------------------------------


However, I found this, in a stack trace:
-------------------------------------------------------------------------------------------
May 12, 2011 4:29:04 AM org.apache.cxf.jaxrs.utils.JAXRSUtils
findTargetMethod
WARNING: No operation matching request path /customer/123 is found,
HTTP Method : GET, ContentType : */*,
Accept : text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,.
-------------------------------------------------------------------------------------------

Apparently the findTargetMethod(?); method named above
takes care of selecting the HTTP method, at least in this case, based upon
what url is received. But I was right, no explicit GET method is sent, the
application simply selects, based upon input, which method to assume, or
use.

Ryan