Saturday, November 20, 2010

Do we need database constraints?

I noticed that the DB2 system belong to one project I am working on does not have any constraints set up. One of my colleagues thought it is too bad that the database does not have constraints like foreign key etc. Do we need database constraints in our system excepts the primary key, and index constraints? Well, I think there is different answers for different people who plays different roles in a software development life cycle.

According to my understanding, the constraints in relational database is constraints set up by Database Designer for Application Developer. Database designer set up the foreign key constraints, null constraints, check constraints etc. Then, developer must follow the design to avoid break constraints.

Properly setting up constraints will be helpful if developer want to use some OR mapping tools to automatically generating database abstract layer. Or, it is useful if the application will depends on RDBMS to cascade delete records. However, I do agree that it could be harmless to remove constraints like foreign key in the product environment if the application takes care of natural key very well to avoid having orphan records. I think it could pay us some performance increasing and computer resource usage decreasing as return. Probably this is the reason for MySQL did not implement foreign key constraints when it is first invented and it still become widely used?

Wednesday, November 17, 2010

Do we need WSDL to describe a RESTful Web service?

Someone shared an article written by IBM engineer (link). They want to use WSDL2.0 to describe the RESTful Web Service. I do not think it is necessary to use WSDL2.0 to describe RESTful service although WSDL2.0 can do that. At least, it is too early to apply this idea into real project before it is widely adopted in industry and code generating tool is given.

First of all, one of major reason for us to use RESTful is that RESTful is simpler than SOAP. In other words, RESTful is more about expose resource through Web server. SOAP based WS serves more as RPC. RESTful and SOAP have different design styles and they focus on different application domain. RESTful is mainly used to execute CRUD methods over HTTP with verb POST, GET, PUT, DELETE respectively. So, RESTful did not use WSDL to describe its service from the begin.

Secondly, there is no handy tool like WSDL2Java or WSDL2PHP for converting RESTful WSDL to be a computing code. Axis2 probably convert RESTful request into WSDL internally before it will further process the request. But, this happens internally.

Thirdly, since RESTful is simple. We can mainly use totally four diagrams to describe a RESTful service. Since we can make a human reading friendly document, why do we need to "go back" to WSDL, which is not fully understood by many programmer. Actually, SOAP WS programmer can still code before he fully understands the WSDL as he has handy tools to help him. We can probably call WSDL as machine reading language. Below is a simple diagram example to describe a RESTful GET method to a resource.


Therefore, I do not think it is good idea to describe RESTful WS in WSDL as WSDL is a kind of machine reading language. We do not need WSDL for RESTful before we get code generating tool for converting WSDL2.0 to to real code and code to WSDL2.0.

Also, it will misguide if designer treat RESTful as a RPC. For my opinion, a designer can treat RESTful WS as static Web content from a Web server. SOAP (WSDL) based Web service services as RPC more. Therefore, a designer can see whether he can model the application as a resource or not as a start point to think about using RESTful or SOAP or other RPC protocols. So, not to think RESTful WS as a RPC will definitely help Web Service designer to keep in right rack.

Tuesday, November 9, 2010

creating RESTful service with Zend_Rest_Route and Zend_Rest_Controller

After trying different ways to make RESTful WS in Zend Framework, I am aware that I can simply use Zend_Rest_Route and ZendRest_Controller to make "standard" RESTful WS in PHP. Especially, Zend_Rest_Route supports RESTful URI pattern like /parameterName/parameterValue/.... This is different from what I read from forums. I am using Zend Framework 1.10 in Zend Server 5.0.1 running on IBM i. Zend_Rest_Server is really not good choice to make RESTful service. Zend_Rest_Server needs to be further improved to meet the requirement of "standard" RESTful.

I tried Zend_Controller_Router_Route to implemented RESTful URI pattern. But, it is easier to use Zend_Rest_Route and Zend_Rest_Controller. Actually, Zend_Rest_Controller does nothing but defines several abstract methods, which map to relevant HTTP methods. But, our RESTful controller must extend from it. Below is the sample codes. In my environment, I can access it with this URL: http://localhost/jiaRESTfulWS/public/index/product/fish/number/100/

BootStrap class
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
 //adding this function in Bootstrap class to initilize Zend_Rest_Route.
 protected function _initRestRoute() {
  //getting an instance of zend front controller.
  $frontController = Zend_Controller_Front::getInstance ();
  //initializing a Zend_Rest_Route
  $restRoute = new Zend_Rest_Route ( $frontController );
  //let all actions to use Zend_Rest_Route.
  $frontController->getRouter ()->addRoute ( 'default', $restRoute );
 }
 
}

IndexController.php
class IndexController extends Zend_Rest_Controller
{
 
 public function init() {
  $this->getHelper ( 'viewRenderer' )->setNoRender ( true ); 
 }
 
