You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

GitblitContext.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. /*
  2. * Copyright 2011 gitblit.com.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.gitblit.servlet;
  17. import java.io.File;
  18. import java.io.FileNotFoundException;
  19. import java.io.FileOutputStream;
  20. import java.io.IOException;
  21. import java.io.InputStream;
  22. import java.io.OutputStream;
  23. import java.text.MessageFormat;
  24. import java.util.ArrayList;
  25. import java.util.List;
  26. import javax.naming.Context;
  27. import javax.naming.InitialContext;
  28. import javax.naming.NamingException;
  29. import javax.servlet.ServletContext;
  30. import javax.servlet.ServletContextEvent;
  31. import com.gitblit.Constants;
  32. import com.gitblit.DaggerModule;
  33. import com.gitblit.FileSettings;
  34. import com.gitblit.IStoredSettings;
  35. import com.gitblit.Keys;
  36. import com.gitblit.WebXmlSettings;
  37. import com.gitblit.dagger.DaggerContext;
  38. import com.gitblit.manager.IAuthenticationManager;
  39. import com.gitblit.manager.IFederationManager;
  40. import com.gitblit.manager.IGitblit;
  41. import com.gitblit.manager.IManager;
  42. import com.gitblit.manager.INotificationManager;
  43. import com.gitblit.manager.IProjectManager;
  44. import com.gitblit.manager.IRepositoryManager;
  45. import com.gitblit.manager.IRuntimeManager;
  46. import com.gitblit.manager.IUserManager;
  47. import com.gitblit.utils.ContainerUtils;
  48. import com.gitblit.utils.StringUtils;
  49. import dagger.ObjectGraph;
  50. /**
  51. * This class is the main entry point for the entire webapp. It is a singleton
  52. * created manually by Gitblit GO or dynamically by the WAR/Express servlet
  53. * container. This class instantiates and starts all managers. Servlets and
  54. * filters are instantiated defined in web.xml and instantiated by the servlet
  55. * container, but those servlets and filters use Dagger to manually inject their
  56. * dependencies.
  57. *
  58. * @author James Moger
  59. *
  60. */
  61. public class GitblitContext extends DaggerContext {
  62. private static GitblitContext gitblit;
  63. private final List<IManager> managers = new ArrayList<IManager>();
  64. private final IStoredSettings goSettings;
  65. private final File goBaseFolder;
  66. /**
  67. * Construct a Gitblit WAR/Express context.
  68. */
  69. public GitblitContext() {
  70. this.goSettings = null;
  71. this.goBaseFolder = null;
  72. gitblit = this;
  73. }
  74. /**
  75. * Construct a Gitblit GO context.
  76. *
  77. * @param settings
  78. * @param baseFolder
  79. */
  80. public GitblitContext(IStoredSettings settings, File baseFolder) {
  81. this.goSettings = settings;
  82. this.goBaseFolder = baseFolder;
  83. gitblit = this;
  84. }
  85. /**
  86. * This method is only used for unit and integration testing.
  87. *
  88. * @param managerClass
  89. * @return a manager
  90. */
  91. @SuppressWarnings("unchecked")
  92. public static <X extends IManager> X getManager(Class<X> managerClass) {
  93. for (IManager manager : gitblit.managers) {
  94. if (managerClass.isAssignableFrom(manager.getClass())) {
  95. return (X) manager;
  96. }
  97. }
  98. return null;
  99. }
  100. /**
  101. * Returns Gitblit's Dagger injection modules.
  102. */
  103. @Override
  104. protected Object [] getModules() {
  105. return new Object [] { new DaggerModule() };
  106. }
  107. /**
  108. * Configure Gitblit from the web.xml, if no configuration has already been
  109. * specified.
  110. *
  111. * @see ServletContextListener.contextInitialize(ServletContextEvent)
  112. */
  113. @Override
  114. public final void contextInitialized(ServletContextEvent contextEvent) {
  115. ServletContext context = contextEvent.getServletContext();
  116. configureContext(context);
  117. }
  118. /**
  119. * Prepare runtime settings and start all manager instances.
  120. */
  121. protected void configureContext(ServletContext context) {
  122. ObjectGraph injector = getInjector(context);
  123. // create the runtime settings object
  124. IStoredSettings runtimeSettings = injector.get(IStoredSettings.class);
  125. final File baseFolder;
  126. if (goSettings != null) {
  127. // Gitblit GO
  128. baseFolder = configureGO(context, goSettings, goBaseFolder, runtimeSettings);
  129. } else {
  130. // servlet container
  131. WebXmlSettings webxmlSettings = new WebXmlSettings(context);
  132. String contextRealPath = context.getRealPath("/");
  133. File contextFolder = (contextRealPath != null) ? new File(contextRealPath) : null;
  134. if (!StringUtils.isEmpty(System.getenv("OPENSHIFT_DATA_DIR"))) {
  135. // RedHat OpenShift
  136. baseFolder = configureExpress(context, webxmlSettings, contextFolder, runtimeSettings);
  137. } else {
  138. // standard WAR
  139. baseFolder = configureWAR(context, webxmlSettings, contextFolder, runtimeSettings);
  140. }
  141. // Test for Tomcat forward-slash/%2F issue and auto-adjust settings
  142. ContainerUtils.CVE_2007_0450.test(runtimeSettings);
  143. }
  144. // Manually configure IRuntimeManager
  145. logManager(IRuntimeManager.class);
  146. IRuntimeManager runtime = injector.get(IRuntimeManager.class);
  147. runtime.setBaseFolder(baseFolder);
  148. runtime.getStatus().isGO = goSettings != null;
  149. runtime.getStatus().servletContainer = context.getServerInfo();
  150. runtime.start();
  151. managers.add(runtime);
  152. // start all other managers
  153. startManager(injector, INotificationManager.class);
  154. startManager(injector, IUserManager.class);
  155. startManager(injector, IAuthenticationManager.class);
  156. startManager(injector, IRepositoryManager.class);
  157. startManager(injector, IProjectManager.class);
  158. startManager(injector, IFederationManager.class);
  159. startManager(injector, IGitblit.class);
  160. logger.info("");
  161. logger.info("All managers started.");
  162. logger.info("");
  163. }
  164. protected <X extends IManager> X startManager(ObjectGraph injector, Class<X> clazz) {
  165. logManager(clazz);
  166. X x = injector.get(clazz);
  167. x.start();
  168. managers.add(x);
  169. return x;
  170. }
  171. protected void logManager(Class<? extends IManager> clazz) {
  172. logger.info("");
  173. logger.info("----[{}]----", clazz.getName());
  174. }
  175. /**
  176. * Gitblit is being shutdown either because the servlet container is
  177. * shutting down or because the servlet container is re-deploying Gitblit.
  178. */
  179. @Override
  180. protected void destroyContext(ServletContext context) {
  181. logger.info("Gitblit context destroyed by servlet container.");
  182. for (IManager manager : managers) {
  183. logger.debug("stopping {}", manager.getClass().getSimpleName());
  184. manager.stop();
  185. }
  186. }
  187. /**
  188. * Configures Gitblit GO
  189. *
  190. * @param context
  191. * @param settings
  192. * @param baseFolder
  193. * @param runtimeSettings
  194. * @return the base folder
  195. */
  196. protected File configureGO(
  197. ServletContext context,
  198. IStoredSettings goSettings,
  199. File goBaseFolder,
  200. IStoredSettings runtimeSettings) {
  201. logger.debug("configuring Gitblit GO");
  202. // merge the stored settings into the runtime settings
  203. //
  204. // if runtimeSettings is also a FileSettings w/o a specified target file,
  205. // the target file for runtimeSettings is set to "localSettings".
  206. runtimeSettings.merge(goSettings);
  207. File base = goBaseFolder;
  208. return base;
  209. }
  210. /**
  211. * Configures a standard WAR instance of Gitblit.
  212. *
  213. * @param context
  214. * @param webxmlSettings
  215. * @param contextFolder
  216. * @param runtimeSettings
  217. * @return the base folder
  218. */
  219. protected File configureWAR(
  220. ServletContext context,
  221. WebXmlSettings webxmlSettings,
  222. File contextFolder,
  223. IStoredSettings runtimeSettings) {
  224. // Gitblit is running in a standard servlet container
  225. logger.debug("configuring Gitblit WAR");
  226. logger.info("WAR contextFolder is " + ((contextFolder != null) ? contextFolder.getAbsolutePath() : "<empty>"));
  227. String path = webxmlSettings.getString(Constants.baseFolder, Constants.contextFolder$ + "/WEB-INF/data");
  228. if (path.contains(Constants.contextFolder$) && contextFolder == null) {
  229. // warn about null contextFolder (issue-199)
  230. logger.error("");
  231. logger.error(MessageFormat.format("\"{0}\" depends on \"{1}\" but \"{2}\" is returning NULL for \"{1}\"!",
  232. Constants.baseFolder, Constants.contextFolder$, context.getServerInfo()));
  233. logger.error(MessageFormat.format("Please specify a non-parameterized path for <context-param> {0} in web.xml!!", Constants.baseFolder));
  234. logger.error(MessageFormat.format("OR configure your servlet container to specify a \"{0}\" parameter in the context configuration!!", Constants.baseFolder));
  235. logger.error("");
  236. }
  237. try {
  238. // try to lookup JNDI env-entry for the baseFolder
  239. InitialContext ic = new InitialContext();
  240. Context env = (Context) ic.lookup("java:comp/env");
  241. String val = (String) env.lookup("baseFolder");
  242. if (!StringUtils.isEmpty(val)) {
  243. path = val;
  244. }
  245. } catch (NamingException n) {
  246. logger.error("Failed to get JNDI env-entry: " + n.getExplanation());
  247. }
  248. File base = com.gitblit.utils.FileUtils.resolveParameter(Constants.contextFolder$, contextFolder, path);
  249. base.mkdirs();
  250. // try to extract the data folder resource to the baseFolder
  251. File localSettings = new File(base, "gitblit.properties");
  252. if (!localSettings.exists()) {
  253. extractResources(context, "/WEB-INF/data/", base);
  254. }
  255. // delegate all config to baseFolder/gitblit.properties file
  256. FileSettings fileSettings = new FileSettings(localSettings.getAbsolutePath());
  257. // merge the stored settings into the runtime settings
  258. //
  259. // if runtimeSettings is also a FileSettings w/o a specified target file,
  260. // the target file for runtimeSettings is set to "localSettings".
  261. runtimeSettings.merge(fileSettings);
  262. return base;
  263. }
  264. /**
  265. * Configures an OpenShift instance of Gitblit.
  266. *
  267. * @param context
  268. * @param webxmlSettings
  269. * @param contextFolder
  270. * @param runtimeSettings
  271. * @return the base folder
  272. */
  273. private File configureExpress(
  274. ServletContext context,
  275. WebXmlSettings webxmlSettings,
  276. File contextFolder,
  277. IStoredSettings runtimeSettings) {
  278. // Gitblit is running in OpenShift/JBoss
  279. logger.debug("configuring Gitblit Express");
  280. String openShift = System.getenv("OPENSHIFT_DATA_DIR");
  281. File base = new File(openShift);
  282. logger.info("EXPRESS contextFolder is " + contextFolder.getAbsolutePath());
  283. // Copy the included scripts to the configured groovy folder
  284. String path = webxmlSettings.getString(Keys.groovy.scriptsFolder, "groovy");
  285. File localScripts = com.gitblit.utils.FileUtils.resolveParameter(Constants.baseFolder$, base, path);
  286. if (!localScripts.exists()) {
  287. File warScripts = new File(contextFolder, "/WEB-INF/data/groovy");
  288. if (!warScripts.equals(localScripts)) {
  289. try {
  290. com.gitblit.utils.FileUtils.copy(localScripts, warScripts.listFiles());
  291. } catch (IOException e) {
  292. logger.error(MessageFormat.format(
  293. "Failed to copy included Groovy scripts from {0} to {1}",
  294. warScripts, localScripts));
  295. }
  296. }
  297. }
  298. // merge the WebXmlSettings into the runtime settings (for backwards-compatibilty)
  299. runtimeSettings.merge(webxmlSettings);
  300. // settings are to be stored in openshift/gitblit.properties
  301. File localSettings = new File(base, "gitblit.properties");
  302. FileSettings fileSettings = new FileSettings(localSettings.getAbsolutePath());
  303. // merge the stored settings into the runtime settings
  304. //
  305. // if runtimeSettings is also a FileSettings w/o a specified target file,
  306. // the target file for runtimeSettings is set to "localSettings".
  307. runtimeSettings.merge(fileSettings);
  308. return base;
  309. }
  310. protected void extractResources(ServletContext context, String path, File toDir) {
  311. for (String resource : context.getResourcePaths(path)) {
  312. // extract the resource to the directory if it does not exist
  313. File f = new File(toDir, resource.substring(path.length()));
  314. if (!f.exists()) {
  315. InputStream is = null;
  316. OutputStream os = null;
  317. try {
  318. if (resource.charAt(resource.length() - 1) == '/') {
  319. // directory
  320. f.mkdirs();
  321. extractResources(context, resource, f);
  322. } else {
  323. // file
  324. f.getParentFile().mkdirs();
  325. is = context.getResourceAsStream(resource);
  326. os = new FileOutputStream(f);
  327. byte [] buffer = new byte[4096];
  328. int len = 0;
  329. while ((len = is.read(buffer)) > -1) {
  330. os.write(buffer, 0, len);
  331. }
  332. }
  333. } catch (FileNotFoundException e) {
  334. logger.error("Failed to find resource \"" + resource + "\"", e);
  335. } catch (IOException e) {
  336. logger.error("Failed to copy resource \"" + resource + "\" to " + f, e);
  337. } finally {
  338. if (is != null) {
  339. try {
  340. is.close();
  341. } catch (IOException e) {
  342. // ignore
  343. }
  344. }
  345. if (os != null) {
  346. try {
  347. os.close();
  348. } catch (IOException e) {
  349. // ignore
  350. }
  351. }
  352. }
  353. }
  354. }
  355. }
  356. }