PageRenderTime 36ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/jira-project/jira-components/jira-core/src/main/java/com/atlassian/jira/web/servlet/InternalServerErrorHelper.java

https://bitbucket.org/ahmed_bilal_360factors/jira7-core
Java | 246 lines | 204 code | 35 blank | 7 comment | 15 complexity | 3b9c9d4fc762cd3738e059b9434b6b5e MD5 | raw file
Possible License(s): Apache-2.0
  1. package com.atlassian.jira.web.servlet;
  2. import com.atlassian.annotations.Internal;
  3. import com.atlassian.jira.component.ComponentAccessor;
  4. import com.atlassian.jira.config.LocaleManager;
  5. import com.atlassian.jira.config.properties.JiraSystemProperties;
  6. import com.atlassian.jira.i18n.BootstrapJiraAuthenticationContext;
  7. import com.atlassian.jira.plugin.navigation.FooterModuleDescriptor;
  8. import com.atlassian.jira.security.JiraAuthenticationContext;
  9. import com.atlassian.jira.security.PermissionManager;
  10. import com.atlassian.jira.security.Permissions;
  11. import com.atlassian.jira.user.ApplicationUser;
  12. import com.atlassian.jira.util.I18nHelper;
  13. import com.atlassian.jira.util.JiraContactHelper;
  14. import com.atlassian.jira.util.system.ExtendedSystemInfoUtilsImpl;
  15. import com.atlassian.jira.web.util.ExternalLinkUtilImpl;
  16. import com.atlassian.jira.web.util.InternalServerErrorDataSource;
  17. import com.atlassian.jira.web.util.MetalResourcesManager;
  18. import com.atlassian.plugin.PluginAccessor;
  19. import com.atlassian.soy.renderer.SoyTemplateRenderer;
  20. import com.google.common.collect.ImmutableList;
  21. import com.google.common.collect.ImmutableMap;
  22. import org.slf4j.Logger;
  23. import org.slf4j.LoggerFactory;
  24. import javax.servlet.http.HttpServletRequest;
  25. import java.io.IOException;
  26. import java.io.Writer;
  27. import java.util.LinkedHashMap;
  28. import java.util.List;
  29. import java.util.Map;
  30. import java.util.Optional;
  31. import java.util.UUID;
  32. import static com.atlassian.jira.component.ComponentAccessor.getComponentSafely;
  33. @Internal
  34. public class InternalServerErrorHelper {
  35. private static final Logger logger = LoggerFactory.getLogger(InternalServerErrorServlet.class);
  36. public static void render500ResponsePage(HttpServletRequest req, Writer writer) throws IOException {
  37. final String errorId = UUID.randomUUID().toString();
  38. try {
  39. InternalServerErrorHelper instance = new InternalServerErrorHelper(req, writer, errorId);
  40. logError(instance.getDataSource(), errorId);
  41. instance.render();
  42. } catch (RuntimeException re) {
  43. logger.error("Cannot render the 500 page for error {}", errorId, re);
  44. // Last resort... Try to give them something.
  45. writer.write(simple500ErrorPage(errorId));
  46. }
  47. }
  48. private static void logError(InternalServerErrorDataSource dataSource, String errorId) {
  49. Map<String, Object> map = new LinkedHashMap<>();
  50. map.put("errorId", errorId);
  51. map.putAll(dataSource.getGeneralInfo());
  52. logger.error(map.toString());
  53. }
  54. private static String simple500ErrorPage(String errorId) {
  55. return ""
  56. + "<html>\n"
  57. + "<head><title>JIRA &mdash; Internal Server Error</title>\n"
  58. + "<meta name=\"decorator\" content=\"none\"/>\n"
  59. + "</head><body>\n"
  60. + "<h1>JIRA &mdash; Internal Server Error</h1>\n"
  61. + "<p>Error reference: " + errorId + "</p>\n"
  62. + "<p>Unable to render full error details at this time.\n"
  63. + "Please consult the error logs for more information.</p>\n"
  64. + "</body></html>\n";
  65. }
  66. private final String id;
  67. private final HttpServletRequest req;
  68. private final ImmutableMap.Builder<String, Object> map;
  69. private final Writer writer;
  70. private final I18nHelper i18n;
  71. private final boolean isDevMode;
  72. private final boolean isAdmin;
  73. private final boolean isSysAdmin;
  74. private final InternalServerErrorDataSource dataSource;
  75. private InternalServerErrorHelper(HttpServletRequest req, Writer writer, String id) {
  76. final JiraAuthenticationContext authenticationContext = getComponentSafely(JiraAuthenticationContext.class)
  77. .orElseGet(BootstrapJiraAuthenticationContext::new);
  78. final ApplicationUser user = authenticationContext.getLoggedInUser();
  79. this.id = id;
  80. this.req = req;
  81. this.map = ImmutableMap.builder();
  82. this.writer = writer;
  83. this.i18n = authenticationContext.getI18nHelper();
  84. this.isDevMode = JiraSystemProperties.getInstance().isDevMode();
  85. this.isAdmin = isAdmin(user);
  86. this.isSysAdmin = isSysAdmin(user);
  87. this.dataSource = initializeInternalServerErrorDS(req, logger);
  88. }
  89. private boolean render() throws IOException {
  90. addBasicInfo();
  91. addTechnicalDetails(dataSource);
  92. return renderSoy();
  93. }
  94. public InternalServerErrorDataSource getDataSource() {
  95. return dataSource;
  96. }
  97. private boolean renderSoy() {
  98. // If Pico isn't initialized, then we haven't even started *thinking* about plugins, yet,
  99. // so the Soy renderer cannot possibly be available. Use presence of the PluginAccessor as
  100. // a proxy for this, as ComponentManager.getInstance().getState().isPicoInitialised() cannot
  101. // be mocked out.
  102. if (ComponentAccessor.getComponentSafely(PluginAccessor.class).isPresent()) {
  103. SoyTemplateRenderer renderer = ComponentAccessor.getOSGiComponentInstanceOfType(SoyTemplateRenderer.class);
  104. if (renderer != null) {
  105. renderer.render(writer, "jira.webresources:jira-errors", "JIRA.Templates.errors.InternalError.page", map.build());
  106. return true;
  107. }
  108. }
  109. return false;
  110. }
  111. private void addTechnicalDetails(final InternalServerErrorDataSource ds) {
  112. final ImmutableMap.Builder<String, Object> technicalDetails = ImmutableMap.builder();
  113. final boolean shouldShowFullInfo = isAdmin || isDevMode;
  114. if (shouldShowFullInfo && !Boolean.parseBoolean(req.getParameter("short"))) {
  115. ds.appendFullMessageData(technicalDetails, isSysAdmin || isDevMode);
  116. map.put("fullInfo", true);
  117. } else {
  118. ds.appendSimpleMessageData(technicalDetails);
  119. map.put("fullInfo", false);
  120. }
  121. map.put("technicalDetails", technicalDetails.build());
  122. }
  123. private void addBasicInfo() {
  124. map.put("footer", getFooterContent());
  125. map.put("helpsteps", getHelpSteps());
  126. map.put("resourcesContent", MetalResourcesManager.getMetalResources(req.getContextPath()));
  127. map.put("errorId", id);
  128. }
  129. private String getFooterContent() {
  130. return getComponentSafely(PluginAccessor.class)
  131. .map(InternalServerErrorHelper::getFooterModule)
  132. .map(footer -> footer.getModule().getFullFooterHtml(req))
  133. .orElse("");
  134. }
  135. private static FooterModuleDescriptor getFooterModule(final PluginAccessor pluginAccessor) {
  136. return (FooterModuleDescriptor) pluginAccessor.getEnabledPluginModule("jira.footer:standard-footer");
  137. }
  138. private InternalServerErrorDataSource initializeInternalServerErrorDS(final HttpServletRequest req, final Logger log) {
  139. final Optional<LocaleManager> localeManager = getComponentSafely(LocaleManager.class);
  140. ExtendedSystemInfoUtilsImpl extendedSystemInfoUtils = null;
  141. try {
  142. // Use whether or not we got the LocaleManager as a proxy for whether or not it is safe to get other
  143. // things from Pico. If we didn't get that, then don't ask for anything else, either.
  144. if (localeManager.isPresent()) {
  145. extendedSystemInfoUtils = new ExtendedSystemInfoUtilsImpl(i18n);
  146. }
  147. } catch (RuntimeException e) {
  148. log.warn("Cannot initialize ExtendedSystemInfoUtilsImpl", e);
  149. }
  150. return new InternalServerErrorDataSource(i18n, extendedSystemInfoUtils, req.getServletContext(),
  151. localeManager.orElse(null), req);
  152. }
  153. private List<String> getHelpSteps() {
  154. return isAdmin ? getJiraSupportHelpSteps() : getContactJiraAdminHelpSteps();
  155. }
  156. private List<String> getContactJiraAdminHelpSteps() {
  157. final String contactAdministratorsLink = getContactAdministratorLink();
  158. return ImmutableList.of(
  159. contactAdministratorsLink != null
  160. ? i18n.getText("500.send.with.ref.to.your.jira.admin.contact.form", "<a target=\"_blank\" href=\"" + contactAdministratorsLink + "\">", "</a>")
  161. : i18n.getText("500.send.with.ref.to.your.jira.admin")
  162. );
  163. }
  164. private List<String> getJiraSupportHelpSteps() {
  165. final ExternalLinkUtilImpl externalLinkUtil = new ExternalLinkUtilImpl();
  166. final String sac = externalLinkUtil.getProperty("external.link.jira.support.site");
  167. final String stp = req.getContextPath() + "/plugins/servlet/stp/view/#support-zip";
  168. return ImmutableList.of(
  169. i18n.getText("500.collect.when.problem.occurred"),
  170. i18n.getText("500.collect.server.log"),
  171. i18n.getText("500.create.support.zip", "<a target=\"_blank\" href=\"" + stp + "\">", "</a>"),
  172. i18n.getText("500.raise.an.issue.with.all.info", "<a href=\"" + sac + "\">", "</a>")
  173. );
  174. }
  175. private List<String> getContactCloudAdminHelpSteps() {
  176. final String contactAdministratorsLink = getContactAdministratorLink();
  177. return ImmutableList.of(
  178. contactAdministratorsLink != null
  179. ? i18n.getText("500.send.to.your.jira.admin.contact.form", "<a target=\"_blank\" href=\"" + contactAdministratorsLink + "\">", "</a>")
  180. : i18n.getText("500.send.to.your.jira.admin")
  181. );
  182. }
  183. private List<String> getCloudSupportHelpSteps() {
  184. final ExternalLinkUtilImpl externalLinkUtil = new ExternalLinkUtilImpl();
  185. final String sac = externalLinkUtil.getProperty("external.link.jira.support.site");
  186. return ImmutableList.of(i18n.getText("500.raise.an.issue.on.sac", "<a href=\"" + sac + "\">", "</a>"));
  187. }
  188. private String getContactAdministratorLink() {
  189. try {
  190. return getComponentSafely(JiraContactHelper.class)
  191. .filter(JiraContactHelper::isAdministratorContactFormEnabled)
  192. .map(helper -> helper.getAdministratorContactLink(req.getContextPath()))
  193. .orElse(null);
  194. } catch (RuntimeException re) {
  195. logger.debug("[{}] Error retrieving contact adminstrator link", id, re);
  196. return null;
  197. }
  198. }
  199. private static boolean isSysAdmin(final ApplicationUser user) {
  200. return user != null && getComponentSafely(PermissionManager.class)
  201. .map(permissionManager -> permissionManager.hasPermission(Permissions.SYSTEM_ADMIN, user))
  202. .orElse(false);
  203. }
  204. private static boolean isAdmin(final ApplicationUser user) {
  205. return user != null && getComponentSafely(PermissionManager.class)
  206. .map(permissionManager ->
  207. permissionManager.hasPermission(Permissions.ADMINISTER, user) ||
  208. permissionManager.hasPermission(Permissions.SYSTEM_ADMIN, user))
  209. .orElse(false);
  210. }
  211. }