Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/316.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何升级Tomcat Java小程序以使用JNLP?_Java_Java Web Start_Jnlp_Keytool - Fatal编程技术网

如何升级Tomcat Java小程序以使用JNLP?

如何升级Tomcat Java小程序以使用JNLP?,java,java-web-start,jnlp,keytool,Java,Java Web Start,Jnlp,Keytool,我有一套在Tomcat上运行的Java小程序。这些项目记录非正式的高尔夫“锦标赛”活动,包括友谊赛 虽然程序细节并不重要,但代码集包含30000多行源代码。为了便于移植和避免维护问题,我选择Java作为实现语言。我使用Tomcat部署应用程序,使用javascript调用小程序。我的所有小程序都使用参数,例如事件名称、课程名称和播放日期 不幸的是,java和浏览器的更改现在给我的应用程序带来了维护问题。第一个问题是java增加了jar文件必须签名的要求。第二个问题是,first Chrome和现

我有一套在Tomcat上运行的Java小程序。这些项目记录非正式的高尔夫“锦标赛”活动,包括友谊赛

虽然程序细节并不重要,但代码集包含30000多行源代码。为了便于移植和避免维护问题,我选择Java作为实现语言。我使用Tomcat部署应用程序,使用javascript调用小程序。我的所有小程序都使用参数,例如事件名称、课程名称和播放日期

不幸的是,java和浏览器的更改现在给我的应用程序带来了维护问题。第一个问题是java增加了jar文件必须签名的要求。第二个问题是,first Chrome和现在的Firefox已经删除了对NPAPI插件的支持,这实际上从html中删除了Java小程序支持

()是新的替代品。这两个问题都有点难以解决,因为没有明确的分步文档详细说明实际需要做什么

将applet迁移到JNLP可能有不同甚至更好的方法,但是这里描述的过程是有效的,并且是完整的。然而,在描述它们时,我必须假设您已经知道如何创建JavaWeb应用程序,因为不需要更新您还没有的东西

我在Windows Cygwin环境中使用Tomcat。我的示例mkJavaKey脚本显式使用该环境,但所有Java和javascript代码都是可移植的。Tomcat使用web.xml来定义如何调用servlet。如果使用不同的部署方法,我的web.xml文件至少应该作为一个起点。

为什么需要对jar文件签名? 我不能回答这部分问题。但是,对于任何非平凡的应用程序,您至少需要经历自签名jar文件的过程,即使这种自签名没有提供真正的额外安全性。任何人都可以使用Java开发工具包中提供的工具对应用程序进行自签名。自签名证书可以用于开发工作,但每次运行应用程序时都必须单击风险接受复选框

好的,我的应用程序非常重要,我需要对jar文件进行签名。程序是什么? 这里有一个简单的答案:这是一个两步的过程。首先使用keytool程序创建必要的凭证,然后使用jarsigner工具对jar文件进行签名。您只需要偶尔创建一次凭据,但需要对每个部署的jar文件进行签名

要创建这些凭据(自签名证书),请使用:

这将在您的主目录中创建一个名为
.keystore
的证书,有效期为五年。您必须响应它的提示,我使用“password”作为密码。由于我只将此证书用于自签名jar文件,所以安全性不是大问题。validity参数指定证书的有效期(以天为单位)

每次更新jar文件时,都需要对其进行签名。假设您在分发目录中,并且需要签署
applet.jar
,请使用:

$JAVA_HOME/bin/jarsigner -tsa http://timestamp.digicert.com -storepass password applet.jar mydomain
-storepass
后面的“密码”与您在keytool中使用的密码匹配,“mydomain”与keytool
-alias
参数匹配。您需要指定-tsa(时间戳授权)参数,并且该参数是(或至少是)一个公开可用的参数。我不知道TSA到底做什么,也不知道你为什么需要它,但jarsigner不喜欢没有它,不会默认它,也不会直接记录如何找到它

现在可以使用或忽略以下批处理文件。我创建它是因为当我需要创建新证书时(我的原始证书已过期),我忘记了如何创建它。希望我们能在下次需要时找到这个批处理文件,也许五年后

#!/bin/bash
#
# Title-
#        mkJavaKey
#
# Function-
#        Create a new key using $JAVA_HOME/bin/keytool
#
# Usage-
#        mkJavaKey ## CYGWIN ONLY ##
#        (This is required when jarsigner complains about an expired key.)
#        NOTE: This *REMOVES* and *REPLACES* your existing .keystore file!
#
#######

##########################################################################
# Environment check
if [ -z "$JAVA_HOME" ] ; then
  . setupJAVA ## (This personal script sets JAVA_HOME)
  if [ -z "$JAVA_HOME" ] ; then
    echo "JAVA_HOME environment variable missing"
    exit 1
  fi
fi

