Java/Scala等代码能否判断自己是否正在被Tomcat运行?

3

我的Web应用程序数据存储在不同位置访问的数据库中。没有通用代码可以同时访问这两个位置。因此,我想在运行时知道当前环境。实际上,代码的运行方式取决于是否在Tomcat内部或外部运行,因此我希望在运行时检测它。如何做到这一点?

6个回答

3
这似乎不是代码应该检测的内容。在同一类中负责检测环境并采取不同行动的负担通常不是一个理想的想法。如果需要执行不同操作,您应该创建几个不同的数据访问类(它们实现相同的接口,并可能在它们都扩展的抽象类中共享公共代码)。然后,您的Web Servlet将实例化并使用一个类进行其数据库访问,而您的非Web代码将实例化另一个类进行数据库访问。
如果出于某种原因您确实需要在单个类中检测是否在Web abb中,我认为一种比较干净的方法可能是在服务器运行时设置系统属性(我认为是-D命令行选项),然后在您的类中查找该属性是否已设置。如果是,则执行Web逻辑,否则执行非Web逻辑。

无论我是让一个类有所不同还是使用两个类,都完全无关紧要。决策必须在运行时进行,并且我想知道如何做到这一点。我已经猜测可能有一个系统属性,但是它是什么? - Matt Roberts
你可以随意命名系统属性,并进行检查。在 Web 容器中运行并不完全无关紧要,因为当您在 Web 上下文中运行时,将使用 Servlet 类,而在非 Web 上下文中运行时则使用常规类。除非您在非 Web 上下文中使用 Servlet 类,这没有太多意义。 - Kaleb Brasee

2

好的,撇开所有注意事项不谈,我想在讨论中提出我的两分钱意见。

我认为ServerDetector使用系统类路径上特定类的存在来确定正在运行的服务器类型有些缺陷:如果包含该类的JAR文件只是恰好在类路径上但未被使用怎么办?我建议使用堆栈跟踪元素来检测正在运行的服务器的不同方法。这可能并不完美,但我认为它比检测类路径上某个类的存在要好。

以下是Scala代码:

def inTomcat: Boolean = (new Throwable).getStackTrace.exists(_.getClassName.startsWith("org.apache.catalina"))

你说得很有道理,沃尔特。你提出的问题是我们在glassfish中遇到的问题(我并不是liferay的相关人员,只是一个用户)。Glassfish使用tomcat作为其servlet引擎,但服务器是glassfish,而不是tomcat。然后会执行tomcat特定的代码,因为在classpath中存在两个类,而tomcat检查发生在glassfish检查之前。我认为你的方法也容易受到这个错误的影响。 - digitaljoel
@digitaljoel 在这种情况下,您需要在servlet代码中抛出任意RuntimeException,并选择一个在调用路径上高层的Glassfish类并进行检查。 - Walter Chang
对的。我的意思是LifeRay代码存在问题,它在检查Glassfish类之前先检查了Tomcat类。然后,因为它找到了Tomcat类,它就假定Tomcat是服务器并且不再检查其他内容,所以重要的是你按正确顺序进行检查。在这种情况下,你需要在检查Tomcat之前检查Glassfish,或者至少理解情况,使检查足够智能化以应对它。 - digitaljoel

2
我同意kbrasee的看法,这不是一个很好的处境,如果可以的话最好尽快离开。
假设你别无选择,必须这么做,Liferay实现中就这么做。请查看ServerDetector类
你会注意到在_detect方法中(靠近底部),他们会寻找一个类,如果它在给定的服务器上运行,则认为该类存在。他们在TOMCAT_BOOTSTRAP_CLASS和TOMCAT_EMBEDDED_CLASS常量中定义这些类。
我们曾经遇到过检测glassfish内嵌tomcat的问题。我不确定我提供的链接版本是否已修复此问题,但对你来说可能不是问题。

谢谢,这正是我所需要的,我自己从未想过!我知道询问是值得的 :) - Matt Roberts

0

如果这不是为了快速的hack,而是为了长期的解决方案,请注意选择任何启发式决策的API都会最终失效。OSGi的整个重点是将JVM中不相关的部分隐藏起来。

我认为对于这种特定情况,最好的方法是查看是否有可用的JNDI环境来确定您是否在Tomcat内部。如果没有,则在独立环境中,如果有,则在Tomcat内部。


谢谢,又一个好的建议。但愿我能标记两个答案 :) 我总是在寻找长期解决方案,但如果这是使其工作的方法,我也很乐意退回到 hack。 - Matt Roberts

0

我理解其他答案的警告,但有时在早期开发阶段,我会通过CLI和Tomcat执行我的类,并且我只想知道我正在运行哪个环境,而不需要大量的样板文件。

我使用这个Java函数

public static boolean _amServer() {
  StackTraceElement[] elements = new Throwable().getStackTrace();

  for (StackTraceElement element : elements) {
    if (element.getClassName().equals("org.apache.catalina.core.StandardEngineValve")) {
      return true;
    }
  }

  return false;
}

0

我发现以下解决方案简单明了(取自另一个Stack Overflow问答):

String path = getClass().getProtectionDomain().getCodeSource().getLocation().getFile();
boolean insideWarFile = path.contains("WEB-INF");

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接