From bd8e6bb58334bc065749fbffb5362eba936b023d Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Sat, 27 Apr 2013 18:20:24 +0200 Subject: [PATCH] Improve interactions between JRuby and Java components --- .../sonar/core/issue/workflow/Transition.java | 4 +- .../core/source/HtmlSourceDecorator.java | 2 +- .../java/org/sonar/api/issue/IssueFinder.java | 2 + .../java/org/sonar/api/issue/JRubyIssues.java | 13 +-- sonar-server/src/dev/web.xml | 10 +- ...ltJRubyIssues.java => JRubyApiIssues.java} | 13 +-- .../server/issue/JRubyInternalIssues.java | 32 +++--- .../server/issue/ServerIssueActions.java | 17 ++- .../org/sonar/server/platform/Platform.java | 25 +++- .../sonar/server/platform/UserSession.java | 66 +++++++++++ .../server/platform/UserSessionFilter.java | 53 +++++++++ ...DefaultJRubyRules.java => JRubyRules.java} | 9 +- .../JRubyFacades.java => text/JRubyText.java} | 39 ++++--- .../sonar/server/{macro => text}/Macro.java | 2 +- .../{macro => text}/MacroInterpreter.java | 22 ++-- .../server/{macro => text}/RuleMacro.java | 8 +- .../java/org/sonar/server/ui/JRubyFacade.java | 28 +---- .../api/authentication_controller.rb | 2 +- .../app/controllers/api/issues_controller.rb | 59 ++++++---- .../app/controllers/issues_controller.rb | 9 +- .../WEB-INF/app/helpers/application_helper.rb | 1 + .../WEB-INF/app/helpers/source_helper.rb | 2 +- .../src/main/webapp/WEB-INF/app/models/api.rb | 9 +- .../webapp/WEB-INF/app/models/api/utils.rb | 4 +- .../{macro_interpreter.rb => internal.rb} | 24 +++- .../webapp/WEB-INF/app/models/snapshot.rb | 4 +- .../WEB-INF/app/views/issue/_issue.html.erb | 2 +- .../WEB-INF/app/views/rules/_show.html.erb | 4 +- .../WEB-INF/app/views/rules/show.html.erb | 4 +- .../rules_configuration/_rule_note.html.erb | 4 +- .../WEB-INF/lib/authenticated_system.rb | 21 +++- sonar-server/src/main/webapp/WEB-INF/web.xml | 8 ++ ...ssuesTest.java => JRubyApiIssuesTest.java} | 31 +++-- .../platform/UserSessionFilterTest.java | 107 ++++++++++++++++++ ...RubyRulesTest.java => JRubyRulesTest.java} | 10 +- .../org/sonar/server/text/JRubyTextTest.java | 59 ++++++++++ .../{ui => text}/MacroInterpreterTest.java | 30 ++--- .../org/sonar/server/ui/JRubyFacadesTest.java | 36 ------ 38 files changed, 527 insertions(+), 248 deletions(-) rename sonar-server/src/main/java/org/sonar/server/issue/{DefaultJRubyIssues.java => JRubyApiIssues.java} (90%) rename sonar-plugin-api/src/main/java/org/sonar/api/rule/JRubyRules.java => sonar-server/src/main/java/org/sonar/server/issue/JRubyInternalIssues.java (62%) create mode 100644 sonar-server/src/main/java/org/sonar/server/platform/UserSession.java create mode 100644 sonar-server/src/main/java/org/sonar/server/platform/UserSessionFilter.java rename sonar-server/src/main/java/org/sonar/server/rule/{DefaultJRubyRules.java => JRubyRules.java} (82%) rename sonar-server/src/main/java/org/sonar/server/{ui/JRubyFacades.java => text/JRubyText.java} (50%) rename sonar-server/src/main/java/org/sonar/server/{macro => text}/Macro.java (96%) rename sonar-server/src/main/java/org/sonar/server/{macro => text}/MacroInterpreter.java (75%) rename sonar-server/src/main/java/org/sonar/server/{macro => text}/RuleMacro.java (90%) rename sonar-server/src/main/webapp/WEB-INF/app/models/{macro_interpreter.rb => internal.rb} (56%) rename sonar-server/src/test/java/org/sonar/server/issue/{DefaultJRubyIssuesTest.java => JRubyApiIssuesTest.java} (75%) create mode 100644 sonar-server/src/test/java/org/sonar/server/platform/UserSessionFilterTest.java rename sonar-server/src/test/java/org/sonar/server/rule/{DefaultJRubyRulesTest.java => JRubyRulesTest.java} (86%) create mode 100644 sonar-server/src/test/java/org/sonar/server/text/JRubyTextTest.java rename sonar-server/src/test/java/org/sonar/server/{ui => text}/MacroInterpreterTest.java (69%) delete mode 100644 sonar-server/src/test/java/org/sonar/server/ui/JRubyFacadesTest.java diff --git a/sonar-core/src/main/java/org/sonar/core/issue/workflow/Transition.java b/sonar-core/src/main/java/org/sonar/core/issue/workflow/Transition.java index 39eb470b6c7..632b944f711 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/workflow/Transition.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/workflow/Transition.java @@ -30,7 +30,7 @@ import java.util.Arrays; import java.util.Date; import java.util.List; -class Transition { +public class Transition { private final String key; private final String from, to; private final Condition[] conditions; @@ -46,7 +46,7 @@ class Transition { automatic = builder.automatic; } - String key() { + public String key() { return key; } diff --git a/sonar-core/src/main/java/org/sonar/core/source/HtmlSourceDecorator.java b/sonar-core/src/main/java/org/sonar/core/source/HtmlSourceDecorator.java index 91936ce34ee..16df96a3010 100644 --- a/sonar-core/src/main/java/org/sonar/core/source/HtmlSourceDecorator.java +++ b/sonar-core/src/main/java/org/sonar/core/source/HtmlSourceDecorator.java @@ -50,7 +50,7 @@ public class HtmlSourceDecorator implements ServerComponent { this.snapshotDataDao= snapshotDataDao; } - public Collection getDecoratedSourceAsHtml(long snapshotId) { + public List getDecoratedSourceAsHtml(long snapshotId) { List highlightingDataTypes = Lists.newArrayList(SnapshotDataType.SYNTAX_HIGHLIGHTING.getValue(), SnapshotDataType.SYMBOL_HIGHLIGHTING.getValue()); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueFinder.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueFinder.java index d82d43c0011..70086f1b245 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueFinder.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueFinder.java @@ -24,6 +24,7 @@ import org.sonar.api.ServerComponent; import org.sonar.api.component.Component; import org.sonar.api.rules.Rule; +import javax.annotation.CheckForNull; import javax.annotation.Nullable; import java.util.Collection; @@ -55,6 +56,7 @@ public interface IssueFinder extends ServerComponent { Results find(IssueQuery query, @Nullable Integer currentUserId, String role); + @CheckForNull Issue findByKey(String key /* TODO @Nullable Integer currentUserId */); /* diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/JRubyIssues.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/JRubyIssues.java index f366e433ee2..a56d44340e4 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/JRubyIssues.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/JRubyIssues.java @@ -35,16 +35,13 @@ public interface JRubyIssues extends ServerComponent { /** * Search for issues. - * - *

- * Ruby: Api.issues.find(hash_of_parameters, current_user.id) - *

- * - *

Parameters

+ *

+ * Ruby: Api.issues.find(hash_of_parameters) + *

*

    - * TODO document parameters + * TODO document parameters *
*/ - IssueFinder.Results find(Map parameters, Integer currentUserId); + IssueFinder.Results find(Map parameters); } diff --git a/sonar-server/src/dev/web.xml b/sonar-server/src/dev/web.xml index fbf1d357710..d1b0b8a7b18 100644 --- a/sonar-server/src/dev/web.xml +++ b/sonar-server/src/dev/web.xml @@ -40,7 +40,11 @@ DatabaseSessionFilter org.sonar.server.ui.DatabaseSessionFilter - + + UserSessionFilter + org.sonar.server.platform.UserSessionFilter + + RackFilter org.jruby.rack.RackFilter @@ -49,6 +53,10 @@ DatabaseSessionFilter /* + + UserSessionFilter + /* + ServletFilters /* diff --git a/sonar-server/src/main/java/org/sonar/server/issue/DefaultJRubyIssues.java b/sonar-server/src/main/java/org/sonar/server/issue/JRubyApiIssues.java similarity index 90% rename from sonar-server/src/main/java/org/sonar/server/issue/DefaultJRubyIssues.java rename to sonar-server/src/main/java/org/sonar/server/issue/JRubyApiIssues.java index 4c72fe8385d..513820ea099 100644 --- a/sonar-server/src/main/java/org/sonar/server/issue/DefaultJRubyIssues.java +++ b/sonar-server/src/main/java/org/sonar/server/issue/JRubyApiIssues.java @@ -30,7 +30,7 @@ import org.sonar.api.issue.JRubyIssues; import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.DateUtils; import org.sonar.api.web.UserRole; -import org.sonar.server.ui.JRubyFacades; +import org.sonar.server.platform.UserSession; import javax.annotation.Nullable; import java.util.Collection; @@ -43,24 +43,21 @@ import java.util.Map; * * @since 3.6 */ -public class DefaultJRubyIssues implements JRubyIssues { +public class JRubyApiIssues implements JRubyIssues { private final IssueFinder finder; - private final ServerIssueActions changes; - public DefaultJRubyIssues(IssueFinder f, ServerIssueActions changes) { + public JRubyApiIssues(IssueFinder f) { this.finder = f; - this.changes = changes; - JRubyFacades.setIssues(this); } /** * Requires the role {@link org.sonar.api.web.UserRole#CODEVIEWER} */ @Override - public IssueFinder.Results find(Map params, @Nullable Integer currentUserId) { + public IssueFinder.Results find(Map params) { // TODO move the role to IssueFinder - return finder.find(toQuery(params), currentUserId, UserRole.CODEVIEWER); + return finder.find(toQuery(params), UserSession.get().userId(), UserRole.CODEVIEWER); } IssueQuery toQuery(Map props) { diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rule/JRubyRules.java b/sonar-server/src/main/java/org/sonar/server/issue/JRubyInternalIssues.java similarity index 62% rename from sonar-plugin-api/src/main/java/org/sonar/api/rule/JRubyRules.java rename to sonar-server/src/main/java/org/sonar/server/issue/JRubyInternalIssues.java index 87656e61133..aa4c4b63cea 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/rule/JRubyRules.java +++ b/sonar-server/src/main/java/org/sonar/server/issue/JRubyInternalIssues.java @@ -17,27 +17,27 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.api.rule; +package org.sonar.server.issue; import org.sonar.api.ServerComponent; +import org.sonar.core.issue.workflow.Transition; +import org.sonar.server.platform.UserSession; + +import java.util.List; /** - * Facade for JRuby on Rails extensions to request rules. - *

- * Reference from Ruby code : Api.rules - *

- * - * @since 3.6 + * All the issue features that are not published to public API */ -public interface JRubyRules extends ServerComponent { +public class JRubyInternalIssues implements ServerComponent { + + private final ServerIssueActions actions; + + public JRubyInternalIssues(ServerIssueActions actions) { + this.actions = actions; + } - /** - * Return the localized name of a rule. - * - *

- * Ruby: Api.rules.ruleName(I18n.locale, rule.rule_key) - *

- */ - String ruleName(String rubyLocale, RuleKey ruleKey); + public List listTransitions(String issueKey) { + return actions.listTransitions(issueKey, UserSession.get().userId()); + } } diff --git a/sonar-server/src/main/java/org/sonar/server/issue/ServerIssueActions.java b/sonar-server/src/main/java/org/sonar/server/issue/ServerIssueActions.java index 7e6db78a103..969e04e17f1 100644 --- a/sonar-server/src/main/java/org/sonar/server/issue/ServerIssueActions.java +++ b/sonar-server/src/main/java/org/sonar/server/issue/ServerIssueActions.java @@ -21,14 +21,18 @@ package org.sonar.server.issue; import org.sonar.api.ServerComponent; import org.sonar.api.issue.Issue; +import org.sonar.api.issue.IssueFinder; import org.sonar.api.web.UserRole; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.db.IssueDao; import org.sonar.core.issue.db.IssueDto; import org.sonar.core.issue.workflow.IssueWorkflow; +import org.sonar.core.issue.workflow.Transition; import org.sonar.core.user.AuthorizationDao; import javax.annotation.Nullable; +import java.util.Collections; +import java.util.List; /** * @since 3.6 @@ -36,15 +40,26 @@ import javax.annotation.Nullable; public class ServerIssueActions implements ServerComponent { private final IssueWorkflow workflow; + private final IssueFinder finder; private final IssueDao issueDao; private final AuthorizationDao authorizationDao; - public ServerIssueActions(IssueWorkflow workflow, IssueDao issueDao, AuthorizationDao authorizationDao) { + public ServerIssueActions(IssueWorkflow workflow, IssueFinder finder, IssueDao issueDao, AuthorizationDao authorizationDao) { this.workflow = workflow; + this.finder = finder; this.issueDao = issueDao; this.authorizationDao = authorizationDao; } + public List listTransitions(String issueKey, @Nullable Integer userId) { + Issue issue = finder.findByKey(issueKey /*, userId */); + // TODO check authorization + if (issue == null) { + return Collections.emptyList(); + } + return workflow.outManualTransitions(issue); + } + public Issue executeAction(String issueKey, String action, @Nullable Integer userId) { if (userId == null) { // must be logged diff --git a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java index e2f6e298c35..c02d1a7e1cd 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java @@ -69,19 +69,21 @@ import org.sonar.server.charts.ChartFactory; import org.sonar.server.configuration.Backup; import org.sonar.server.configuration.ProfilesManager; import org.sonar.server.database.EmbeddedDatabaseFactory; -import org.sonar.server.issue.DefaultJRubyIssues; +import org.sonar.server.issue.JRubyApiIssues; +import org.sonar.server.issue.JRubyInternalIssues; import org.sonar.server.issue.ServerIssueActions; import org.sonar.server.issue.ServerIssueFinder; -import org.sonar.server.macro.MacroInterpreter; import org.sonar.server.notifications.NotificationCenter; import org.sonar.server.notifications.NotificationService; import org.sonar.server.notifications.reviews.ReviewsNotificationManager; import org.sonar.server.plugins.*; import org.sonar.server.qualitymodel.DefaultModelManager; -import org.sonar.server.rule.DefaultJRubyRules; +import org.sonar.server.rule.JRubyRules; import org.sonar.server.rules.ProfilesConsole; import org.sonar.server.rules.RulesConsole; import org.sonar.server.startup.*; +import org.sonar.server.text.JRubyText; +import org.sonar.server.text.MacroInterpreter; import org.sonar.server.ui.*; import javax.servlet.ServletContext; @@ -185,7 +187,6 @@ public final class Platform { coreContainer.addSingleton(ThreadLocalDatabaseSessionFactory.class); coreContainer.addPicoAdapter(new DatabaseSessionProvider()); coreContainer.addSingleton(ServerMetadataPersister.class); - coreContainer.addSingleton(MacroInterpreter.class); coreContainer.startComponents(); } @@ -242,10 +243,15 @@ public final class Platform { servicesContainer.addSingleton(IssueWorkflow.class); servicesContainer.addSingleton(ServerIssueActions.class); servicesContainer.addSingleton(ServerIssueFinder.class); - servicesContainer.addSingleton(DefaultJRubyIssues.class); + servicesContainer.addSingleton(JRubyApiIssues.class); + servicesContainer.addSingleton(JRubyInternalIssues.class); // rules - servicesContainer.addSingleton(DefaultJRubyRules.class); + servicesContainer.addSingleton(JRubyRules.class); + + // text + servicesContainer.addSingleton(MacroInterpreter.class); + servicesContainer.addSingleton(JRubyText.class); // Notifications servicesContainer.addSingleton(EmailSettings.class); @@ -329,4 +335,11 @@ public final class Platform { public static Server getServer() { return (Server) getInstance().getComponent(Server.class); } + + /** + * Used by ruby code + */ + public static T component(Class type) { + return getInstance().getContainer().getComponentByType(type); + } } diff --git a/sonar-server/src/main/java/org/sonar/server/platform/UserSession.java b/sonar-server/src/main/java/org/sonar/server/platform/UserSession.java new file mode 100644 index 00000000000..0fb7dd8ddbb --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/platform/UserSession.java @@ -0,0 +1,66 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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 this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform; + +import com.google.common.base.Objects; + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + +public class UserSession { + + private static final ThreadLocal threadLocal = new ThreadLocal(); + private static final UserSession ANONYMOUS = new UserSession(null, null); + + private final Integer userId; + private final String login; + + public UserSession(@Nullable Integer userId, @Nullable String login) { + this.userId = userId; + this.login = login; + } + + @CheckForNull + public String login() { + return login; + } + + @CheckForNull + public Integer userId() { + return userId; + } + + public boolean isLoggedIn() { + return userId != null; + } + + /** + * @return never null + */ + public static UserSession get() { + return Objects.firstNonNull(threadLocal.get(), ANONYMOUS); + } + + static void set(@Nullable UserSession session) { + threadLocal.set(session); + } + +} + diff --git a/sonar-server/src/main/java/org/sonar/server/platform/UserSessionFilter.java b/sonar-server/src/main/java/org/sonar/server/platform/UserSessionFilter.java new file mode 100644 index 00000000000..a63923c5139 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/platform/UserSessionFilter.java @@ -0,0 +1,53 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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 this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; +import java.io.IOException; + +public class UserSessionFilter implements Filter { + public void init(FilterConfig filterConfig) throws ServletException { + } + + public void destroy() { + } + + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + try { + HttpServletRequest request = (HttpServletRequest) servletRequest; + HttpSession httpSession = request.getSession(true); + + // See available attributes in authenticated_system.rb, methods + // current_user= and logout_keeping_session! + String login = (String) httpSession.getAttribute("login"); + Long userId = (Long) httpSession.getAttribute("user_id"); + + UserSession.set(new UserSession(userId != null ? userId.intValue() : null, login)); + + filterChain.doFilter(servletRequest, servletResponse); + + } finally { + UserSession.set(null); + } + } + +} diff --git a/sonar-server/src/main/java/org/sonar/server/rule/DefaultJRubyRules.java b/sonar-server/src/main/java/org/sonar/server/rule/JRubyRules.java similarity index 82% rename from sonar-server/src/main/java/org/sonar/server/rule/DefaultJRubyRules.java rename to sonar-server/src/main/java/org/sonar/server/rule/JRubyRules.java index 7323d0a2d3c..30d6c3a0e26 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule/DefaultJRubyRules.java +++ b/sonar-server/src/main/java/org/sonar/server/rule/JRubyRules.java @@ -19,9 +19,7 @@ */ package org.sonar.server.rule; -import org.sonar.api.rule.JRubyRules; import org.sonar.api.rule.RuleKey; -import org.sonar.server.ui.JRubyFacades; import org.sonar.server.ui.JRubyI18n; /** @@ -29,17 +27,16 @@ import org.sonar.server.ui.JRubyI18n; * * @since 3.6 */ -public class DefaultJRubyRules implements JRubyRules { +public class JRubyRules { private final JRubyI18n jRubyI18n; - public DefaultJRubyRules(JRubyI18n jRubyI18n) { + public JRubyRules(JRubyI18n jRubyI18n) { this.jRubyI18n = jRubyI18n; - JRubyFacades.setRules(this); } public String ruleName(String rubyLocale, RuleKey ruleKey) { - String l18n = jRubyI18n.getRuleName(rubyLocale, ruleKey.repository(), ruleKey.rule()); + String l18n = jRubyI18n.getRuleName(rubyLocale, ruleKey.repository(), ruleKey.rule()); if (l18n != null) { return l18n; } else { diff --git a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacades.java b/sonar-server/src/main/java/org/sonar/server/text/JRubyText.java similarity index 50% rename from sonar-server/src/main/java/org/sonar/server/ui/JRubyFacades.java rename to sonar-server/src/main/java/org/sonar/server/text/JRubyText.java index 0cd75620df2..7c8fd2a5dc4 100644 --- a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacades.java +++ b/sonar-server/src/main/java/org/sonar/server/text/JRubyText.java @@ -17,36 +17,35 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.server.ui; +package org.sonar.server.text; +import org.apache.commons.lang.StringEscapeUtils; import org.sonar.api.ServerComponent; -import org.sonar.api.issue.JRubyIssues; -import org.sonar.api.rule.JRubyRules; +import org.sonar.core.source.HtmlSourceDecorator; +import org.sonar.markdown.Markdown; -/** - * All the facades to Java components - * - * @since 3.6 - */ -public class JRubyFacades implements ServerComponent { +import java.util.List; - private static JRubyIssues issues = null; - private static JRubyRules rules = null; +public class JRubyText implements ServerComponent { - public static void setIssues(JRubyIssues i) { - JRubyFacades.issues = i; - } + private final MacroInterpreter macroInterpreter; + private final HtmlSourceDecorator sourceDecorator; - public static JRubyIssues issues() { - return issues; + public JRubyText(MacroInterpreter macroInterpreter, HtmlSourceDecorator sourceDecorator) { + this.macroInterpreter = macroInterpreter; + this.sourceDecorator = sourceDecorator; } - public static void setRules(JRubyRules rules) { - JRubyFacades.rules = rules; + public String interpretMacros(String text) { + return macroInterpreter.interpret(text); } - public static JRubyRules rules() { - return rules; + public String markdownToHtml(String markdown) { + // TODO move HTML escaping to sonar-markdown + return Markdown.convertToHtml(StringEscapeUtils.escapeHtml(markdown)); } + public List highlightedSourceLines(long snapshotId) { + return sourceDecorator.getDecoratedSourceAsHtml(snapshotId); + } } diff --git a/sonar-server/src/main/java/org/sonar/server/macro/Macro.java b/sonar-server/src/main/java/org/sonar/server/text/Macro.java similarity index 96% rename from sonar-server/src/main/java/org/sonar/server/macro/Macro.java rename to sonar-server/src/main/java/org/sonar/server/text/Macro.java index 91bd8410655..8b0a10fd75f 100644 --- a/sonar-server/src/main/java/org/sonar/server/macro/Macro.java +++ b/sonar-server/src/main/java/org/sonar/server/text/Macro.java @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.server.macro; +package org.sonar.server.text; public interface Macro { diff --git a/sonar-server/src/main/java/org/sonar/server/macro/MacroInterpreter.java b/sonar-server/src/main/java/org/sonar/server/text/MacroInterpreter.java similarity index 75% rename from sonar-server/src/main/java/org/sonar/server/macro/MacroInterpreter.java rename to sonar-server/src/main/java/org/sonar/server/text/MacroInterpreter.java index 6c85ff35777..9f55e385b4b 100644 --- a/sonar-server/src/main/java/org/sonar/server/macro/MacroInterpreter.java +++ b/sonar-server/src/main/java/org/sonar/server/text/MacroInterpreter.java @@ -18,33 +18,27 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.server.macro; +package org.sonar.server.text; +import com.google.common.collect.ImmutableList; import org.sonar.api.ServerComponent; import javax.servlet.ServletContext; - import java.util.List; -import static com.google.common.collect.Lists.newArrayList; - public class MacroInterpreter implements ServerComponent { - private ServletContext servletContext; - private List macroList; + private final List macros; public MacroInterpreter(ServletContext servletContext) { - this.servletContext = servletContext; - this.macroList = newArrayList(); - } - - public void start(){ - macroList.add(new RuleMacro(servletContext.getContextPath())); + this.macros = ImmutableList.of( + new RuleMacro(servletContext.getContextPath()) + ); } - public String interpret(String text){ + public String interpret(String text) { String textReplaced = text; - for (Macro macro : macroList) { + for (Macro macro : macros) { textReplaced = textReplaced.replaceAll(macro.getRegex(), macro.getReplacement()); } return textReplaced; diff --git a/sonar-server/src/main/java/org/sonar/server/macro/RuleMacro.java b/sonar-server/src/main/java/org/sonar/server/text/RuleMacro.java similarity index 90% rename from sonar-server/src/main/java/org/sonar/server/macro/RuleMacro.java rename to sonar-server/src/main/java/org/sonar/server/text/RuleMacro.java index df48a37d626..e0209ef5699 100644 --- a/sonar-server/src/main/java/org/sonar/server/macro/RuleMacro.java +++ b/sonar-server/src/main/java/org/sonar/server/text/RuleMacro.java @@ -18,13 +18,13 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.server.macro; +package org.sonar.server.text; -public class RuleMacro implements Macro{ +class RuleMacro implements Macro { private final String contextPath; - public RuleMacro(String contextPath){ + RuleMacro(String contextPath) { this.contextPath = contextPath; } @@ -35,7 +35,7 @@ public class RuleMacro implements Macro{ return "\\{rule:([a-zA-Z0-9._-]++):([a-zA-Z0-9._-]++)\\}"; } - public String getReplacement(){ + public String getReplacement() { return "$1:$2"; } } diff --git a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java index 0e16d6329d7..fc5a1d470d8 100644 --- a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java +++ b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java @@ -56,13 +56,10 @@ import org.sonar.core.persistence.DryRunDatabaseFactory; import org.sonar.core.purge.PurgeDao; import org.sonar.core.resource.ResourceIndexerDao; import org.sonar.core.resource.ResourceKeyUpdaterDao; -import org.sonar.core.source.HtmlSourceDecorator; import org.sonar.core.timemachine.Periods; import org.sonar.core.workflow.WorkflowEngine; -import org.sonar.markdown.Markdown; import org.sonar.server.configuration.Backup; import org.sonar.server.configuration.ProfilesManager; -import org.sonar.server.macro.MacroInterpreter; import org.sonar.server.notifications.reviews.ReviewsNotificationManager; import org.sonar.server.platform.*; import org.sonar.server.plugins.*; @@ -73,7 +70,6 @@ import org.sonar.updatecenter.common.UpdateCenter; import org.sonar.updatecenter.common.Version; import javax.annotation.Nullable; - import java.net.InetAddress; import java.sql.Connection; import java.util.*; @@ -89,11 +85,7 @@ public final class JRubyFacade { return SINGLETON; } - public static String markdownToHtml(String input) { - return Markdown.convertToHtml(input); - } - - private T get(Class componentType) { + T get(Class componentType) { return getContainer().getComponentByType(componentType); } @@ -317,7 +309,7 @@ public final class JRubyFacade { public void ruleSeverityChanged(int parentProfileId, int activeRuleId, int oldSeverityId, int newSeverityId, String userName) { getProfilesManager().ruleSeverityChanged(parentProfileId, activeRuleId, RulePriority.values()[oldSeverityId], - RulePriority.values()[newSeverityId], userName); + RulePriority.values()[newSeverityId], userName); } public void ruleDeactivated(int parentProfileId, int deactivatedRuleId, String userName) { @@ -513,10 +505,10 @@ public final class JRubyFacade { // notifier is null when creating the administrator in the migration script 011. if (notifier != null) { notifier.onNewUser(NewUserHandler.Context.builder() - .setLogin(fields.get("login")) - .setName(fields.get("name")) - .setEmail(fields.get("email")) - .build()); + .setLogin(fields.get("login")) + .setName(fields.get("name")) + .setEmail(fields.get("email")) + .build()); } } @@ -555,12 +547,4 @@ public final class JRubyFacade { public Testable testable(String componentKey) { return get(SnapshotPerspectives.class).as(MutableTestable.class, componentKey); } - - public MacroInterpreter getMacroInterpreter(){ - return get(MacroInterpreter.class); - } - - public Collection getHighlightedSourceLines(long snapshotId) { - return get(HtmlSourceDecorator.class).getDecoratedSourceAsHtml(snapshotId); - } } diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/authentication_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/authentication_controller.rb index 78f30d8e632..a27a930cdc5 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/authentication_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/authentication_controller.rb @@ -55,7 +55,7 @@ class Api::AuthenticationController < Api::ApiController end def anonymous? - !session.has_key?(:user_id) + !session.has_key?('user_id') end def set_cache_buster diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/issues_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/issues_controller.rb index d5350a804f1..b73e015ec80 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/issues_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/issues_controller.rb @@ -22,13 +22,31 @@ class Api::IssuesController < Api::ApiController # GET /api/issues/search? def search - user_id = current_user ? current_user.id : nil - results = Api.issues.find(params, user_id) - render :json => jsonp(results_to_json(results)) + results = Api.issues.find(params) + render :json => jsonp( + { + :securityExclusions => results.securityExclusions, + :paging => paging_to_json(results.paging), + :issues => results.issues.map { |issue| issue_to_json(issue) } + } + ) end - # POST /api/issues/change?key=&&... - def change + # GET /api/issues/transitions?issue= + def transitions + # TODO deal with errors (404, ...) + require_parameters :issue + issue_key = params[:issue] + transitions = Internal.issues.listTransitions(issue_key) + render :json => jsonp( + { + :transitions => transitions.map { |t| t.key() } + } + ) + end + + # POST /api/issues/transition?issue=&transition=&comment= + def transition verify_post_request access_denied unless logged_in? @@ -47,21 +65,13 @@ class Api::IssuesController < Api::ApiController private - def results_to_json(results) - json = {} - json[:issues] = results.issues.map { |issue| issue_to_json(issue) } - json[:paging] = pagination_to_json(results.paging) - json[:securityExclusions] = results.securityExclusions - json - end - def issue_to_json(issue) json = { - :key => issue.key, - :component => issue.componentKey, - :rule => issue.ruleKey.toString(), - :resolution => issue.resolution, - :status => issue.status + :key => issue.key, + :component => issue.componentKey, + :rule => issue.ruleKey.toString(), + :resolution => issue.resolution, + :status => issue.status } json[:severity] = issue.severity if issue.severity json[:desc] = issue.description if issue.description @@ -76,14 +86,13 @@ class Api::IssuesController < Api::ApiController json end - def pagination_to_json(paging) - json = { - :pageIndex => paging.pageIndex, - :pageSize => paging.pageSize, - :total => paging.total, - :pages => paging.pages + def paging_to_json(paging) + { + :pageIndex => paging.pageIndex, + :pageSize => paging.pageSize, + :total => paging.total, + :pages => paging.pages } - json end def format_java_datetime(java_date) diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb index af2ad7ab831..3a19d5890e5 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb @@ -22,16 +22,9 @@ class IssuesController < ApplicationController def index page_index = params[:page_index] || 1 - issues_result = find_issues({'pageSize' => 25, 'pageIndex' => page_index}) + issues_result = Api.issues.find({'pageSize' => 25, 'pageIndex' => page_index}) @paging = issues_result.paging @issues = issues_result.issues.collect {|issue| issue} end - protected - - def find_issues(map) - user = current_user ? current_user.id : nil - Api.issues.find(map, user) - end - end \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb b/sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb index a16f41d5098..450f07d2257 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb @@ -828,6 +828,7 @@ module ApplicationHelper html end + # TODO move to Api def to_date(java_date) java_date ? Time.at(java_date.time/1000) : nil end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/helpers/source_helper.rb b/sonar-server/src/main/webapp/WEB-INF/app/helpers/source_helper.rb index 2b50ec23f6c..3a56cd84f10 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/helpers/source_helper.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/helpers/source_helper.rb @@ -73,7 +73,7 @@ module SourceHelper end panel.html_lines=[] - html_source_lines = snapshot.highlighting_data || snapshot.source.syntax_highlighted_lines() + html_source_lines = snapshot.highlighted_source_lines || snapshot.source.syntax_highlighted_lines() line_range=sanitize_range(options[:line_range], 1..html_source_lines.length) html_source_lines.each_with_index do |source, index| diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/api.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/api.rb index 478d9a3a591..101ef78aa3d 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/api.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/api.rb @@ -18,16 +18,13 @@ # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # +# Entry points to Java API. All other Ruby classes are not considered +# as an API and can evolve through time. class Api # since 3.6 def self.issues - Java::OrgSonarServerUi::JRubyFacades.issues() - end - - # since 3.6 - def self.rules - Java::OrgSonarServerUi::JRubyFacades.rules() + Internal.issues_api end end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/api/utils.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/api/utils.rb index d22f095e580..14eae5135c0 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/api/utils.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/api/utils.rb @@ -52,8 +52,8 @@ class Api::Utils false end - def self.markdown_to_html(markdown) - markdown ? Java::OrgSonarServerUi::JRubyFacade.markdownToHtml(ERB::Util.html_escape(markdown)) : '' + def self.markdown_to_html(markdown='') + Internal.text.markdownToHtml(markdown) end # Splits a string into an array of lines diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/macro_interpreter.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/internal.rb similarity index 56% rename from sonar-server/src/main/webapp/WEB-INF/app/models/macro_interpreter.rb rename to sonar-server/src/main/webapp/WEB-INF/app/models/internal.rb index 852ed29c2a7..332bc95fdc2 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/macro_interpreter.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/internal.rb @@ -18,10 +18,28 @@ # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # -class MacroInterpreter +# All the Java components that are not published to public plugin API. +# Must NOT be used by plugins. Forward-compatibility is NOT guaranteed. +class Internal - def self.interpret_macro(text) - Java::OrgSonarServerUi::JRubyFacade.getInstance().getMacroInterpreter().interpret(text) + def self.issues + component(Java::OrgSonarServerIssue::JRubyInternalIssues.java_class) end + def self.issues_api + component(Java::OrgSonarApiIssue::JRubyIssues.java_class) + end + + def self.text + component(Java::OrgSonarServerText::JRubyText.java_class) + end + + def self.rules + component(Java::OrgSonarServerRule::JRubyRules.java_class) + end + + private + def self.component(component_java_class) + Java::OrgSonarServerPlatform::Platform.component(component_java_class) + end end \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/snapshot.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/snapshot.rb index a59ce9f9d25..ec55b8a46d2 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/snapshot.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/snapshot.rb @@ -252,8 +252,8 @@ class Snapshot < ActiveRecord::Base end end - def highlighting_data - Java::OrgSonarServerUi::JRubyFacade.getInstance().getHighlightedSourceLines(id) + def highlighted_source_lines + Internal.text.highlightedSourceLines(id) end def has_source diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/issue/_issue.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/issue/_issue.html.erb index d32da0dc041..fb18dabbf95 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/issue/_issue.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/issue/_issue.html.erb @@ -59,7 +59,7 @@ <% # TODO action plan %> <% if rule %> - <% rule_name = Api.rules.rule_name(I18n.locale, rule.rule_key) %> + <% rule_name = Internal.rules.ruleName(I18n.locale, rule.rule_key) %> <%= message('rule') -%>: diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/rules/_show.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/rules/_show.html.erb index c4e6d9b174a..1d4a91542d3 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/rules/_show.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/rules/_show.html.erb @@ -19,9 +19,9 @@