aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-plugin-api/src/main/java/org/sonar/api/Plugin.java
blob: 659f5c6e948c0d5c7be01cde9c8075d4e459c1b7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/*
 * SonarQube
 * Copyright (C) 2009-2016 SonarSource SA
 * mailto:contact AT sonarsource DOT com
 *
 * This program 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.
 *
 * This program 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.api;

import com.google.common.annotations.Beta;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.sonar.api.utils.Version;

import static java.util.Arrays.asList;
import static java.util.Objects.requireNonNull;

/**
 * Entry-point for plugins to inject extensions into SonarQube.
 * <p>The JAR manifest must declare the name of the implementation class in the property <code>Plugin-Class</code>.
 * This property is automatically set by sonar-packaging-maven-plugin when building plugin.
 * <p>Example of implementation
 * <pre>
 * package com.mycompany.sonarqube;
 * public class MyPlugin implements Plugin {
 *  {@literal @}Override
 *   public void define(Context context) {
 *     context.addExtensions(MySensor.class, MyRules.class);
 *     if (context.getSonarQubeVersion().isGreaterThanOrEqual(SonarQubeVersion.V5_6)) {
 *       // Extension which supports only versions 5.6 and greater
 *       // See org.sonar.api.SonarQubeVersion for more details.
 *       context.addExtension(MyNewExtension.class);
 *     }
 *   }
 * }
 * </pre>
 *
 * <p>Example of pom.xml
 * <pre>
 * &lt;project&gt;
 *   ...
 *   &lt;packaging&gt;sonar-plugin&lt;/packaging&gt;
 *
 *   &lt;build&gt;
 *     &lt;plugins&gt;
 *       &lt;plugin&gt;
 *         &lt;groupId&gt;org.sonarsource.sonar-packaging-maven-plugin&lt;/groupId&gt;
 *         &lt;artifactId&gt;sonar-packaging-maven-plugin&lt;/artifactId&gt;
 *         &lt;extensions&gt;true&lt;/extensions&gt;
 *         &lt;configuration&gt;
 *           &lt;pluginClass&gt;com.mycompany.sonarqube.MyPlugin&lt;/pluginClass&gt;
 *         &lt;/configuration&gt;
 *       &lt;/plugin&gt;
 *     &lt;/plugins&gt;
 *   &lt;/build&gt;
 * &lt;/project&gt;
 * </pre>
 *
 * <p>Example of test
 * <pre>
 * MyPlugin underTest = new MyPlugin();
 *
 *{@literal @}Test
 * public void test_plugin_extensions_compatible_with_5_5() {
 *   Plugin.Context context = new Plugin.Context(SonarQubeVersion.V5_5);
 *   underTest.define(context);
 *   assertThat(context.getExtensions()).hasSize(4);
 * }
 * </pre>
 *
 * @since 5.5
 */
@Beta
public interface Plugin {

  class Context {
    private final Version version;
    private final List extensions = new ArrayList();

    public Context(Version version) {
      this.version = version;
    }

    /**
     * Runtime version of SonarQube
     */
    public Version getSonarQubeVersion() {
      return version;
    }

    /**
     * Add an extension as :
     * <ul>
     *   <li>a Class that is annotated with {@link org.sonar.api.batch.BatchSide}, {@link org.sonar.api.server.ServerSide}
     *   or {@link org.sonar.api.ce.ComputeEngineSide}. The extension will be instantiated once. Its dependencies are
     *   injected through constructor parameters.</li>
     *   <li>an instance that is annotated with {@link org.sonar.api.batch.BatchSide}, {@link org.sonar.api.server.ServerSide}
     *   or {@link org.sonar.api.ce.ComputeEngineSide}.</li>
     * </ul>
     * Only a single component can be registered for a class. It's not allowed for example to register:
     * <ul>
     *   <li>two MyExtension.class</li>
     *   <li>MyExtension.class and new MyExtension()</li>
     * </ul>
     */
    public Context addExtension(Object extension) {
      requireNonNull(extension);
      this.extensions.add(extension);
      return this;
    }

    /**
     * @see #addExtension(Object)
     */
    public Context addExtensions(Collection extensions) {
      this.extensions.addAll(extensions);
      return this;
    }

    /**
     * @see #addExtension(Object)
     */
    public Context addExtensions(Object first, Object second, Object... others) {
      addExtension(first);
      addExtension(second);
      addExtensions(asList(others));
      return this;
    }

    public List getExtensions() {
      return extensions;
    }
  }

  /**
   * This method is executed at runtime when:
   * <ul>
   *   <li>Web Server starts</li>
   *   <li>Compute Engine starts</li>
   *   <li>Scanner starts</li>
   * </ul>
   */
  void define(Context context);
}