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