if [ -z "$HOMEPATH" ] ; then
  echo "HOMEPATH environment variable missing"
  echo "Try export HOMEPATH=\Users\myname"
  exit 1
fi

home_path=`cygpath --path --unix C:$HOMEPATH`
PGM=$JAVA_HOME/bin/keytool
if [ ! -x "$PGM" ] ; then
  echo "$PGM not executable"
  exit 1
fi

##########################################################################
# Create a new .keystore
set -x
rm -Rf $home_path/.keystore
$PGM -genkeypair -keyalg RSA -keysize 2048 -alias mydomain -validity 1825
exit $?
注意:我的setupJAVA脚本设置
JAVA\u HOME
环境变量。对于Linux,请使用
$HOME
而不是
$HOMEPATH
并跳过
cygpath
部分。这些文件在Cygwin环境中在Linux和Windows文件名格式之间转换

每次安装jar文件时,都需要对其进行签名。为了实现自动化,我修改了Makefile来实现这一点。下面是我使用的make代码片段:

.PHONY: golfer.install
golfer.install: test golfer
: (Not relevant to discussion)
cp -p $(OBJDIR)/usr/fne/golfer/Applet/applet.jar $(DEPLOYDIR)/webapps/golfer/.
jarsigner -tsa http://timestamp.digicert.com -storepass password "$(shell cygpath --path --windows "$(DEPLOYDIR)/webapps/golfer/applet.jar")" mydomain
: (Not relevant to discussion)
$(OBDIR)
$(DEPLOYDIR)
变量与本讨论无关。它们是在基于Makefile的构建环境中设置的目录路径

如何将小程序迁移到新的JNLP环境? 现在我们有了自签名的JAR文件,我们可以开始考虑如何运行它们了。许多浏览器不再支持NPAPI,因此
标记无法工作。也不会部署java.runApplet()。我不想解释为什么取消了对NPAPI的支持,只想说明需要做些什么才能使现有的应用程序运行

迁移代码时我发现的最大问题是,最终我不得不创建.jnlp文件而不是.html文件。我将向您展示如何做到这一点,描述我修改和添加的代码

这是我用来生成html的javascript代码(现已过时):

//------------------------------------------------------------------------
//
// Title-
//       applet.js
//
// Purpose-
//       Common applet javascript.
//
// Last change date-
//       2010/10/19
//
//------------------------------------------------------------------------
var out;     // Output document

//------------------------------------------------------------------------
// appHead
//
// Generate html header for application.
//------------------------------------------------------------------------
function appHead(title,cname,height,width)
{
   var todoWindow= window.open('','','');
   out= todoWindow.document;
   out.write('<html>');
   out.write('<head><title>' + title + '</title></head>');
   out.write('<body>\n');
   out.write('<applet code="' + cname + '.class"');
   out.write('    codebase="./"')
   out.write('    archive="applet.jar,jars/common.jar"');
   out.write('    width="' + width + '" height="' + height + '">\n');
}

//------------------------------------------------------------------------
// appParm
//
// Add parameter information
//------------------------------------------------------------------------
function appParm(name, value)
{
     out.write('        <param-name="' + name + '" value="' + value + '"/>\n');
}

//------------------------------------------------------------------------
// appTail
//
// Generate html trailer information.
//------------------------------------------------------------------------
function appTail()
{
   out.write('Your browser is completely ignoring the &lt;APPLET&gt; tag!\n');
   out.write('</applet>');
   out.write('<form>');
   out.write('<input type="button" value="Done" onclick="window.close()">');
   out.write('</form>');
   out.write('</body>');
   out.write('</html>');
   out.close();
   out= null;
}

//------------------------------------------------------------------------
// cardEvents
//
// Display scorecard for selected date.
//------------------------------------------------------------------------
function cardEvents(eventsID, obj) 
{
   if( obj.selectedIndex == 0 )
   {
     alert("No date selected");
     return;
   }
   appHead('Score card', 'EventsCard', '100%', '100%');
   appParm('events-nick', eventsID);
   appParm('events-date', obj[obj.selectedIndex].value);
   appTail();
   reset();
}
这将以
Applet.jnlp、类名、描述、parm=value、parm=value、
的形式生成URL

然后我创建了一个名为AppletServlet.java的新Servlet。传递给它的URL提供生成.jnlp文件所需的所有信息。此代码遵循标准示例Servlet结构,其中调用doGet来处理请求。代码如下:

//------------------------------------------------------------------------
//
// Method-
//       AppletServlet.doGet
//
// Purpose-
//       Called for each HTTP GET request.
//
//------------------------------------------------------------------------
public void
   doGet(                           // Handle HTTP "GET" request
     HttpServletRequest  req,       // Request information
     HttpServletResponse res)       // Response information
   throws ServletException, IOException
{
   String q= req.getQueryString();
   if( debug ) log("doGet("+q+")");

   res.setContentType("text/html");

   query(req, res);
}

