]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-2066: Allow PUT requests from Java Web Service Client
authorJulien HENRY <henryju@yahoo.fr>
Tue, 18 Jan 2011 18:41:34 +0000 (02:41 +0800)
committerEvgeny Mandrikov <mandrikov@gmail.com>
Wed, 19 Jan 2011 21:24:22 +0000 (05:24 +0800)
sonar-server/src/main/webapp/WEB-INF/app/controllers/api/properties_controller.rb
sonar-ws-client/src/main/java/org/sonar/wsclient/Sonar.java
sonar-ws-client/src/main/java/org/sonar/wsclient/connectors/Connector.java
sonar-ws-client/src/main/java/org/sonar/wsclient/connectors/HttpClient3Connector.java
sonar-ws-client/src/main/java/org/sonar/wsclient/connectors/HttpClient4Connector.java
sonar-ws-client/src/main/java/org/sonar/wsclient/services/AbstractQuery.java
sonar-ws-client/src/main/java/org/sonar/wsclient/services/UpdateQuery.java [new file with mode: 0644]

index dafcc01e8990873ef1733a867735945bdcf9d442..b79538925c16e54e5123e11490b1c5a85ffad5c8 100644 (file)
-#\r
-# Sonar, entreprise quality control tool.\r
-# Copyright (C) 2009 SonarSource SA\r
-# mailto:contact AT sonarsource DOT com\r
-#\r
-# Sonar is free software; you can redistribute it and/or\r
-# modify it under the terms of the GNU Lesser General Public\r
-# License as published by the Free Software Foundation; either\r
-# version 3 of the License, or (at your option) any later version.\r
-#\r
-# Sonar is distributed in the hope that it will be useful,\r
-# but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
-# Lesser General Public License for more details.\r
-#\r
-# You should have received a copy of the GNU Lesser General Public\r
-# License along with Sonar; if not, write to the Free Software\r
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02\r
-#\r
-\r
-require "json"\r
-\r
-class Api::PropertiesController < Api::RestController\r
-\r
-  before_filter :admin_required, :only => [ :create, :update, :destroy ]\r
-\r
-  def index\r
-    properties = Property.find(:all, :conditions => ['resource_id is null and user_id is null']).select do |property|\r
-      viewable?(property.key)\r
-    end\r
-    rest_render(properties)\r
-  end\r
-\r
-  def show\r
-    key = params[:id]\r
-    prop = Property.by_key(key)\r
-    if prop\r
-      if viewable?(key)\r
-        rest_render([prop])\r
-      else\r
-        rest_status_ko('You are not authorized to see this ressource', 401)\r
-      end\r
-    else\r
-      rest_status_ko('Property [' + params[:id] + '] does not exist', 404)\r
-    end\r
-  end\r
-\r
-  def create\r
-    key = params[:id]\r
-    value = params[:value]\r
-    if key\r
-      begin\r
-        Property.set(key, value)\r
-        rest_status_ok\r
-      rescue Exception => ex\r
-        rest_status_ko(ex.message, 400)\r
-      end\r
-    else\r
-      rest_status_ko('Property key [' + params[:id] + '] is not valid', 400)\r
-    end\r
-  end\r
-\r
-  def update\r
-    key = params[:id]\r
-    value = params[:value]\r
-    if key\r
-      begin\r
-        Property.set(key, value)\r
-        rest_status_ok\r
-      rescue Exception => ex\r
-        rest_status_ko(ex.message, 400)\r
-      end\r
-    else\r
-      rest_status_ko('Property key [' + params[:id] + '] is not valid', 400)\r
-    end\r
-  end\r
-\r
-  def destroy\r
-    key = params[:id]\r
-    if key \r
-      begin\r
-        Property.clear(key)\r
-        rest_status_ok\r
-      rescue Exception => ex\r
-        rest_status_ko(ex.message, 400)\r
-      end\r
-    else\r
-      rest_status_ko('Property key [' + params[:id] + '] is not valid', 400)\r
-    end\r
-  end\r
-\r
-  protected\r
-\r
-  def rest_to_json(properties)\r
-    JSON(properties.collect{|property| property.to_hash_json})\r
-  end\r
-\r
-  def rest_to_xml(properties)\r
-    xml = Builder::XmlMarkup.new(:indent => 0)\r
-    xml.instruct!\r
-    xml.properties do\r
-      properties.each do |property|\r
-        property.to_xml(xml)\r
-      end\r
-    end\r
-  end\r
-\r
-  private\r
-\r
-  def viewable?(property_key)\r
-    !property_key.to_s.index('.secured') || is_admin?\r
-  end\r
-\r
+#
+# Sonar, entreprise quality control tool.
+# Copyright (C) 2009 SonarSource SA
+# mailto:contact AT sonarsource DOT com
+#
+# Sonar is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# Sonar is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with Sonar; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+#
+
+require "json"
+
+class Api::PropertiesController < Api::RestController
+
+  before_filter :admin_required, :only => [ :create, :update, :destroy ]
+
+  def index
+    properties = Property.find(:all, :conditions => ['resource_id is null and user_id is null']).select do |property|
+      viewable?(property.key)
+    end
+    rest_render(properties)
+  end
+
+  def show
+    key = params[:id]
+    prop = Property.by_key(key)
+    if prop
+      if viewable?(key)
+        rest_render([prop])
+      else
+        rest_status_ko('You are not authorized to see this ressource', 401)
+      end
+    else
+      rest_status_ko('Property [' + params[:id] + '] does not exist', 404)
+    end
+  end
+
+  def create
+    key = params[:id]
+    value = params[:value] || request.raw_post
+    if key
+      begin
+        Property.set(key, value)
+        rest_status_ok
+      rescue Exception => ex
+        rest_status_ko(ex.message, 400)
+      end
+    else
+      rest_status_ko('Property key [' + params[:id] + '] is not valid', 400)
+    end
+  end
+
+  def update
+    key = params[:id]
+    value = params[:value] || request.raw_post
+    if key
+      begin
+        Property.set(key, value)
+        rest_status_ok
+      rescue Exception => ex
+        rest_status_ko(ex.message, 400)
+      end
+    else
+      rest_status_ko('Property key [' + params[:id] + '] is not valid', 400)
+    end
+  end
+
+  def destroy
+    key = params[:id]
+    if key 
+      begin
+        Property.clear(key)
+        rest_status_ok
+      rescue Exception => ex
+        rest_status_ko(ex.message, 400)
+      end
+    else
+      rest_status_ko('Property key [' + params[:id] + '] is not valid', 400)
+    end
+  end
+
+  protected
+
+  def rest_to_json(properties)
+    JSON(properties.collect{|property| property.to_hash_json})
+  end
+
+  def rest_to_xml(properties)
+    xml = Builder::XmlMarkup.new(:indent => 0)
+    xml.instruct!
+    xml.properties do
+      properties.each do |property|
+        property.to_xml(xml)
+      end
+    end
+  end
+
+  private
+
+  def viewable?(property_key)
+    !property_key.to_s.index('.secured') || is_admin?
+  end
+
 end
