/java/com/google/gerrit/server/plugins/ServerPlugin.java
Java | 313 lines | 268 code | 32 blank | 13 comment | 57 complexity | d6c17c0535288ff4c285472973f83002 MD5 | raw file
1// Copyright (C) 2012 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.gerrit.server.plugins;
16
17import com.google.common.base.Strings;
18import com.google.common.collect.Lists;
19import com.google.gerrit.common.Nullable;
20import com.google.gerrit.extensions.registration.RegistrationHandle;
21import com.google.gerrit.extensions.registration.ReloadableRegistrationHandle;
22import com.google.gerrit.lifecycle.LifecycleManager;
23import com.google.gerrit.server.PluginUser;
24import com.google.gerrit.server.util.RequestContext;
25import com.google.inject.Guice;
26import com.google.inject.Injector;
27import com.google.inject.Module;
28import java.io.IOException;
29import java.nio.file.Path;
30import java.util.ArrayList;
31import java.util.List;
32import java.util.jar.Attributes;
33import java.util.jar.Manifest;
34import org.eclipse.jgit.internal.storage.file.FileSnapshot;
35import org.slf4j.Logger;
36import org.slf4j.LoggerFactory;
37
38public class ServerPlugin extends Plugin {
39 private static final Logger log = LoggerFactory.getLogger(ServerPlugin.class);
40
41 private final Manifest manifest;
42 private final PluginContentScanner scanner;
43 private final Path dataDir;
44 private final String pluginCanonicalWebUrl;
45 private final ClassLoader classLoader;
46 private final String metricsPrefix;
47 protected Class<? extends Module> sysModule;
48 protected Class<? extends Module> sshModule;
49 protected Class<? extends Module> httpModule;
50
51 private Injector sysInjector;
52 private Injector sshInjector;
53 private Injector httpInjector;
54 private LifecycleManager serverManager;
55 private List<ReloadableRegistrationHandle<?>> reloadableHandles;
56
57 public ServerPlugin(
58 String name,
59 String pluginCanonicalWebUrl,
60 PluginUser pluginUser,
61 Path srcJar,
62 FileSnapshot snapshot,
63 PluginContentScanner scanner,
64 Path dataDir,
65 ClassLoader classLoader,
66 String metricsPrefix)
67 throws InvalidPluginException {
68 super(
69 name,
70 srcJar,
71 pluginUser,
72 snapshot,
73 scanner == null ? ApiType.PLUGIN : Plugin.getApiType(getPluginManifest(scanner)));
74 this.pluginCanonicalWebUrl = pluginCanonicalWebUrl;
75 this.scanner = scanner;
76 this.dataDir = dataDir;
77 this.classLoader = classLoader;
78 this.manifest = scanner == null ? null : getPluginManifest(scanner);
79 this.metricsPrefix = metricsPrefix;
80 if (manifest != null) {
81 loadGuiceModules(manifest, classLoader);
82 }
83 }
84
85 public ServerPlugin(
86 String name,
87 String pluginCanonicalWebUrl,
88 PluginUser pluginUser,
89 Path srcJar,
90 FileSnapshot snapshot,
91 PluginContentScanner scanner,
92 Path dataDir,
93 ClassLoader classLoader)
94 throws InvalidPluginException {
95 this(
96 name,
97 pluginCanonicalWebUrl,
98 pluginUser,
99 srcJar,
100 snapshot,
101 scanner,
102 dataDir,
103 classLoader,
104 null);
105 }
106
107 private void loadGuiceModules(Manifest manifest, ClassLoader classLoader)
108 throws InvalidPluginException {
109 Attributes main = manifest.getMainAttributes();
110 String sysName = main.getValue("Gerrit-Module");
111 String sshName = main.getValue("Gerrit-SshModule");
112 String httpName = main.getValue("Gerrit-HttpModule");
113
114 if (!Strings.isNullOrEmpty(sshName) && getApiType() != Plugin.ApiType.PLUGIN) {
115 throw new InvalidPluginException(
116 String.format(
117 "Using Gerrit-SshModule requires Gerrit-ApiType: %s", Plugin.ApiType.PLUGIN));
118 }
119
120 try {
121 this.sysModule = load(sysName, classLoader);
122 this.sshModule = load(sshName, classLoader);
123 this.httpModule = load(httpName, classLoader);
124 } catch (ClassNotFoundException e) {
125 throw new InvalidPluginException("Unable to load plugin Guice Modules", e);
126 }
127 }
128
129 @SuppressWarnings("unchecked")
130 protected static Class<? extends Module> load(String name, ClassLoader pluginLoader)
131 throws ClassNotFoundException {
132 if (Strings.isNullOrEmpty(name)) {
133 return null;
134 }
135
136 Class<?> clazz = Class.forName(name, false, pluginLoader);
137 if (!Module.class.isAssignableFrom(clazz)) {
138 throw new ClassCastException(
139 String.format("Class %s does not implement %s", name, Module.class.getName()));
140 }
141 return (Class<? extends Module>) clazz;
142 }
143
144 Path getDataDir() {
145 return dataDir;
146 }
147
148 String getPluginCanonicalWebUrl() {
149 return pluginCanonicalWebUrl;
150 }
151
152 String getMetricsPrefix() {
153 return metricsPrefix;
154 }
155
156 private static Manifest getPluginManifest(PluginContentScanner scanner)
157 throws InvalidPluginException {
158 try {
159 return scanner.getManifest();
160 } catch (IOException e) {
161 throw new InvalidPluginException("Cannot get plugin manifest", e);
162 }
163 }
164
165 @Override
166 @Nullable
167 public String getVersion() {
168 Attributes main = manifest.getMainAttributes();
169 return main.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
170 }
171
172 @Override
173 protected boolean canReload() {
174 Attributes main = manifest.getMainAttributes();
175 String v = main.getValue("Gerrit-ReloadMode");
176 if (Strings.isNullOrEmpty(v) || "reload".equalsIgnoreCase(v)) {
177 return true;
178 } else if ("restart".equalsIgnoreCase(v)) {
179 return false;
180 } else {
181 log.warn(
182 String.format(
183 "Plugin %s has invalid Gerrit-ReloadMode %s; assuming restart", getName(), v));
184 return false;
185 }
186 }
187
188 @Override
189 protected void start(PluginGuiceEnvironment env) throws Exception {
190 RequestContext oldContext = env.enter(this);
191 try {
192 startPlugin(env);
193 } finally {
194 env.exit(oldContext);
195 }
196 }
197
198 private void startPlugin(PluginGuiceEnvironment env) throws Exception {
199 Injector root = newRootInjector(env);
200 serverManager = new LifecycleManager();
201 serverManager.add(root);
202
203 AutoRegisterModules auto = null;
204 if (sysModule == null && sshModule == null && httpModule == null) {
205 auto = new AutoRegisterModules(getName(), env, scanner, classLoader);
206 auto.discover();
207 }
208
209 if (sysModule != null) {
210 sysInjector = root.createChildInjector(root.getInstance(sysModule));
211 serverManager.add(sysInjector);
212 } else if (auto != null && auto.sysModule != null) {
213 sysInjector = root.createChildInjector(auto.sysModule);
214 serverManager.add(sysInjector);
215 } else {
216 sysInjector = root;
217 }
218
219 if (env.hasSshModule()) {
220 List<Module> modules = new ArrayList<>();
221 if (getApiType() == ApiType.PLUGIN) {
222 modules.add(env.getSshModule());
223 }
224 if (sshModule != null) {
225 modules.add(sysInjector.getInstance(sshModule));
226 sshInjector = sysInjector.createChildInjector(modules);
227 serverManager.add(sshInjector);
228 } else if (auto != null && auto.sshModule != null) {
229 modules.add(auto.sshModule);
230 sshInjector = sysInjector.createChildInjector(modules);
231 serverManager.add(sshInjector);
232 }
233 }
234
235 if (env.hasHttpModule()) {
236 List<Module> modules = new ArrayList<>();
237 if (getApiType() == ApiType.PLUGIN) {
238 modules.add(env.getHttpModule());
239 }
240 if (httpModule != null) {
241 modules.add(sysInjector.getInstance(httpModule));
242 httpInjector = sysInjector.createChildInjector(modules);
243 serverManager.add(httpInjector);
244 } else if (auto != null && auto.httpModule != null) {
245 modules.add(auto.httpModule);
246 httpInjector = sysInjector.createChildInjector(modules);
247 serverManager.add(httpInjector);
248 }
249 }
250
251 serverManager.start();
252 }
253
254 private Injector newRootInjector(PluginGuiceEnvironment env) {
255 List<Module> modules = Lists.newArrayListWithCapacity(2);
256 if (getApiType() == ApiType.PLUGIN) {
257 modules.add(env.getSysModule());
258 }
259 modules.add(new ServerPluginInfoModule(this, env.getServerMetrics()));
260 return Guice.createInjector(modules);
261 }
262
263 @Override
264 protected void stop(PluginGuiceEnvironment env) {
265 if (serverManager != null) {
266 RequestContext oldContext = env.enter(this);
267 try {
268 serverManager.stop();
269 } finally {
270 env.exit(oldContext);
271 }
272 serverManager = null;
273 sysInjector = null;
274 sshInjector = null;
275 httpInjector = null;
276 }
277 }
278
279 @Override
280 public Injector getSysInjector() {
281 return sysInjector;
282 }
283
284 @Override
285 @Nullable
286 public Injector getSshInjector() {
287 return sshInjector;
288 }
289
290 @Override
291 @Nullable
292 public Injector getHttpInjector() {
293 return httpInjector;
294 }
295
296 @Override
297 public void add(RegistrationHandle handle) {
298 if (serverManager != null) {
299 if (handle instanceof ReloadableRegistrationHandle) {
300 if (reloadableHandles == null) {
301 reloadableHandles = new ArrayList<>();
302 }
303 reloadableHandles.add((ReloadableRegistrationHandle<?>) handle);
304 }
305 serverManager.add(handle);
306 }
307 }
308
309 @Override
310 public PluginContentScanner getContentScanner() {
311 return scanner;
312 }
313}