//------------------------------------------------------------------------
//
// Method-
//       AppletServlet.putError
//
// Purpose-
//       Generate error response.
//
//------------------------------------------------------------------------
public void
   putError(                        // Generate error response
     PrintWriter       out,         // The response writer
     String            msg)         // The error message
{       out.println("<HTML>");
   out.println("<HEAD><TITLE>" + msg + "</TITLE></HEAD>");
   out.println("<BODY>");
   out.println("<H1 align=\"center\">" + msg + "</H1>");
   out.println("</BODY>");
   out.println("</HTML>");
}

//------------------------------------------------------------------------
//
// Method-
//       AppletServlet.query
//
// Purpose-
//       Handle a query.
//
//------------------------------------------------------------------------
protected void
   query(                           // Handle a query
     HttpServletRequest  req,       // Request information
     HttpServletResponse res)       // Response information
   throws ServletException, IOException
{
   String q= req.getQueryString();
   if( debug ) log("query("+q+")");

   PrintWriter out = res.getWriter();
   String BOGUS= "<br> Malformed request: query: '" + q + "'";

   //=====================================================================
   // Applet.jnlp?classname,title,parm=value,parm=value,...
   int index= q.indexOf(',');
   if( index < 0 || index == (q.length() - 1) )
   {
     putError(out, BOGUS);
     return;
   }
   String invoke= q.substring(0, index);

   q= q.substring(index+1);
   index= q.indexOf(',');
   if( index < 0 )
     index= q.length();
   String title= q.substring(0, index);
   title= java.net.URLDecoder.decode(title, "UTF-8");

   // Parameter extraction
   Vector<String> param= new Vector<String>();
   if( index < q.length() )
   {
     q= q.substring(index+1);
     for(;;)
     {
       index= q.indexOf(',');
       if( index < 0 )
         index= q.length();

       String s= q.substring(0, index);
       int x= s.indexOf('=');
       if( x < 0 )
       {
         putError(out, BOGUS);
         return;
       }

       param.add(s);
       if( index >= q.length() )
         break;

       q= q.substring(index+1);
     }
   }

   //---------------------------------------------------------------------
   // We now have enough information to generate the response
   //---------------------------------------------------------------------
   res.setContentType("application/x-java-jnlp-file");
   out.println("<?xml version='1.0' encoding='utf-8'?>");
   out.println("<jnlp spec='1.0+' codebase='http://localhost:8080/golfer'>");
   out.println(" <information>");
   out.println("  <title>" + title + "</title>");
   out.println("  <vendor>My Name</vendor>");
   out.println("  <description>" + title + "</description>");
   out.println(" </information>");
   out.println(" <security><all-permissions/></security>");
   out.println(" <resources>");
   out.println("  <j2se version='1.7+'/>");
   out.println("  <jar href='applet.jar'/>");
   out.println("  <jar href='jars/common.jar'/>");
   out.println(" </resources>");
   out.println(" <applet-desc main-class='" + invoke + "' name='" + title + "'" +
                " height='90%' width='98%'>");

   // Insert applet parameters
   for(int i= 0; i<param.size(); i++)
   {
     String s= param.elementAt(i);
     int    x= s.indexOf('=');
     String n= s.substring(0,x);
     String v= s.substring(x+1);
     out.println("  <param name='" + n+ "' value='" + v + "'/>");
   }
   out.println(" </applet-desc>");
   out.println("</jnlp>");
}
这将导致对任何Applet.jnlp URL调用AppletServlet。浏览器会忽略查询字符串,并将结果视为文件名为Applet.jnlp