\ No newline at end of file
index 99cd619b9586bce012bccc3e3449afc1c8a9839b..3f5fbf129ccadd796681b8560495d7ef74eb0554 100644 (file)
@@ -76,6 +76,10 @@ public class Sonar {
     return result;
   }
 
+  public void update(UpdateQuery<?> query) {
+    connector.execute(query);
+  }
+
   public void delete(DeleteQuery<?> query) {
     connector.execute(query);
   }
index 48599874db42e18c5e3fa459637146ed021527f6..c7e2176029b7ac6c27240900e78df82c5153ec02 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.wsclient.connectors;
 import org.sonar.wsclient.services.CreateQuery;
 import org.sonar.wsclient.services.DeleteQuery;
 import org.sonar.wsclient.services.Query;
+import org.sonar.wsclient.services.UpdateQuery;
 
 /**
  * @since 2.1
@@ -48,4 +49,9 @@ public abstract class Connector {
    */
   public abstract String execute(DeleteQuery<?> query);
 
+  /**
+   * @return JSON response or null if 404 NOT FOUND error
+   * @since 2.6
+   */
+  public abstract String execute(UpdateQuery<?> query);
 }
index e3b2619549e203f6f4aeaf98ff5aec3f5b4a703a..34c8c795f3cffbf70c56a5f144cef1d6aa15cac5 100644 (file)
@@ -24,17 +24,21 @@ import org.apache.commons.httpclient.auth.AuthScope;
 import org.apache.commons.httpclient.methods.DeleteMethod;
 import org.apache.commons.httpclient.methods.GetMethod;
 import org.apache.commons.httpclient.methods.PostMethod;
+import org.apache.commons.httpclient.methods.PutMethod;
+import org.apache.commons.httpclient.methods.StringRequestEntity;
 import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
 import org.apache.commons.httpclient.util.URIUtil;
 import org.sonar.wsclient.Host;
 import org.sonar.wsclient.services.CreateQuery;
 import org.sonar.wsclient.services.DeleteQuery;
 import org.sonar.wsclient.services.Query;