 /**
  * The index action handles index/list requests; it should respond with a
  * list of the requested resources.
  */
 public function indexAction() {
  //HTTP code 500 might not good choice here.
  $this->getResponse ()->setHttpResponseCode ( 500 );
  $this->getResponse ()->appendBody ( "no list/index allowed" );
  
 } 
 
 /**
  * The get action handles GET requests and receives an 'id' parameter; it
  * should respond with the server resource state of the resource identified
  * by the 'id' value.
  */
 public function getAction() {  
  
  //I will return result in XML format.
  $this->getResponse ()->setHeader ( 'Content-Type', 'text/xml' );
  
  //Note: the Request object here is not HttpRequest. It is Zend controller request. This is the key!
  if ($this->getRequest ()->getParam ( "product" ) != NULL and $this->getRequest ()->getParam ( "number") != NULL ) {   
   //Initializing a dummy object for return. 
   $return = new Jia_Return ();
   $return->setProducts ( $this->getRequest ()->getParam ( "product" ) );
   $return->setQuantity ( $this->getRequest ()->getParam ( "number" ) );
   //We prevent the product has been found.
   //So, we set HTTP code 200 here.  
   $this->getResponse ()->setHttpResponseCode ( 200 );
  }    
   else {
    $return= new Jia_ErrorCode('no parameters!');
    //prevent the product is not found.
    $this->getResponse ()->setHttpResponseCode ( 200 );
   }
  
  print $this->_handleStruct( $return );
 
 }
 
 /**
  * The post action handles POST requests; it should accept and digest a
  * POSTed resource representation and persist the resource state.
  */
 public function postAction() {
  
 }
 
 /**
  * The put action handles PUT requests and receives an 'id' parameter; it
  * should update the server resource state of the resource identified by
  * the 'id' value.
  */
 public function putAction() {
 
 }
 
 /**
  * The delete action handles DELETE requests and receives an 'id'
  * parameter; it should update the server resource state of the resource
  * identified by the 'id' value.
  */
 public function deleteAction() {
 
 }
 
  
 /**
  * Handle an array or object result
  *
  * @param array|object $struct Result Value
  * @return string XML Response
  */
 protected function _handleStruct($struct) {

  $dom = new DOMDocument ( '1.0', 'UTF-8' );
  
  $root = $dom->createElement ( "Jia" );
  $method = $root;
  
  $root->setAttribute ( 'generator', 'Yiyu Blog' );
  $root->setAttribute ( 'version', '1.0' );
  $dom->appendChild ( $root );
  
  $this->_structValue ( $struct, $dom, $method );
  
  $struct = ( array ) $struct;
  if (! isset ( $struct ['status'] )) {
   $status = $dom->createElement ( 'status', 'success' );
   $method->appendChild ( $status );
  }
  return $dom->saveXML ();
 }
 
 /**
  * Recursively iterate through a struct
  *
  * Recursively iterates through an associative array or object's properties
  * to build XML response.
  *
  * @param mixed $struct
  * @param DOMDocument $dom
  * @param DOMElement $parent
  * @return void
  */
 protected function _structValue($struct, DOMDocument $dom, DOMElement $parent) {
  $struct = ( array ) $struct;
  
  foreach ( $struct as $key => $value ) {
   if ($value === false) {
    $value = 0;
   } elseif ($value === true) {
    $value = 1;
   }
   
   if (ctype_digit ( ( string ) $key )) {
    $key = 'key_' . $key;
   }
   
   if (is_array ( $value ) || is_object ( $value )) {
    $element = $dom->createElement ( $key );
    $this->_structValue ( $value, $dom, $element );
   } else {
    $element = $dom->createElement ( $key );
    $element->appendChild ( $dom->createTextNode ( $value ) );
   }
   
   $parent->appendChild ( $element );
  }
 }
 
}
Return.php
/**
 * A dummy class as return objec.
 * 
 * @author Yiyu Jia
 *
 */
class Jia_Return {
 
 /**
  * 
  * @var unknown_type
  */
 public $products;
 public $quantity;
 /**
  * @return the $products
  */
 public function getProducts() {
  return $this->products;
 }

 /**
  * @param $products the $products to set
  */
 public function setProducts($products) {
  $this->products = $products;
 }
 /**
  * @return the $quantity
  */
 public function getQuantity() {
  return $this->quantity;
 }

 /**
  * @param $quantity the $quantity to set
  */
 public function setQuantity($quantity) {
  $this->quantity = $quantity;
 }
 
}

ErrorCode.php
/**
 * An dummy error code class.
 * 
 * @author Yiyu Jia
 *
 */
class Jia_ErrorCode { 
 
 public $errorCode;
 
 function __construct($errMsg){
  $this->errorCode = $errMsg;
 }  

}

ZendStudio 7.2 project file can be downloaded from here.

Friday, November 5, 2010