为了使操作更顺畅,您需要设置Windows文件关联,以便
.jnlp
文件调用Java(TM)Web Start启动器。在Windows中,JWS启动器是
C:\ProgramFiles\java\jre*\bin\javaws.exe
(
//------------------------------------------------------------------------
//
// Title-
//       applet.js
//
// Purpose-
//       Common applet javascript.
//
// Last change date-
//       2017/03/15
//
//------------------------------------------------------------------------
var out;     // Output URL

//------------------------------------------------------------------------
// appHead
//
// Generate application URL header.
//------------------------------------------------------------------------
function appHead(title,cname,height,width)
{
   out= cname + ',' + title;
}

//------------------------------------------------------------------------
// appParm
//
// Generate html parameter information.
//------------------------------------------------------------------------
function appParm(name, value)
{
   out= out + ',' + name + '=' + value;
}

//------------------------------------------------------------------------
// appTail
//
// Generate html trailer information.
//------------------------------------------------------------------------
function appTail()
{
   var specs= 'menubar=yes,toolbar=yes';
   window.open('Applet.jnlp?' + out, '_self', specs);
}

//------------------------------------------------------------------------
// cardEvents
//
// Display scorecard for selected date.
//------------------------------------------------------------------------
function cardEvents(eventsID, obj)
{
    // (UNCHANGED!)
}
//------------------------------------------------------------------------
//
// Method-
//       AppletServlet.doGet
//
// Purpose-
//       Called for each HTTP GET request.
//
//------------------------------------------------------------------------
public void
   doGet(                           // Handle HTTP "GET" request
     HttpServletRequest  req,       // Request information
     HttpServletResponse res)       // Response information
   throws ServletException, IOException
{
   String q= req.getQueryString();
   if( debug ) log("doGet("+q+")");

   res.setContentType("text/html");

   query(req, res);
}

//------------------------------------------------------------------------
//
// Method-
//       AppletServlet.putError
//
// Purpose-
//       Generate error response.
//
//------------------------------------------------------------------------
public void
   putError(                        // Generate error response
     PrintWriter       out,         // The response writer
     String            msg)         // The error message
{       out.println("<HTML>");
   out.println("<HEAD><TITLE>" + msg + "</TITLE></HEAD>");
   out.println("<BODY>");
   out.println("<H1 align=\"center\">" + msg + "</H1>");
   out.println("</BODY>");
   out.println("</HTML>");
}

//------------------------------------------------------------------------
//
// Method-
//       AppletServlet.query
//
// Purpose-
//       Handle a query.
//
//------------------------------------------------------------------------
protected void
   query(                           // Handle a query
     HttpServletRequest  req,       // Request information
     HttpServletResponse res)       // Response information
   throws ServletException, IOException
{
   String q= req.getQueryString();
   if( debug ) log("query("+q+")");

   PrintWriter out = res.getWriter();
   String BOGUS= "<br> Malformed request: query: '" + q + "'";

   //=====================================================================
   // Applet.jnlp?classname,title,parm=value,parm=value,...
   int index= q.indexOf(',');
   if( index < 0 || index == (q.length() - 1) )
   {
     putError(out, BOGUS);
     return;
   }
   String invoke= q.substring(0, index);

   q= q.substring(index+1);
   index= q.indexOf(',');
   if( index < 0 )
     index= q.length();
   String title= q.substring(0, index);
   title= java.net.URLDecoder.decode(title, "UTF-8");

   // Parameter extraction
   Vector<String> param= new Vector<String>();
   if( index < q.length() )
   {
     q= q.substring(index+1);
     for(;;)
     {
       index= q.indexOf(',');
       if( index < 0 )
         index= q.length();

       String s= q.substring(0, index);
       int x= s.indexOf('=');
       if( x < 0 )
       {
         putError(out, BOGUS);
         return;
       }

       param.add(s);
       if( index >= q.length() )
         break;

       q= q.substring(index+1);
     }
   }

   //---------------------------------------------------------------------
   // We now have enough information to generate the response
   //---------------------------------------------------------------------
   res.setContentType("application/x-java-jnlp-file");
   out.println("<?xml version='1.0' encoding='utf-8'?>");
   out.println("<jnlp spec='1.0+' codebase='http://localhost:8080/golfer'>");
   out.println(" <information>");
   out.println("  <title>" + title + "</title>");
   out.println("  <vendor>My Name</vendor>");
   out.println("  <description>" + title + "</description>");
   out.println(" </information>");
   out.println(" <security><all-permissions/></security>");
   out.println(" <resources>");
   out.println("  <j2se version='1.7+'/>");
   out.println("  <jar href='applet.jar'/>");
   out.println("  <jar href='jars/common.jar'/>");
   out.println(" </resources>");
   out.println(" <applet-desc main-class='" + invoke + "' name='" + title + "'" +
                " height='90%' width='98%'>");

   // Insert applet parameters
   for(int i= 0; i<param.size(); i++)
   {
     String s= param.elementAt(i);
     int    x= s.indexOf('=');
     String n= s.substring(0,x);
     String v= s.substring(x+1);
     out.println("  <param name='" + n+ "' value='" + v + "'/>");
   }
   out.println(" </applet-desc>");
   out.println("</jnlp>");
}
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
   <servlet>
     <servlet-name>Applet</servlet-name>
     <servlet-class>usr.fne.golfer.AppletServlet</servlet-class>
     <init-param>
       <param-name>property-path</param-name>
       <param-value>profile</param-value>
     </init-param>
     <init-param>
       <param-name>property-file</param-name>
       <param-value>golfer.pro</param-value>
     </init-param>
     <load-on-startup>30</load-on-startup>
   </servlet>

   <servlet-mapping>
     <servlet-name>Applet</servlet-name>
     <url-pattern>/Applet.jnlp</url-pattern>
   </servlet-mapping>

   : (Other Servlets unchanged) 
</web-app>