Thursday 11 April 2013

Restful Web Services example using CXF , JAXRS.

A web service is a method of communication between multiple devices over the World Wide Web. Using Web Services we can publish application's functions to everyone. In the web services world, REpresentational State Transfer (REST) is a key design idiom that embraces a stateless client-server architecture in which the web services are viewed as resources and can be identified by their URLs. This basic REST design principle establishes a one-to-one mapping between create, read, update, and delete (CRUD) operations and HTTP methods.
    • To create a resource on the server, use POST.
    • To retrieve a resource, use GET.
    • To change the state of a resource or to update it, use PUT.
    • To remove or delete a resource, use DELETE.
More details about RESTFul web services please go to references section of this blog.
This Blog instruction will help us to create a sample RESTful webservice using cxf and jaxrs api.

  • Required Software
  • Pre-requsite
  • Instructions assume that jdk,jboss,cxf is installed without any error.

  • Instructions
    1. Create a dynamic web project in eclipse.
















    2. Create a file name customer.xsd under /WEB-INF and use the following xml snippet.

    3. <?xml version="1.0" encoding="UTF-8"?>
      <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
          <xsd:complexType name="customer">
              <xsd:sequence>
                  <xsd:element name="address" type="address" />
                  <xsd:element name="customerId" type="xsd:long" />
                  <xsd:element name="customerName" type="xsd:string" />
                  <xsd:element name="phone" type="xsd:long" />
              </xsd:sequence>
          </xsd:complexType>
          <xsd:complexType name="address">
              <xsd:sequence>
                  <xsd:element name="street" type="xsd:string" />
                  <xsd:element name="city" type="xsd:string" />
                  <xsd:element name="state" type="xsd:string" />
                  <xsd:element name="country" type="xsd:string" />
                  <xsd:element name="pincode" type="xsd:long" />
              </xsd:sequence>
          </xsd:complexType>
      </xsd:schema>
      


















    4. Generate Jaxb classes from customer.xsd using eclipse.
    5. After execution of above steps com.kaustuv.jaxrs.example.vo package will be created which contains three Jaxb generated classes. Address.java , Customer.java , ObjectFactory.


































    6. Add @XmlRootElement to both jaxb generated classes. please have a look into following code snippets.

    7. Address.java
      package com.kaustuv.jaxrs.example.vo;
      
      import javax.xml.bind.annotation.XmlAccessType;
      import javax.xml.bind.annotation.XmlAccessorType;
      import javax.xml.bind.annotation.XmlElement;
      import javax.xml.bind.annotation.XmlRootElement;
      import javax.xml.bind.annotation.XmlType;
      
      
      /**
       * <p>Java class for address complex type.
       * 
       * <p>The following schema fragment specifies the expected content contained within this class.
       * 
       * <pre>
       * &lt;complexType name="address">
       *   &lt;complexContent>
       *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
       *       &lt;sequence>
       *         &lt;element name="street" type="{http://www.w3.org/2001/XMLSchema}string"/>
       *         &lt;element name="city" type="{http://www.w3.org/2001/XMLSchema}string"/>
       *         &lt;element name="state" type="{http://www.w3.org/2001/XMLSchema}string"/>
       *         &lt;element name="country" type="{http://www.w3.org/2001/XMLSchema}string"/>
       *         &lt;element name="pincode" type="{http://www.w3.org/2001/XMLSchema}long"/>
       *       &lt;/sequence>
       *     &lt;/restriction>
       *   &lt;/complexContent>
       * &lt;/complexType>
       * </pre>
       * 
       * 
       */
      @XmlRootElement(name="address")
      @XmlAccessorType(XmlAccessType.FIELD)
      @XmlType(name = "address", propOrder = {
          "street",
          "city",
          "state",
          "country",
          "pincode"
      })
      public class Address {
      
          @XmlElement(required = true)
          protected String street;
          @XmlElement(required = true)
          protected String city;
          @XmlElement(required = true)
          protected String state;
          @XmlElement(required = true)
          protected String country;
          protected long pincode;
      
          /**
           * Gets the value of the street property.
           * 
           * @return
           *     possible object is
           *     {@link String }
           *     
           */
          public String getStreet() {
              return street;
          }
      
          /**
           * Sets the value of the street property.
           * 
           * @param value
           *     allowed object is
           *     {@link String }
           *     
           */
          public void setStreet(String value) {
              this.street = value;
          }
      
          /**
           * Gets the value of the city property.
           * 
           * @return
           *     possible object is
           *     {@link String }
           *     
           */
          public String getCity() {
              return city;
          }
      
          /**
           * Sets the value of the city property.
           * 
           * @param value
           *     allowed object is
           *     {@link String }
           *     
           */
          public void setCity(String value) {
              this.city = value;
          }
      
          /**
           * Gets the value of the state property.
           * 
           * @return
           *     possible object is
           *     {@link String }
           *     
           */
          public String getState() {
              return state;
          }
      
          /**
           * Sets the value of the state property.
           * 
           * @param value
           *     allowed object is
           *     {@link String }
           *     
           */
          public void setState(String value) {
              this.state = value;
          }
      
          /**
           * Gets the value of the country property.
           * 
           * @return
           *     possible object is
           *     {@link String }
           *     
           */
          public String getCountry() {
              return country;
          }
      
          /**
           * Sets the value of the country property.
           * 
           * @param value
           *     allowed object is
           *     {@link String }
           *     
           */
          public void setCountry(String value) {
              this.country = value;
          }
      
          /**
           * Gets the value of the pincode property.
           * 
           */
          public long getPincode() {
              return pincode;
          }
      
          /**
           * Sets the value of the pincode property.
           * 
           */
          public void setPincode(long value) {
              this.pincode = value;
          }
      }
      

      Customer.java
      package com.kaustuv.jaxrs.example.vo;
      
      import javax.xml.bind.annotation.XmlAccessType;
      import javax.xml.bind.annotation.XmlAccessorType;
      import javax.xml.bind.annotation.XmlElement;
      import javax.xml.bind.annotation.XmlRootElement;
      import javax.xml.bind.annotation.XmlType;
      
      
      /**
       * <p>Java class for customer complex type.
       * 
       * <p>The following schema fragment specifies the expected content contained within this class.
       * 
       * <pre>
       * &lt;complexType name="customer">
       *   &lt;complexContent>
       *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
       *       &lt;sequence>
       *         &lt;element name="address" type="{}address"/>
       *         &lt;element name="customerId" type="{http://www.w3.org/2001/XMLSchema}long"/>
       *         &lt;element name="customerName" type="{http://www.w3.org/2001/XMLSchema}string"/>
       *         &lt;element name="phone" type="{http://www.w3.org/2001/XMLSchema}long"/>
       *       &lt;/sequence>
       *     &lt;/restriction>
       *   &lt;/complexContent>
       * &lt;/complexType>
       * </pre>
       * 
       * 
       */
      @XmlRootElement(name="customer")
      @XmlAccessorType(XmlAccessType.FIELD)
      @XmlType(name = "customer", propOrder = {
          "address",
          "customerId",
          "customerName",
          "phone"
      })
      public class Customer {
      
          @XmlElement(required = true)
          protected Address address;
          protected long customerId;
          @XmlElement(required = true)
          protected String customerName;
          protected long phone;
      
          /**
           * Gets the value of the address property.
           * 
           * @return
           *     possible object is
           *     {@link Address }
           *     
           */
          public Address getAddress() {
              return address;
          }
      
          /**
           * Sets the value of the address property.
           * 
           * @param value
           *     allowed object is
           *     {@link Address }
           *     
           */
          public void setAddress(Address value) {
              this.address = value;
          }
      
          /**
           * Gets the value of the customerId property.
           * 
           */
          public long getCustomerId() {
              return customerId;
          }
      
          /**
           * Sets the value of the customerId property.
           * 
           */
          public void setCustomerId(long value) {
              this.customerId = value;
          }
      
          /**
           * Gets the value of the customerName property.
           * 
           * @return
           *     possible object is
           *     {@link String }
           *     
           */
          public String getCustomerName() {
              return customerName;
          }
      
          /**
           * Sets the value of the customerName property.
           * 
           * @param value
           *     allowed object is
           *     {@link String }
           *     
           */
          public void setCustomerName(String value) {
              this.customerName = value;
          }
      
          /**
           * Gets the value of the phone property.
           * 
           */
          public long getPhone() {
              return phone;
          }
      
          /**
           * Sets the value of the phone property.
           * 
           */
          public void setPhone(long value) {
              this.phone = value;
          }
      }
      

      ObjectFactory
      package com.kaustuv.jaxrs.example.vo;
      
      import javax.xml.bind.annotation.XmlRegistry;
      
      
      /**
       * This object contains factory methods for each 
       * Java content interface and Java element interface 
       * generated in the com.kaustuv.jaxrs.example.vo package. 
       * <p>An ObjectFactory allows you to programatically 
       * construct new instances of the Java representation 
       * for XML content. The Java representation of XML 
       * content can consist of schema derived interfaces 
       * and classes representing the binding of schema 
       * type definitions, element declarations and model 
       * groups.  Factory methods for each of these are 
       * provided in this class.
       * 
       */
      @XmlRegistry
      public class ObjectFactory {
      
      
          /**
           * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: com.kaustuv.jaxrs.example.vo
           * 
           */
          public ObjectFactory() {
          }
      
          /**
           * Create an instance of {@link Customer }
           * 
           */
          public Customer createCustomer() {
              return new Customer();
          }
      
          /**
           * Create an instance of {@link Address }
           * 
           */
          public Address createAddress() {
              return new Address();
          }
      }
      

    8. Open web.xml under WEB-INF\web.xml and add following xml snippet


    9. <?xml version="1.0" encoding="UTF-8"?>
      <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xmlns="http://java.sun.com/xml/ns/javaee"
      xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
      http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
      id="WebApp_ID" 
      version="2.5">
          <display-name>jaxrs-example</display-name>
          <servlet>
              <description>Apache CXF Endpoint</description>
              <display-name>cxf</display-name>
              <servlet-name>cxf</servlet-name>
              <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
              <load-on-startup>1</load-on-startup>
          </servlet>
          <servlet-mapping>
              <servlet-name>cxf</servlet-name>
              <url-pattern>/*</url-pattern>
          </servlet-mapping>
          <session-config>
              <session-timeout>60</session-timeout>
          </session-config>
          <context-param>
              <param-name>contextConfigLocation</param-name>
              <param-value>WEB-INF/conf/*beans.xml</param-value>
          </context-param>
          <listener>
              <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
          </listener>
      </web-app>
      


    10. Create a new package "com.kaustuv.jaxrs.example.service"

    11. create a new service interface name DemoCustomerService under package "com.kaustuv.jaxrs.example.service". Add following code snippet.

    12. /**
      *
      * Copyright (c) Kaustuv Maji , 2013
      * Repos - https://github.com/kaustuvmaji
      * Blog -  http://kaustuvmaji.blogspot.in 
      *
      */
      package com.kaustuv.jaxrs.example.service;
      
      import javax.ws.rs.DELETE;
      import javax.ws.rs.GET;
      import javax.ws.rs.POST;
      import javax.ws.rs.PUT;
      import javax.ws.rs.Path;
      import javax.ws.rs.QueryParam;
      import javax.ws.rs.core.Response;
      
      import com.kaustuv.jaxrs.example.vo.Customer;
      
      /**
       * This is rest services interface .
       * Methods  
       *     i.   Add customer               {@link #addCustomer(Customer)}.
       *     ii.  Get customer by customerId {@link #getCustomerById(long)}.
       *     iii. Update customer            {@link #updateCustomer(Customer)}.
       *     iv.  Delete customer            {@link #deleteCustomer(long)}.
       *     
       * @since   1.0
       * @version 1.0
       * 
       * @author KMaji
       *
       */
      public interface DemoCustomerService {
      
        /**
         * This method is used to demonstrate http method GET.
         * 
         * @param custId will be accepted as Queryparam. 
         * @return response
         */
        @GET
        @Path("/getCustomerById")
        public Response getCustomerById(@QueryParam("custId") long custId);
      
        /**
         * This method is used to demonstrate http method POST.
         * @param customer
         * @return response
         */
        @POST
        @Path("/addCustomer")
        public Response addCustomer(Customer customer);
      
        /**
         * This method is used to demonstrate Http method DELETE.
         * @param custId will be accepted as Queryparam. 
         * @return response
         */
        @DELETE
        @Path("/deleteCustomer")
        public Response deleteCustomer(@QueryParam("custId") long custId);
      
        /**
         * This method is to demonstrate http method PUT.
         * 
         * @param customer
         * @return response
         */
        @PUT
        @Path("/updateCustomer")
        public Response updateCustomer(Customer customer);
      }
      

    13. Create DemoCustomerServiceImpl under package "com.kaustuv.jaxrs.example.service". Add following code snippet

    14. /**
      |
      | Copyright (c) Kaustuv Maji , 2013
      | 
      | Please do not use source code in production.
      | Repos -  https://github.com/kaustuvmaji
      | Blog  -  http://kaustuvmaji.blogspot.in 
      */
      package com.kaustuv.jaxrs.example.service;
      
      import java.util.HashMap;
      import java.util.Map;
      
      import javax.ws.rs.core.Context;
      import javax.ws.rs.core.Response;
      import javax.ws.rs.core.UriInfo;
      
      import org.apache.log4j.Logger;
      
      import com.kaustuv.jaxrs.example.vo.Address;
      import com.kaustuv.jaxrs.example.vo.Customer;
      
      /**
       * This implementation class is to implement rest service interface.
       * Methods  
       *     --   Add customer               {@link #addCustomer(Customer)}.
       *     --   Get customer by customerId {@link #getCustomerById(long)}.
       *     --   Update customer            {@link #updateCustomer(Customer)}.
       *     --   Delete customer            {@link #deleteCustomer(long)}.
       *     --   init                       {@link #init()}. 
       *     --   destory                    {@link #destroy()}.
       *     
       * @since   1.0
       * @version 1.0
       * 
       * @author KMaji
       *
       */
      public class DemoCustomerServiceImpl implements DemoCustomerService {
      
        @Context
        UriInfo uriInfo;
      
        private final static String ACCESS_CONTROL_ALLOW_ORIGIN = "*";
      
        private Logger log;
      
        private Map<Long, Customer> customers;
      
        public DemoCustomerServiceImpl() {
          log = Logger.getLogger(DemoCustomerServiceImpl.class);
          customers = new HashMap<Long, Customer>();
        }
      
        public Response getCustomerById(long custId) {
          log.info("Executing getCustomerById operation");
          if (!customers.containsKey(custId)) {
            return Response.status(Response.Status.BAD_REQUEST)
                           .location(uriInfo.getAbsolutePath())
                           .entity("customer not found")
                           .header("Access-Control-Allow-Origin", ACCESS_CONTROL_ALLOW_ORIGIN)
                           .build();
          }
          return Response.ok(uriInfo.getAbsolutePath())
                         .entity(customers.get(custId))
                         .header("Access-Control-Allow-Origin", ACCESS_CONTROL_ALLOW_ORIGIN)
                         .build();
        }
      
        public Response addCustomer(Customer customer) {
          log.info("Executing Add Customer operation");
          if (customers.containsKey(customer.getCustomerId())) {
           return Response.status(Response.Status.BAD_REQUEST)
                    .location(uriInfo.getAbsolutePath())
                    .entity("Exsisting customer found with same id.")
                    .header("Access-Control-Allow-Origin", ACCESS_CONTROL_ALLOW_ORIGIN)
                    .build();
          }
          customers.put(customer.getCustomerId(), customer);
          log.info("Added new customer with customer id" + customer.getCustomerId());
          return Response.ok(uriInfo.getAbsolutePath())
                         .entity(customer)
                         .header("Access-Control-Allow-Origin", ACCESS_CONTROL_ALLOW_ORIGIN)
                         .build();
        }
      
        public Response deleteCustomer(long custId) {
          log.info("Executing Delete Customer operation");
          if (!customers.containsKey(custId)) {
           return  Response.status(Response.Status.BAD_REQUEST)
                    .location(uriInfo.getAbsolutePath())
                    .entity("customer not found")
                    .header("Access-Control-Allow-Origin", ACCESS_CONTROL_ALLOW_ORIGIN)
                    .build();
          }
          customers.remove(custId);
          log.info("Removed customer contains customerid " + custId);
          return Response.ok(uriInfo.getAbsolutePath())
                         .entity("Removed customer contains customerid " + custId)
                         .header("Access-Control-Allow-Origin", ACCESS_CONTROL_ALLOW_ORIGIN)
                         .build();
        }
      
        public Response updateCustomer(Customer customer) {
          log.info("Executing update Customer operation");
          if (!customers.containsKey(customer.getCustomerId())) {
           return Response.status(Response.Status.BAD_REQUEST)
                    .location(uriInfo.getAbsolutePath())
                    .entity("customer not found")
                    .header("Access-Control-Allow-Origin", ACCESS_CONTROL_ALLOW_ORIGIN)
                    .build();
          }
          customers.put(customer.getCustomerId(), customer);
          return Response.ok(uriInfo.getAbsolutePath())
                         .entity(customer)
                         .header("Access-Control-Allow-Origin", ACCESS_CONTROL_ALLOW_ORIGIN)
                         .build();
        }
      
        /**
         * Init method is used to prepare data which we 
         * will use for testing purpose.
         * usage of this method is defined in bean definition.
         */
        public void init() {
          Customer cus1 = new Customer();
          cus1.setCustomerId(1l);
          cus1.setCustomerName("Kaustuv Maji");
          cus1.setPhone(9830098300l);
          Address add = new Address();
          add.setStreet("saltlake");
          add.setCity("kolkata");
          add.setState("westbengal");
          add.setCountry("India");
          add.setPincode(700106);
          cus1.setAddress(add);
          customers.put(cus1.getCustomerId(), cus1);
          log.info("Added " + cus1.getCustomerName() + " into Customer info");
          Customer cus2 = new Customer();
          cus2.setCustomerId(2l);
          cus2.setCustomerName("customer 2");
          customers.put(cus2.getCustomerId(), cus2);
          log.info("Added " + cus2.getCustomerName() + " into Customer info");
          log.info("Customer info map contains " + customers.size() + " details");
        }
      
        /**
         * destroy method is also used after our testing purpose is done.
         * usage of this method is defined in bean definition.
         */
        public void destroy() {
          log.info("Cleaning up customers info map");
          customers.clear();
        }
      }
      

    15. Create a xml file name global-beans.xml under WEB-INF/conf
    16. <beans xmlns="http://www.springframework.org/schema/beans" 
      xmlns:cache="http://www.springframework.org/schema/cache" 
      xmlns:p="http://www.springframework.org/schema/p"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:cxf="http://cxf.apache.org/core" 
      xmlns:jaxrs="http://cxf.apache.org/jaxrs"
      xsi:schemaLocation="
      http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://cxf.apache.org/core 
      http://cxf.apache.org/schemas/core.xsd
      http://cxf.apache.org/jaxrs
      http://cxf.apache.org/schemas/jaxrs.xsd"
          default-lazy-init="false">
          <import resource="classpath:META-INF/cxf/cxf*.xml" />
          <!-- Cxf Jason provider -->
          <bean id="jsonProvider" class="org.apache.cxf.jaxrs.provider.JSONProvider">
              <property name="dropRootElement" value="true" />
              <property name="supportUnwrapped" value="true" />
          </bean>
          <!-- Cxf Jaxb Provider -->
          <bean id="jaxbXmlProvider" class="org.apache.cxf.jaxrs.provider.JAXBElementProvider" />
          <!-- Cxf Cors filter (cross domain purpose)-->
          <bean id="cors-filter" class="org.apache.cxf.jaxrs.cors.CrossOriginResourceSharingFilter"/>
      </beans>
      

    17. Create a xml name jaxrs-beans.xml under WEB-INF/conf

    18. <?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.xsd
      http://cxf.apache.org/core 
      http://cxf.apache.org/schemas/core.xsd
      http://cxf.apache.org/jaxrs
      http://cxf.apache.org/schemas/jaxrs.xsd"
          default-lazy-init="false">
          <!-- Service -->
          <bean id="customerService" class="com.kaustuv.jaxrs.example.service.DemoCustomerServiceImpl" 
          init-method="init" 
          destroy-method="destroy" />
          <!-- Jaxrs Server -->
          <jaxrs:server id="restserver" address="/customerService">
              <!-- adding service bean -->
              <jaxrs:serviceBeans>
                  <ref bean="customerService" />
              </jaxrs:serviceBeans>
              <!-- adding media type provider -->
              <jaxrs:providers>
                  <ref bean="jsonProvider" />
                  <ref bean="jaxbXmlProvider" />
                  <ref bean="cors-filter" />
              </jaxrs:providers>
              <jaxrs:features>
                  <cxf:logging/>
              </jaxrs:features>
              <!-- Keeping extention type -->
              <jaxrs:extensionMappings>
                  <entry key="json" value="application/json" />
                  <entry key="xml" value="application/xml" />
              </jaxrs:extensionMappings>
          </jaxrs:server>
      </beans>
      

    19. Export and package this as war. Destination path %JBOSS_HOME%\server\default

























    20. Open command prompt and go to directory %JBOSS_HOME\bin .
    21. Execute run.bat -c default -b 0.0.0.0
    22. click on our wadl url for our restful webservice project.
    23. http://localhost:8080/jaxrs-example/customerService?_wadl






























      • Post Development testing

    1. open soap ui and create a new project using our wadl url.
    2. http://localhost:8080/jaxrs-example/customerService?_wadl



      Test Http GET Method
    3. Explore soap ui project name RestCustomerService. Select Request of service getCustomerById
    4. enter value of parameter custId in following example picture value is 2 . click on submit



    5. Test Http POST Method
    6. Explore soap ui project name RestCustomerService. Select Request of service addCustoer
    7. enter customer xml use following snippet and click on submit .
    8. <customer>
      <customerId>3</customerId>
      <customerName>testPost</customerName>
      <phoneNumber>9831098301</phoneNumber>
      <address>
      <street>saltlake</street>
      <city>kolkata</city>
      <state>westbengal</state>
      <country>India</country>
      <pincode>700064</pincode>
      </address>
      </customer>

       





      Test Http PUT Method
    9. Explore soap ui project name RestCustomerService. Select Request of service updateCustomer
    10. enter customer xml use following snippet and click on submit .

    11. <customer>
      <customerId>3</customerId>
      <customerName>testUpdate</customerName>
      <phoneNumber>9831098301</phoneNumber>
      <address>
      <street>saltlake</street>
      <city>kolkata</city>
      <state>westbengal</state>
      <country>India</country>
      <pincode>700064</pincode>
      </address>
      </customer>

       




      Test Http DELETE Method
    12. Explore soap ui project name RestCustomerService. Select Request of service deleteCustomer
    13. enter value of parameter custId in following example picture value is 3 . click on submit .


    Above instruction is also compatible with jboss 5.1.* series for deployment. Remove xml-beans.jar and geronimo-servlet_2.5_spec-1.1.2.jar due to incompatibility of slf4j version we need to remove slf4j-api-1.6.2.jar and slf4j-jdk14-1.6.2.jar from your web application packaging path (WEB-INF\lib).
    Click here to download source code of above example

    Source Code


    • References:
    CXF documents

    European Union laws require you to give European Union visitors information about cookies used on your blog. In many cases, these laws also require you to obtain consent.

    As a courtesy, we have added a notice on your blog to explain Google's use of certain Blogger and Google cookies, including use of Google Analytics and AdSense cookies.

    No comments:

    Post a Comment