title: SmartGWT RestDataSource calling an existing REST service
body: <p>I have a database back-end that I've thoroughly tested with several unit tests.  The controller looks like this:</p>
  
  <pre><code>@RequestMapping(value = "/create", method = RequestMethod.POST, produces = "application/json", headers = "content-type=application/json")
  public @ResponseBody UserDTO createUser(@RequestBody UserDTO user)
  {
      UserEntity userEntity = service.add(user);
      return mappingUser(userEntity);
  }
  </code></pre>
  
  <p>The unit test looks like:</p>
  
  <pre><code> @Test
      public void testCreateUser() throws Exception
      {
          UserDTO userDto = createUserDto();
          String url = BASE_URL + "/rest/users/create";
          UserDTO newUserDto = restTemplate
          .postForObject(url, userDto, UserDTO.class, new Object[]{});
      }
  </code></pre>
  
  <p>I have confirmed that the unit test works great, and the actual web-service is called correctly, and data is put into the database.</p>
  
  <p>Now, I am using a SmartGWT RestDataSource, and I am trying to configure the RestDataSource correctly to pass a new user in the request body, and return the new object.  I want to send the data as JSON in the body, and return JSON from this call.   So, it might be that I need to change the controller itself to match up with the datasource.</p>
  
  <p>Here is the AbstractDataSource which extends RestDataSource:</p>
  
  <pre><code>import java.util.Map;
  
  import com.google.gwt.http.client.URL;
  import com.smartgwt.client.data.DSRequest;
  import com.smartgwt.client.data.OperationBinding;
  import com.smartgwt.client.data.Record;
  import com.smartgwt.client.data.RestDataSource;
  import com.smartgwt.client.types.DSOperationType;
  import com.smartgwt.client.types.DSProtocol;
  
  public abstract class AbstractRestDataSource extends RestDataSource
  {
      public AbstractRestDataSource(String id)
      {
          setID(id);
          setClientOnly(false);
  
          // set up FETCH to use GET requests
          OperationBinding fetch = new OperationBinding();
          fetch.setOperationType(DSOperationType.FETCH);
          fetch.setDataProtocol(DSProtocol.GETPARAMS);
          DSRequest fetchProps = new DSRequest();
          fetchProps.setHttpMethod("GET");
          fetch.setRequestProperties(fetchProps);
  
          // set up ADD to use POST requests
          OperationBinding add = new OperationBinding();
          add.setOperationType(DSOperationType.ADD);
          add.setDataProtocol(DSProtocol.POSTMESSAGE);
          DSRequest addProps = new DSRequest();
          addProps.setHttpMethod("POST");
          addProps.setContentType("application/json");
          add.setRequestProperties(addProps);
  
          // set up UPDATE to use PUT
          OperationBinding update = new OperationBinding();
          update.setOperationType(DSOperationType.UPDATE);
          update.setDataProtocol(DSProtocol.POSTMESSAGE);
          DSRequest updateProps = new DSRequest();
          updateProps.setHttpMethod("PUT");
          update.setRequestProperties(updateProps);
  
          // set up REMOVE to use DELETE
          OperationBinding remove = new OperationBinding();
          remove.setOperationType(DSOperationType.REMOVE);
          DSRequest removeProps = new DSRequest();
          removeProps.setHttpMethod("DELETE");
          remove.setRequestProperties(removeProps);
  
          // apply all the operational bindings
          setOperationBindings(fetch, add, update, remove);
  
          init();
      }
  
      @Override
      protected Object transformRequest(DSRequest request)
      {
          super.transformRequest(request);
  
          // now post process the request for our own means
          postProcessTransform(request);
  
          return request.getData();
      }
  
      /*
       * Implementers can override this method to create a 
       * different override.
       */
      @SuppressWarnings("rawtypes")
      protected void postProcessTransform(DSRequest request)
      {
          StringBuilder url = new StringBuilder(getServiceRoot());
  
          Map dataMap = request.getAttributeAsMap("data");
          if (request.getOperationType() == DSOperationType.REMOVE)
          {
              // in case of remove, append the primary key
              url.append(getPrimaryKeyProperty()).append("/").append(dataMap.get(getPrimaryKeyProperty()));
          }
          else if (request.getOperationType() == DSOperationType.UPDATE)
          {
              url.append("update");
              appendParameters(url, request);
          }
          else if (request.getOperationType() == DSOperationType.FETCH &amp;&amp; dataMap.size() &gt; 0)
          {
              url.append(getPrimaryKeyProperty()).append("/").append(dataMap.get(getPrimaryKeyProperty()));
          }
          else if (request.getOperationType() == DSOperationType.ADD)
          {
              url.append("create");
          }
  
          System.out.println("AbstractRestDataSource: postProcessTransform: url=" + url.toString());
          request.setActionURL(URL.encode(url.toString()));
      }
  
      /*
       * This simply appends parameters that have changed to the URL
       * so that PUT requests go through successfully. This is usually
       * necessary because when smart GWT updates a row using a form,
       * it sends the data as form parameters. Most servers cannot
       * understand this and will simply disregard the form data
       * sent to the server via PUT. So we need to transform the form
       * data into URL parameters.
       */
      @SuppressWarnings("rawtypes")
      protected void appendParameters(StringBuilder url, DSRequest request)
      {
          Map dataMap = request.getAttributeAsMap("data");
          Record oldValues = request.getOldValues();
          boolean paramsAppended = false;
  
          if (!dataMap.isEmpty())
          {
              url.append("?");
          }
  
          for (Object keyObj : dataMap.keySet())
          {
              String key = (String) keyObj;
              if (!dataMap.get(key).equals(oldValues.getAttribute(key)) || isPrimaryKey(key))
              {
                  // only append those values that changed or are primary keys
                  url.append(key).append('=').append(dataMap.get(key)).append('&amp;');
                  paramsAppended = true;
              }
          }
  
          if (paramsAppended)
          {
              // delete the last '&amp;'
              url.deleteCharAt(url.length() - 1);
          }
      }
  
      private boolean isPrimaryKey(String property)
      {
          return getPrimaryKeyProperty().equals(property);
      }
  
      /*
       * The implementer can override this to change the name of the
       * primary key property.
       */
      protected String getPrimaryKeyProperty()
      {
          return "id";
      }
  
      protected abstract String getServiceRoot();
  
      protected abstract void init();
  }
  </code></pre>
  
  <p>And here is the UserDataSource which extends AbstractRestDataSource:</p>
  
  <pre><code>import java.util.Map;
  
  import com.google.gwt.http.client.URL;
  import com.opensource.restful.shared.Constants;
  import com.smartgwt.client.data.DSRequest;
  import com.smartgwt.client.data.fields.DataSourceBooleanField;
  import com.smartgwt.client.data.fields.DataSourceDateField;
  import com.smartgwt.client.data.fields.DataSourceIntegerField;
  import com.smartgwt.client.data.fields.DataSourceTextField;
  import com.smartgwt.client.types.DSDataFormat;
  import com.smartgwt.client.types.DSOperationType;
  
  public class UserDataSource extends AbstractRestDataSource
  {
      private static UserDataSource instance = null;
  
      public static UserDataSource getInstance()
      {
          if (instance == null)
          {
              instance = new UserDataSource("restUserDS");
          }
  
          return instance;
      }
  
      private UserDataSource(String id)
      {
          super(id);
      }
  
      private DataSourceIntegerField userIdField;
      private DataSourceBooleanField userActiveField;
      private DataSourceTextField usernameField;
      private DataSourceTextField passwordField;
      private DataSourceTextField firstnameField;
      private DataSourceTextField lastnameField;
      private DataSourceTextField emailField;
      private DataSourceTextField securityQuestion1Field;
      private DataSourceTextField securityAnswer1Field;
      private DataSourceTextField securityQuestion2Field;
      private DataSourceTextField securityAnswer2Field;
      private DataSourceDateField birthdateField;
  
      private DataSourceIntegerField positionIdField;
  
      protected void init()
      {
          setDataFormat(DSDataFormat.JSON);
          setJsonRecordXPath("/");
  
          // set the values for the datasource
          userIdField = new DataSourceIntegerField(Constants.USER_ID, Constants.TITLE_USER_ID);
          userIdField.setPrimaryKey(true);
          userIdField.setCanEdit(false);
  
          userActiveField = new DataSourceBooleanField(Constants.USER_ACTIVE, Constants.TITLE_USER_ACTIVE);
  
          usernameField = new DataSourceTextField(Constants.USER_USERNAME, Constants.TITLE_USER_USERNAME);
          passwordField = new DataSourceTextField(Constants.USER_PASSWORD, Constants.TITLE_USER_PASSWORD);
  
          firstnameField = new DataSourceTextField(Constants.USER_FIRST_NAME, Constants.TITLE_USER_FIRST_NAME);
          lastnameField = new DataSourceTextField(Constants.USER_LAST_NAME, Constants.TITLE_USER_LAST_NAME);
  
          emailField = new DataSourceTextField(Constants.USER_EMAIL, Constants.TITLE_USER_EMAIL);
  
          securityQuestion1Field =
              new DataSourceTextField(Constants.USER_SECURITY_QUESTION_1, Constants.TITLE_USER_SECURITY_QUESTION_1);
          securityAnswer1Field =
              new DataSourceTextField(Constants.USER_SECURITY_ANSWER_1, Constants.TITLE_USER_SECURITY_ANSWER_1);
          securityQuestion2Field =
              new DataSourceTextField(Constants.USER_SECURITY_QUESTION_2, Constants.TITLE_USER_SECURITY_QUESTION_2);
          securityAnswer2Field =
              new DataSourceTextField(Constants.USER_SECURITY_ANSWER_2, Constants.TITLE_USER_SECURITY_ANSWER_2);
  
          birthdateField = new DataSourceDateField(Constants.USER_BIRTHDATE, Constants.TITLE_USER_BIRTHDATE);
  
          positionIdField = new DataSourceIntegerField(Constants.USER_POSITION_ID, Constants.TITLE_USER_POSITION_ID);
          // positionActiveField = new DataSourceBooleanField(Constants.USER_ACTIVE, Constants.TITLE_USER_ACTIVE);
          // positionCodeField;
          // positionDescriptionField;
  
          setFields(userIdField, userActiveField, usernameField, passwordField, firstnameField, lastnameField,
              emailField, birthdateField, securityQuestion1Field, securityAnswer1Field, securityQuestion2Field,
              securityAnswer2Field, positionIdField);
      }
  
      protected String getServiceRoot()
      {
          return "rest/users/";
      }
  
      protected String getPrimaryKeyProperty()
      {
          return "userId";
      }
  
      /*
       * Implementers can override this method to create a 
       * different override.
       */
      @SuppressWarnings("rawtypes")
      protected void postProcessTransform(DSRequest request)
      {
          // request.setContentType("application/json");
  
          StringBuilder url = new StringBuilder(getServiceRoot());
  
          Map dataMap = request.getAttributeAsMap("data");
          if (request.getOperationType() == DSOperationType.REMOVE)
          {
              // in case of remove, append the primary key
              url.append(getPrimaryKeyProperty()).append("/").append(dataMap.get(getPrimaryKeyProperty()));
          }
          else if (request.getOperationType() == DSOperationType.UPDATE)
          {
              url.append("update");
              System.out.println("UserDataSource: postProcessTransform: update: url=" + url.toString());
          }
          else if (request.getOperationType() == DSOperationType.FETCH &amp;&amp; dataMap.size() &gt; 0)
          {
              url.append(getPrimaryKeyProperty()).append("/").append(dataMap.get(getPrimaryKeyProperty()));
          }
          else if (request.getOperationType() == DSOperationType.ADD)
          {
              url.append("create");
          }
  
          System.out.println("UserDataSource: postProcessTransform: url=" + url.toString());
          request.setActionURL(URL.encode(url.toString()));
      }
  }
  </code></pre>
  
  <p>If I can find out how to get the UserDTO as JSON into the requestBody, I think I will have solved all my issues.  For extra information you should know, I am using Spring 3.2 and the Jackson Message Converters configured in the springmvc-servlet.xml file.  </p>
  
  <p>At one point I did see all the data getting appended to the URL, but I would prefer if the data was not in the URL as parameters, but rather in the request body.  SO, I need to know if this is possible, and how to do it.</p>
  
  <p>Thanks for any help!!!</p>