我们有一个servlet需要某些变量,如密码,加密盐等,不能永久保存在文件系统中。这就是我们目前的工作(摘要):
在初始化期间,
Perl脚本将ReadMode设置为2以屏蔽stdout echo,提示用户输入变量,过滤已知文件以放入它们并调用tomcat / bin / startup.sh
servlet init()方法从文件中读取变量并删除它(文件)。
问题: 重新编译WAR时,tomcat会尝试部署它(autodeploy = true),这是我们想要的。但是数据文件不再存在,因此抛出了FileNotFoundException(正确地说是这样)。
问题:servlet可以使用属性或某些HashMap / Table,在手动启动期间可以存储一些变量吗?我们的想法是,如果在重新部署期间数据文件不存在,init()可以检查它们。谢谢, - MS。
答案 0 :(得分:4)
当你调用startup.sh时,如何将它们作为系统属性传递,即。 'bin / startup.sh -DencryptionSalt = foobar'?然后,您可以在JVM的持续时间内调用System.getProperty(“encryptionSalt”)。
答案 1 :(得分:2)
将易失性数据放入JNDI。重新部署之间不会清除JNDI。在init
期间,您的servlet仍然可以执行相同的操作,以确保JNDI中的数据是最新的。
我不记得JNDI读/写是否是线程安全的,但它不是,你总是可以放一个线程安全的对象(例如ConcurrentHashMap
)并毫无问题地使用它。
MS编辑:
==========
restart.pl:
==========
ReadMode 2; print "\nEnter spice: "; chomp ($spice = <STDIN>); print "\n";
ReadMode 0;
open (FP, ">$spicefile") or die "$0: Cannot write file $spicefile: $!\n";
print FP "$spice\n"; close (FP);
system "bin/shutdown.sh"; sleep 8;
system "bin/startup.sh"; sleep 8; system "wget $tomcaturl";
system '/bin/rm -rf *spicefile*'; # delete wget output file
foreach $sCount (1..10) { # give it 10 more secs
sleep 1;
if (-f $spicefile) {
print "$0: Waiting on servlet to delete spicefile [$sCount]\n"
} else {
print "$0: Successful servlet initialization, no spicefile\n";
exit 0
}}
print "\n$0: deleting file $spicefile ...\n"; # Error condition
system "unlink $spicefile";
exit 1;
===========
context.xml
===========
<Resource name="MyServlet/upsBean" auth="Container" type="packageName.UPSBean"
factory="org.apache.naming.factory.BeanFactory" readOnly="false"/>
===================
app/WEB-INF/web.xml
===================
<listener>
<listener-class>packageName.DBInterface</listener-class>
</listener>
<resource-env-ref>
<description>Use spice as transferable during redeployment</description>
<resource-env-ref-name>MyServlet/upsBean</resource-env-ref-name>
<resource-env-ref-type>packageName.UPSBean</resource-env-ref-type>
</resource-env-ref>
==============
MyServlet.java:
==============
init() {
if (new File (spiceFile).exists()) # Same name in restart.pl
dbi = new DBInterface (spiceFile);
else
dbi = new DBInterface (); # Redeployment
otherInitializations (dbi.getSpice());
}
================
DBInterface.java:
================
public DBInterface () {
// Comment out following block if contextInitialized works
FileInputStream fin = new FileInputStream (safeDepositBox);
ObjectInputStream ois = new ObjectInputStream (fin);
UPSBean upsBean = (UPSBean) ois.readObject();
ois.close();
spice = upsBean.getSpice();
dbiIndex = 2;
// do stuff with spice
}
public DBInterface (String spiceFileName) {
File file = new File (spiceFileName);
BufferedReader br = new BufferedReader (new FileReader (file));
spice = br.readLine();
br.close();
file.delete();
dbiIndex = 1;
// Delete following block if contextInitialized works
UPSBean upsBean = new UPSBean();
upsBean.setSpice (spice);
FileOutputStream fout = new FileOutputStream (safeDepositBox);
ObjectOutputStream oos = new ObjectOutputStream (fout);
oos.writeObject (upsBean);
oos.flush();
oos.close();
// do stuff with spice and if it works, ...
// contextInitialized (null);
}
// Above is working currently, would like the following to work
public void contextDestroyed(ServletContextEvent sce) {
System.setProperty ("spice", spice);
System.out.println ("[DBInterface" + dbiIndex +
"] Spice saved at " +
DateFormat.getDateTimeInstance (DateFormat.SHORT,
DateFormat.LONG).format (new Date()));
}
public void contextInitialized(ServletContextEvent sce) {
if (sce != null) {
spice = System.getProperty ("spice");
System.out.println ("[DBInterface" + dbiIndex +
"] Spice retrieved at " +
DateFormat.getDateTimeInstance (DateFormat.SHORT,
DateFormat.LONG).format (new Date()));
}
// do stuff with spice
}
============
UPSBean.java:
============
public class UPSBean implements Serializable {
private String spice = "parsley, sage, rosemary and thyme";
public UPSBean() { }
public String getSpice() { return spice; }
public void setSpice (String s) { spice = s; }
}
我正在尝试查看get / setProperty是否在上面工作。试图直接使用JNDI,但是当我使用contextDestroyed()中的资源MyServlet / upsBean setSpice并尝试在contextInitialized()中读取它时,我得到一个null(抱歉,我已经删除了那部分代码)。现在context.xml中的声明以及resource-env-ref已经变得多余。目前的解决方法是将序列化实例保存在数据文件中(不是很好)。
答案 2 :(得分:1)
如果您想以编程方式处理它,我认为您可能正在寻找的是ServletContextListener。创建一个实现接口的类,并在 contextInitialized(ServletContextEvent sce) -method中编写所需的功能,有关简单示例,请参阅here。
答案 3 :(得分:1)
Tomcat在这里没有提供任何帮助。
这是个坏消息。
好消息是......这是一个流程问题,可以使用servlet-api提供的工具以及Tomcat可以提供的一些其他项来解决。这是成分..
鉴于上述所有内容 - 您可以从文件中读取“受保护”值并将其写入系统属性。然后,为了增加保护,以防您不希望值始终作为系统属性存在 - 您可以使用ServletContextListener读取系统属性值并从系统属性中删除它们。然后在重新部署时 - 侦听器会在关闭时写入重置系统属性 - 所以当webapp重新启动时 - 它们仍在那里。
所以启动顺序就像这样
通过上述内容 - 您可以在不将密码写入磁盘的情况下进行重新部署,并希望通过临时存储为system.properties来最小化任何攻击媒介。
祝你好运。答案 4 :(得分:1)
Tomcat中没有任何东西可以做你想要的。如果将设置移动到tomcat JNDI树中,则必须将用户/名称密码组合放在server.xml或context.xml文件中。以下是针对您遇到的问题的几种可能的解决方案。
选项1:使用Tomcat侦听器
如果查看tomcat server.xml文件的顶部,您将看到几个侦听器,这些是在tomcat启动时执行的java类。您可以创建自己的tomcat侦听器,该侦听器从文件系统读取密码,删除文件并以应用程序可访问的方式存储用户名/密码Comobo。 tomcat监听器绑定到tomcat服务器的生命周期,因此应用程序的自动重新部署不会导致重新加载tomcat监听器类。您的tomcat侦听器的代码必须放入jar并放在CATALINA_HOME \ lib文件夹中。
监听器可以使用静态变量来存储用户名/密码,并且有一个返回它们的静态方法,这将起作用,因为监听器将位于tomcat应用程序的父类加载器中。这种方法的缺点是您的应用程序代码依赖于tomcat资源侦听器实现,并且在您的单元测试中,您可能需要执行一些额外的步骤以确保您的代码可以进行单元测试。
侦听器还可以访问tomcat全局JNDI树并将用户名和密码组合放在那里,然后你的应用必须让context.xml文件使用ResourceLink元素来使应用程序可以使用全局jndi条目。您还需要做一些额外的工作来使这种方法与单元测试一起工作,因为在JNDI中查找内容通常会使单元测试变得复杂。
如果您在此项目中使用Spring,最好的办法是在tomcat侦听器中使用静态变量,然后使用自定义弹簧范围将数据从tomcat侦听器中提取出来。这样你的应用程序可以测试,你可以将用户名/密码组合注入任何需要它们的代码片段。
选项2:使用Servlet上下文侦听器
在此选项中,您可以编写一个上下文侦听器,它将允许您的应用在应用启动和停止时得到通知。使用此方法启动时,上下文侦听器将运行并读取密码信息并删除该文件。如果启动时没有密码文件,那么上下文监听器必须有办法让管理员重新生成文件。
选项3:使用JMX
创建一个JMX MBean,将其注册到JVM MBeanServer,然后使用它来存储用户名/密码组合。如果从tomcat侦听器初始化此MBean,则可以让perl脚本远程调用MBean并传入用户名/密码组合。
希望这会有所帮助。