From 94c86319caf6c3a1fd3d6c617f8f702b5c0c4fcb Mon Sep 17 00:00:00 2001 From: Julien HENRY Date: Wed, 19 Jan 2011 02:41:34 +0800 Subject: [PATCH] SONAR-2066: Allow PUT requests from Java Web Service Client --- .../controllers/api/properties_controller.rb | 226 +++++++++--------- .../main/java/org/sonar/wsclient/Sonar.java | 4 + .../sonar/wsclient/connectors/Connector.java | 6 + .../connectors/HttpClient3Connector.java | 32 ++- .../connectors/HttpClient4Connector.java | 28 +++ .../wsclient/services/AbstractQuery.java | 7 + .../sonar/wsclient/services/UpdateQuery.java | 31 +++ 7 files changed, 220 insertions(+), 114 deletions(-) create mode 100644 sonar-ws-client/src/main/java/org/sonar/wsclient/services/UpdateQuery.java diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/properties_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/properties_controller.rb index dafcc01e899..b79538925c1 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/properties_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/properties_controller.rb @@ -1,114 +1,114 @@ -# -# 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] - 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] - 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 - +# +# 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 diff --git a/sonar-ws-client/src/main/java/org/sonar/wsclient/Sonar.java b/sonar-ws-client/src/main/java/org/sonar/wsclient/Sonar.java index 99cd619b958..3f5fbf129cc 100644 --- a/sonar-ws-client/src/main/java/org/sonar/wsclient/Sonar.java +++ b/sonar-ws-client/src/main/java/org/sonar/wsclient/Sonar.java @@ -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); } diff --git a/sonar-ws-client/src/main/java/org/sonar/wsclient/connectors/Connector.java b/sonar-ws-client/src/main/java/org/sonar/wsclient/connectors/Connector.java index 48599874db4..c7e2176029b 100644 --- a/sonar-ws-client/src/main/java/org/sonar/wsclient/connectors/Connector.java +++ b/sonar-ws-client/src/main/java/org/sonar/wsclient/connectors/Connector.java @@ -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); } diff --git a/sonar-ws-client/src/main/java/org/sonar/wsclient/connectors/HttpClient3Connector.java b/sonar-ws-client/src/main/java/org/sonar/wsclient/connectors/HttpClient3Connector.java index e3b2619549e..34c8c795f3c 100644 --- a/sonar-ws-client/src/main/java/org/sonar/wsclient/connectors/HttpClient3Connector.java +++ b/sonar-ws-client/src/main/java/org/sonar/wsclient/connectors/HttpClient3Connector.java @@ -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); } } diff --git a/sonar-ws-client/src/main/java/org/sonar/wsclient/connectors/HttpClient4Connector.java b/sonar-ws-client/src/main/java/org/sonar/wsclient/connectors/HttpClient4Connector.java index 9bb556cf5fa..8cb372d284c 100644 --- a/sonar-ws-client/src/main/java/org/sonar/wsclient/connectors/HttpClient4Connector.java +++ b/sonar-ws-client/src/main/java/org/sonar/wsclient/connectors/HttpClient4Connector.java @@ -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"); } diff --git a/sonar-ws-client/src/main/java/org/sonar/wsclient/services/AbstractQuery.java b/sonar-ws-client/src/main/java/org/sonar/wsclient/services/AbstractQuery.java index f2133b5fe02..4ceb4443aa1 100644 --- a/sonar-ws-client/src/main/java/org/sonar/wsclient/services/AbstractQuery.java +++ b/sonar-ws-client/src/main/java/org/sonar/wsclient/services/AbstractQuery.java @@ -31,6 +31,13 @@ public abstract class AbstractQuery { */ 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 index 00000000000..3676a927a30 --- /dev/null +++ b/sonar-ws-client/src/main/java/org/sonar/wsclient/services/UpdateQuery.java @@ -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 extends AbstractQuery { + + public abstract Class getModelClass(); + +} -- 2.39.5