+import org.sonar.wsclient.services.UpdateQuery;
 
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
 
 /**
  * @since 2.1
@@ -85,6 +89,10 @@ public class HttpClient3Connector extends Connector {
     return executeRequest(newPostRequest(query));
   }
 
+  public String execute(UpdateQuery<?> query) {
+    return executeRequest(newPutRequest(query));
+  }
+
   public String execute(DeleteQuery<?> query) {
     return executeRequest(newDeleteRequest(query));
   }
@@ -129,12 +137,34 @@ public class HttpClient3Connector extends Connector {
   private HttpMethodBase newPostRequest(CreateQuery<?> query) {
     try {
       String url = server.getHost() + URIUtil.encodeQuery(query.getUrl());
-      HttpMethodBase method = new PostMethod(url);
+      PostMethod method = new PostMethod(url);
+      method.setRequestHeader("Accept", "application/json");
+      if (query.getBody() != null) {
+        method.setRequestEntity(new StringRequestEntity(query.getBody(), "text/plain; charset=UTF-8", "UTF-8"));
+      }
+      return method;
+
+    } catch (URIException e) {
+      throw new ConnectionException("Query: " + query, e);
+    } catch (UnsupportedEncodingException e) {
+      throw new ConnectionException("Unsupported encoding", e);
+    }
+  }
+
+  private HttpMethodBase newPutRequest(UpdateQuery<?> query) {
+    try {
+      String url = server.getHost() + URIUtil.encodeQuery(query.getUrl());
+      PutMethod method = new PutMethod(url);
       method.setRequestHeader("Accept", "application/json");
+      if (query.getBody() != null) {
+        method.setRequestEntity(new StringRequestEntity(query.getBody(), "text/plain; charset=UTF-8", "UTF-8"));
+      }
       return method;
 
     } catch (URIException e) {
       throw new ConnectionException("Query: " + query, e);
+    } catch (UnsupportedEncodingException e) {
+      throw new ConnectionException("Unsupported encoding", e);
     }
   }
 
index 9bb556cf5fa1ebdabf29f10b5b949eb18d93ac61..8cb372d284cec1e21e0e4d9cd27666c9d3dd5b6d 100644 (file)
@@ -25,8 +25,10 @@ import org.apache.http.client.CredentialsProvider;
 import org.apache.http.client.methods.HttpDelete;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
 import org.apache.http.client.methods.HttpRequestBase;
 import org.apache.http.client.protocol.ClientContext;
+import org.apache.http.entity.StringEntity;
 import org.apache.http.impl.auth.BasicScheme;
 import org.apache.http.impl.client.DefaultHttpClient;
 import org.apache.http.params.HttpConnectionParams;
@@ -39,8 +41,10 @@ import org.sonar.wsclient.Host;
 import org.sonar.wsclient.services.CreateQuery;
 import org.sonar.wsclient.services.DeleteQuery;
 import org.sonar.wsclient.services.Query;
+import org.sonar.wsclient.services.UpdateQuery;
 
 import java.io.IOException;
+import java.io.UnsupportedEncodingException;
 
 /**
  * @since 2.1
@@ -61,6 +65,10 @@ public class HttpClient4Connector extends Connector {
     return executeRequest(newPostMethod(query));
   }
 
+  public String execute(UpdateQuery<?> query) {
+    return executeRequest(newPutMethod(query));
+  }
+
   public String execute(DeleteQuery<?> query) {
     return executeRequest(newDeleteMethod(query));
   }
@@ -130,10 +138,30 @@ public class HttpClient4Connector extends Connector {
 
   private HttpPost newPostMethod(CreateQuery<?> query) {
     HttpPost post = new HttpPost(server.getHost() + query.getUrl());
+    if (query.getBody() != null) {
+      try {
+        post.setEntity(new StringEntity(query.getBody(), "UTF-8"));
+      } catch (UnsupportedEncodingException e) {
+        throw new ConnectionException("Encoding is not supported", e);
+      }
+    }
     setJsonHeader(post);
     return post;
   }
 
+  private HttpPut newPutMethod(UpdateQuery<?> query) {
+    HttpPut put = new HttpPut(server.getHost() + query.getUrl());
+    if (query.getBody() != null) {
+      try {
+        put.setEntity(new StringEntity(query.getBody(), "UTF-8"));
+      } catch (UnsupportedEncodingException e) {
+        throw new ConnectionException("Encoding is not supported", e);
+      }
+    }
+    setJsonHeader(put);
+    return put;
+  }
+
   private void setJsonHeader(HttpRequestBase request) {
     request.setHeader("Accept", "application/json");
   }
index f2133b5fe02812cb17a0b71c6ffc9e32c596b3d4..4ceb4443aa18982f5fb33f2c87400171f359cdc8 100644 (file)
@@ -31,6 +31,13 @@ public abstract class AbstractQuery<MODEL extends Model> {
    */
   public abstract String getUrl();
 
+  /**
+   * Request body. By defaut it is empty but it can be overriden. 
+   */
+  public String getBody() {
+    return null;
+  }
+
   protected static void appendUrlParameter(StringBuilder url, String paramKey, Object paramValue) {
     if (paramValue != null) {
       url.append(paramKey)
diff --git a/sonar-ws-client/src/main/java/org/sonar/wsclient/services/UpdateQuery.java b/sonar-ws-client/src/main/java/org/sonar/wsclient/services/UpdateQuery.java
new file mode 100644 (file)
index 0000000..3676a92
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.wsclient.services;
+
+/**
+ * PUT HTTP request
+ *
+ * @since 2.6
+ */
+public abstract class UpdateQuery<MODEL extends Model> extends AbstractQuery<MODEL> {
+
+  public abstract Class<MODEL> getModelClass();
+
+}