A simple tutorial about creating Zend_Rest_Server based RESTful service.

It seems that RESTful WS is attracting more people to consider as RPC solution. Compared with SOAP based Web Service, RESTful has its advantage and disadvantage. RESTful WS is declared to be more scalability as it exactly maps HTTP methods  POST, GET, PUT, DELETE as a CRUD (Create, Request, Update, Delete) operations. So, if we design our RESTful WS as stateless, it could be more scalable in a cluster farm. It can get benefit from Cache mechanism of HTTP (limited to HTTP GET method configuration on proxy server in most time). However, it could be problem or ask for cost if we do not want the result to be cached. Furthermore, we can not always assume client Web browser has cache enabled. So, RESTful is not always right choice. Especially, it is not good choice when we develop an enterprise application which ask for complex data structure delivered between sub systems and it does not have extremely large volume of the concurrent accessing.

I will compose another post to describe my comparison about SOAP WS and RESTful WS and JSON-RPC. Here, I will show a simple demo about how to create Zend_Rest_Server based RESTful WS in Zend Studio. Zend_Rest_Server is not perfect. For instances, it can not work together with Zend_Action_Router_Route. It also asked for calling function by put "method=functionName" in requesting URL after the "?". This make some guys think it is not RESTful WS as some person said it is not good design. But, I think it is not wrong design at least.  Below is source for simple ZF based RESTful WS.
  1. Create a new ZF project in Zend Studio and name it as jiaRESTfulWS.
  2. Add autoloaderNamespaces[] = "Jia_" in application.ini to allow Zend Autoloader load Jia_* classes.
  3. Creating a action class DummyController under directory /application/controllers. 
  4. creating a function restAction() in DummyController .
  5. Creating a Jia_Hello class with sayHello($name) function under /library/Jia directory.
It is done. You need to pay attention on how to specify web service URI in the code if you use different project name and function names. Or, you might want to add alias in your apache httpd configuration file to simplify the URL. On my machine, this demo RESTful WS can be accessed with following URL: http://localhost/jiaRESTfulWS/public/index.php/Dummy/rest?method=sayHello&name=jia . See key source code as below,

Jia_Hello class
/**
 * This class contains function which will be used by Web service caller.
 * All business logics will be implented or called in these functions.
 * 
 * @author Yiyu Jia
 *
 */
class Jia_Hello {

 /**
  * 
  * @param string $name
  * @return string
  * 
  */
 public function sayHello($name){
  return "hello ".$name;
 }
}


DummyController class
/**
 * This class includes one function restAction(), wich has Zend_Rest_Server initialized.
 * However, you can only use URI with "?" like this "/rest?method=sayHello&name=jia". You 
 * can not have URI like "/rest/name/jia"
 * 
 * @author yiyu
 *
 */
class DummyController extends Zend_Controller_Action
{
 /**
  *  action named as rest.
  */
 public function restAction() {
  
  // disable layouts and renderers
  $this->getHelper ( 'viewRenderer' )->setNoRender ( true );
  
  // initialize REST server
  $server = new Zend_Rest_Server();
  // set REST service class
  $server->setClass ( 'Jia_Hello' ); 
    
  // handle request
  $server->handle ();
 
 }
 
}

Zend Studio 7.2 project file can be downloaded here. Also, I do not recommend to use Zend_Rest_Server for developing RESTful WS as I think it is old and uncompleted class. Zend_Rest_Route should be a better choice to make RESTful Web Service in PHP. Here is a simple tutorial about how to use Zend_Rest_Route and Zend_Rest_Controller to create RESTful Web Service. Creating RESTful service with Zend_Rest_Route and Zend_Rest_Controller

Wednesday, November 3, 2010

upgrade Fedora 13 to Fedora 14

I have been waiting for Fedora 14 for months as Fedora 13 has screen flickers (see bug report here) on my notebook. Fedora 14 was released yesterday. I luckily upgrade my notebook from Fedora 13 to Fedora 14 without problem. I just followed the instruction from Fedora forum. I will list the command here,

1) type su to be a root user.
2) run command yum update rpm
3) run command yum -y update to make sure upgrading Fedora 13 to the latest version. If new Fedora 13 kernel is installed at this step, reboot is required.
4) run command yum clean all
5) run command yum -install preupgrade
6) run command preupgrade-cli and find the valid entry
7) run command preupgrade-cli "Fedora 14 (Laughlin)"
8) answered yes during the upgrading and waited patiently.

The only problem I got is that preupgrade GUI does not work. It shows the windows. But, I can not make selection in its drop down box. So, I use preupgrade-cli and it works. I installed both GNOME and KDE in my Fedora 13. preupgrade-cli upgraded my desktop environment too. Btw, I think we need to leave enough spare space on hard drive for upgrading as I saw somebody complain the upgrade command does not work and the error message shows the enough hard drive space.