文章关键字 ‘jspc’

Windows下编写google app engine jsp出现中文乱码问题的真正原因

2009年04月12日,星期天

Windows下编写google app engine jsp出现中文乱码问题的真正原因,是com.google.appengine.tools.admin.Application里启动javac没加-encoding参数导致.

在Linux下,因为一般都是LANG=zh_CN.UTF-8,因此就不会遇到这个问题了。

Windows下一般是取的默认的file.encoding为GBK,因此根据jsp生成的class文件就是乱码内容了。

验证流程如下:
1.首先重命名E:\Java\appengine-java-sdk-1.2.0\lib\shared\jsp\jasper-compiler-5.0.28.jar为 jasper-compiler-5.0.28.jar.bak。
然后在E:\Java\appengine-java-sdk-1.2.0\bin下运行 appcfg update E:\Java\workspace\testwe\war。
这个时候在命令行下会看到如下的出错信息。
E:\Java\appengine-java-sdk-1.2.0\bin>java -cp “E:\Java\appengine-java-sdk-1.2.0\bin\\..\lib\appengine-tools-api.jar” com.google.appengine.tools.admin.AppCfg update e:\Java\workspace\testweb\war
Reading application configuration data…
2009-04-12 03:05:34.240::INFO:  Logging to STDERR via org.mortbay.log.StdErrLog
Beginning server interaction for lizongbo…
0% Creating staging directory
5% Scanning for jsp files.
8% Compiling jsp files.
Exception in thread “main” java.lang.NoClassDefFoundError: org/apache/jasper/JspC
at com.google.appengine.tools.development.LocalJspC.main(LocalJspC.java:
14)
Caused by: java.lang.ClassNotFoundException: org.apache.jasper.JspC
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
… 1 more
java.lang.RuntimeException: Failed to compile jsp files via E:\Java\jdk1.6.0_13\
jre\bin\java.exe -classpath com.google.appengine.tools.development.LocalJspC -uriroot C:\DOCUME
~1\ADMINI~1\LOCALS~1\Temp\appcfg8338201102819535049.tmp -p org.apache.jsp -l -v
-webinc C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\appcfg8338201102819535049.tmp\WEB-INF
\generated_web.xml -d C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\appcfg83382011028195350
49.tmp\WEB-INF\classes

2.将jasper-compiler-5.0.28.jar.bak重命名回 jasper-compiler-5.0.28.jar,
将E:\Java\jdk1.6.0_13\bin\javac.exe重命名为 javacc.exe,
在Eclipse里通过Google app Engine的插件发布,得到下面的出错信息:

Unable to upload:
java.lang.IllegalStateException: cannot find javac executable based on java.home, tried “E:\Java\jdk1.6.0_13\jre\bin\javac.exe” and “E:\Java\jdk1.6.0_13\bin\javac.exe”
at com.google.appengine.tools.admin.AppAdminFactory$ApplicationProcessingOptions.getJavaCompiler(AppAdminFactory.java:325)
at com.google.appengine.tools.admin.Application.compileJavaFiles(Application.java:340)
at com.google.appengine.tools.admin.Application.compileJsps(Application.java:326)
at com.google.appengine.tools.admin.Application.createStagingDirectory(Application.java:235)
at com.google.appengine.tools.admin.AppAdminImpl.update(AppAdminImpl.java:39)
at com.google.appengine.eclipse.core.proxy.AppEngineBridgeImpl.deploy(AppEngineBridgeImpl.java:203)
at com.google.appengine.eclipse.core.deploy.DeployProjectJob.runInWorkspace(DeployProjectJob.java:97)
at org.eclipse.core.internal.resources.InternalWorkspaceJob.run(InternalWorkspaceJob.java:38)
at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55)
java.lang.IllegalStateException: cannot find javac executable based on java.home, tried “E:\Java\jdk1.6.0_13\jre\bin\javac.exe” and “E:\Java\jdk1.6.0_13\bin\javac.exe”
于是根据堆栈找到了com.google.appengine.tools.admin.Application,使用Jd-gui(来自http://java.decompiler.free.fr/)打开class找到了如下代码:
[code]
String javacCmd = opts.getJavaCompiler().getPath();
args.add(javacCmd);
args.add("-classpath");
args.add(classpath.toString());
args.add("-d");
args.add(classDir.getPath());

for (File f : new FileIterator(classDir)) {
if (f.getPath().toLowerCase().endsWith(".java"))
args.add(f.getPath());

}

if (args.size() == 5)
return;

Process javac = startProcess((String[])args.toArray(new String[0]));
[/code]

从代码看到,Google App engine只用 apache Tomcat jasper生成java文件,再通过拼凑命令行字符串方式生成编译java文件为Class的命令,通过Process执行。
由于命令行参数没有设置encoding参数,于是javac使用了系统默认的file.encoding,在中文Windows下,基本都是GBK。在Linux下,因为一般都是LANG=zh_CN.UTF-8,因此就不会遇到这个问题了。
下面是linux 下ps看到的完整javac命令:

lizongbo  6733  0.0  0.4 690156  9228 ?        Sl   21:16   0:00 /usr/local/jdk1.6.0_12/bin/javac -classpath /usr/local/java/appengine-java-sdk-1.2.0/lib/impl/appengine-api.jar:/usr/local/java/appengine-java-sdk-1.2.0/lib/impl/appengine-api-stubs.jar:/usr/local/java/appengine-java-sdk-1.2.0/lib/impl/appengine-local-runtime.jar:/usr/local/java/appengine-java-sdk-1.2.0/lib/shared/geronimo-el_1.0_spec-1.0.1.jar:/usr/local/java/appengine-java-sdk-1.2.0/lib/shared/geronimo-servlet_2.5_spec-1.2.jar:/usr/local/java/appengine-java-sdk-1.2.0/lib/shared/appengine-local-runtime-shared.jar:/usr/local/java/appengine-java-sdk-1.2.0/lib/shared/geronimo-jsp_2.1_spec-1.0.1.jar:/usr/local/java/appengine-java-sdk-1.2.0/lib/shared/jsp/jasper-runtime-5.0.28.jar:/usr/local/java/appengine-java-sdk-1.2.0/lib/shared/jsp/commons-el-1.0.jar:/usr/local/java/appengine-java-sdk-1.2.0/lib/shared/jsp/ant-launcher-1.6.5.jar:/usr/local/java/appengine-java-sdk-1.2.0/lib/shared/jsp/commons-logging-1.1.1.jar:/usr/local/java/appengine-java-sdk-1.2.0/lib/shared/jsp/ant-1.6.5.jar:/usr/local/java/appengine-java-sdk-1.2.0/lib/shared/jsp/jasper-compiler-5.0.28.jar:/tmp/appcfg8102867646621343748.tmp/WEB-INF/classes:/tmp/appcfg8102867646621343748.tmp/WEB-INF/lib/ant-launcher-1.6.5.jar:/tmp/appcfg8102867646621343748.tmp/WEB-INF/lib/jakarta-standard-1.1.2.jar:/tmp/appcfg8102867646621343748.tmp/WEB-INF/lib/jasper-compiler-5.0.28.jar:/tmp/appcfg8102867646621343748.tmp/WEB-INF/lib/datanucleus-appengine-1.0.0.final.jar:/tmp/appcfg8102867646621343748.tmp/WEB-INF/lib/ant-1.6.5.jar:/tmp/appcfg8102867646621343748.tmp/WEB-INF/lib/datanucleus-core-1.1.0.jar:/tmp/appcfg8102867646621343748.tmp/WEB-INF/lib/geronimo-jta_1.1_spec-1.1.1.jar:/tmp/appcfg8102867646621343748.tmp/WEB-INF/lib/jdo2-api-2.3-SNAPSHOT.jar:/tmp/appcfg8102867646621343748.tmp/WEB-INF/lib/jasper-runtime-5.0.28.jar:/tmp/appcfg8102867646621343748.tmp/WEB-INF/lib/commons-logging-1.1.1.jar:/tmp/appcfg8102867646621343748.tmp/WEB-INF/lib/appengine-api-1.0-sdk-1.2.0.jar:/tmp/appcfg8102867646621343748.tmp/WEB-INF/lib/geronimo-jpa_3.0_spec-1.1.1.jar:/tmp/appcfg8102867646621343748.tmp/WEB-INF/lib/datanucleus-jpa-1.1.0.jar:/tmp/appcfg8102867646621343748.tmp/WEB-INF/lib/commons-el-1.0.jar:/tmp/appcfg8102867646621343748.tmp/WEB-INF/lib/jakarta-jstl-1.1.2.jar: -d /tmp/appcfg8102867646621343748.tmp/WEB-INF/classes /tmp/appcfg8102867646621343748.tmp/WEB-INF/classes/org/apache/jsp/test_jsp.java /tmp/appcfg8102867646621343748.tmp/WEB-INF/classes/org/apache/jsp/p_jsp.java

在Google app Engine的java SDK没有完善之前,只有在Ubuntu下写jsp最方便不会出现中文乱码问题。
受网上文章相关文章的误导,花了不少时间去看 apache tomcat japser的代码,并尝试替换 jasper-compiler-5.0.28.jar为Apache tomcat 6.0.18的最新版本,结果发现乱码问题的原因根本不在哪里。

appengine_web.xml里可以配置:<property name=”file.encoding” value=”UTF-8″ />。但是也不能解决乱码问题。

Tags: encoding, Google App Engine, Java, jsp, jspc

Related posts

配置独立运行的jsp预编译ant脚本

2008年05月8日,星期四

配置resin开启预编译jsp,在启动时,resin会自动编译jsp,出错信息在 stdout.log中。

[code]
<web-app id="/lizongbo" document-directory="webapps/lizongbo">
<listener>
<listener-class>com.caucho.jsp.JspPrecompileListener</listener-class>
<init>
<extension>jsp</extension>
<extension>jspx</extension>
<extension>xtp</extension>
</init>
</listener>
</web-app>
[/code]

在开发过程,经常将页面传到resin上,才发现页面编译失败,浪费了不少时间,因此需要一个在本地预编译进行检查的操作。

resin 3.1以前的版本提供命令行方式进行jsp预编译。

完整的命令例子如下
%JAVA_HOME%\bin\java.exe -classpath %JAVA_HOME%\lib\tools.jar;D:\Java\resin-3.0.19\lib\aopalliance.jar;D:\Java\resin-3.0.19\lib\ejb-20.jar;D:\Java\resin-3.0.19\lib\ejb-30.jar;D:\Java\resin-3.0.19\lib\j2eedeploy.jar;D:\Java\resin-3.0.19\lib\jca-15.jar;D:\Java\resin-3.0.19\lib\jms-11.jar;D:\Java\resin-3.0.19\lib\jmx-12.jar;D:\Java\resin-3.0.19\lib\jsdk-24.jar;D:\Java\resin-3.0.19\lib\jstl-11.jar;D:\Java\resin-3.0.19\lib\jta-101.jar;D:\Java\resin-3.0.19\lib\portlet-10.jar;D:\Java\resin-3.0.19\lib\quercus.jar;D:\Java\resin-3.0.19\lib\resin-jdk15.jar;D:\Java\resin-3.0.19\lib\resin.jar;D:\Java\resin-3.0.19\lib\resinboot.jar;D:\Java\resin-3.0.19\lib\script-10.jar;D:\Java\resin-3.0.19\lib\webutil.jar;D:\Java\resin-3.0.19\plugins\resin-ant.jar com.caucho.jsp.JspCompiler -app-dir  E:\lizongbo\workspace\webapp E:\lizongbo\workspace\webapp
使用起来很不方便。(注意classpath里特意加了%JAVA_HOME%\lib\tools.jar,否则会报错,
出错信息如下:
Exception in thread “main” com.caucho.java.JavaCompileException: Resin can’t loa
d com.sun.tools.javac.Main.  Usually this means that the JDK tools.jar is missin
g from the classpath, possibly because of using a JRE instead of the JDK.  You c
an either add tools.jar to the classpath or change the compiler to an external o
ne with <java compiler=’javac’/> or jikes.

java.lang.ClassNotFoundException: com.sun.tools.javac.Main
at com.caucho.java.InternalCompiler.compileInt(InternalCompiler.java:77)
at com.caucho.java.AbstractJavaCompiler.run(AbstractJavaCompiler.java:101)
at java.lang.Thread.run(Thread.java:595)
)

resin 3.1.5及以后的版本终于提供了 ant方式的jspc的atsk,但是却有bug。
[code]
<project name="test" default="test" basedir=".">
<property name="resin.home" value="/usr/local/share/resin"/>

<target name="test">
<taskdef name="resin-jspc"
classname="com.caucho.ant.Jspc">
<classpath>
<fileset dir="${resin.home}">
<include name="plugins/resin-ant.jar"/>
<include name="lib/*.jar"/>
</fileset>
</classpath>
</taskdef>

<resin-jspc rootDirectory="/home/ferg/ws/dist/my-webapp"/>
</target>

</project>
[/code]
来源: http://wiki.caucho.com/Ant
在该方式下,会遇到提示说contentType重复定义的bug
出错信息如下:
E:\lizongbo\workspace\webapp\WEB-INF\ant\resin-jspc.xml:38:
com.caucho.jsp.JspLineParseException: /618119/test.jsp:6: contentType ‘text/vnd.wap.wml; charset=UTF-8′ conflicts with previous value of contentType ‘text/html; charset=UTF-8′.  Check the .jsp and any included .jsp files for conflicts.

原因是resin 3.1 对带BOM格式的UTF-8编码的jsp识别有问题。
参考: http://anotherbug.blog.chinajavaworld.com/entry/3683/0/

此时改页面是件痛苦的事情,于是想到使用Tomcat的ant jspC task

Tomcat 6.0.16的文件目录已经发生了变化,而官方的jspC的文档还没有更新:

http://tomcat.apache.org/tomcat-6.0-doc/jasper-howto.html

以前写过一篇笔记,基本是copy tomcat的官方文档
参考: http://blog.donews.com/lizongbo/archive/2005/06/06/413985.aspx
而现在主要用resin,因此想要一个可以独立进行jsp预编译的ant脚本,
所以手工清理后制作了个独立的jspc,方便随时预编译.

所用到的jar文件为:D:\Java\apache-tomcat-6.0.16\lib和D:\Java\apache-tomcat-6.0.16\bin 目录下所有jar
[code]
D:\Java\jspc\tomcat6lib 的目录

2008-05-08  20:09    <DIR>          .
2008-05-08  20:09    <DIR>          ..
2008-01-28  23:39            10,368 annotations-api.jar
2008-01-28  23:39            17,530 bootstrap.jar
2008-01-28  23:39            49,240 catalina-ant.jar
2008-01-28  23:39           122,869 catalina-ha.jar
2008-01-28  23:39           228,175 catalina-tribes.jar
2008-01-28  23:39         1,128,229 catalina.jar
2008-01-28  23:39             9,341 commons-daemon.jar
2008-01-28  23:39            27,699 el-api.jar
2008-01-28  23:39           102,129 jasper-el.jar
2008-01-28  23:39         1,395,270 jasper-jdt.jar
2008-01-28  23:39           511,240 jasper.jar
2008-01-28  23:39            72,408 jsp-api.jar
2008-01-28  23:39            85,287 servlet-api.jar
2008-01-28  23:39           742,089 tomcat-coyote.jar
2008-01-28  23:39           197,846 tomcat-dbcp.jar
2008-01-28  23:39            18,980 tomcat-juli.jar
16 个文件      4,718,700 字节
2 个目录    895,870,976 可用字节
[/code]

tomcat-jspc.bat的内容为:
(因为jsp文件太多,消耗内存较大,因此通过 ANT_OPTS=-Xmx512m 指定内存)
[code]
set ANT_OPTS=-Xmx512m
ant -f tomcat-jspc.xml -Dwebapp.name=mywebapp
[/code]
tomcat-jspc.properties的内容为:
[code]
webapp.root=E:/lizongbo/workspace
webapp.name = webapp
[/code]

tomcat-jspc.xml的内容如下:

[code]
<project name="Webapp Precompilation" default="all" basedir=".">
<property file="${basedir}/tomcat-jspc.properties"/>
<property name="tomcatlib.home" value="${basedir}/tomcat6lib"/>
<property name="webapp.path" value="${webapp.root}/${webapp.name}"/>
<description> JSPC Tasks</description>
<taskdef resource="org/apache/catalina/ant/catalina.tasks">
<classpath>
<fileset file="${tomcatlib.home}/tomcat-juli.jar"/>
<fileset file="${tomcatlib.home}/jasper.jar"/>
<fileset file="${tomcatlib.home}/jasper-el.jar"/>
<fileset file="${tomcatlib.home}/el-api.jar"/>
<fileset file="${tomcatlib.home}/jsp-api.jar"/>
<fileset file="${tomcatlib.home}/servlet-api.jar"/>
<fileset file="${tomcatlib.home}/catalina-ant.jar"/>
</classpath>
</taskdef>
<taskdef resource="org/apache/catalina/ant/jmx/jmxaccessor.tasks">
<classpath>
<fileset file="${tomcatlib.home}/catalina-ant.jar"/>
</classpath>
</taskdef>
<typedef
name="jmxEquals"
classname="org.apache.catalina.ant.jmx.JMXAccessorEqualsCondition">
<classpath>
<fileset file="${tomcatlib.home}/catalina-ant.jar"/>
</classpath>
</typedef>
<typedef
name="jmxCondition"
classname="org.apache.catalina.ant.jmx.JMXAccessorCondition">
<classpath>
<fileset file="${tomcatlib.home}/catalina-ant.jar"/>
</classpath>
</typedef>
<!-- 先把WEB-INF/src下的java文件编译成class -->
<target name="compilejava">
<echo>jdk path: ${java.home} </echo>
<echo>webapp path: ${webapp.path} </echo>

<mkdir dir="${webapp.path}/WEB-INF/classes"/>
<mkdir dir="${webapp.path}/WEB-INF/lib"/>

<javac destdir="${webapp.path}/WEB-INF/classes"
optimize="off"
debug="on" failonerror="false"
encoding="utf-8" nowarn="true" target="1.5"
srcdir="${webapp.path}/WEB-INF/src"
excludes="**/*.smap">
<classpath>
<pathelement location="${webapp.path}/WEB-INF/classes"/>
<fileset dir="${webapp.path}/WEB-INF/lib">
<include name="*.jar"/>
</fileset>
<pathelement location="${tomcatlib.home}"/>
<fileset dir="${tomcatlib.home}">
<include name="*.jar"/>
</fileset>
</classpath>
<include name="**" />
<exclude name="tags/**" />
</javac>

</target>
<!-- 再把jsp页面生成java文件 -->
<target name="jspc">
<jasper
validateXml="false"
uriroot="${webapp.path}"
webXmlFragment="${webapp.path}/WEB-INF/generated_web.xml"
outputDir="${webapp.path}/WEB-INF/jspsrc" />

</target>
<!-- 再把jsp的java代码编译成class -->
<target name="compilejspjava">

<mkdir dir="${webapp.path}/WEB-INF/work"/>
<mkdir dir="${webapp.path}/WEB-INF/lib"/>

<javac destdir="${webapp.path}/WEB-INF/work"
optimize="off"
debug="on" failonerror="true"
encoding="utf-8" nowarn="true" target="1.5"
srcdir="${webapp.path}/WEB-INF/jspsrc"
excludes="**/*.smap">
<classpath>
<pathelement location="${webapp.path}/WEB-INF/classes"/>
<fileset dir="${webapp.path}/WEB-INF/lib">
<!-- need ignore mywebapp/WEB-INF/lib/servlet.jar 在这里忽略掉webapp lib里的低版本的servlet,以免编译失败-->
<exclude name="servlet.jar"/>
<include name="*.jar"/>
</fileset>
<pathelement location="${tomcatlib.home}"/>
<fileset dir="${tomcatlib.home}">
<include name="*.jar"/>
</fileset>
</classpath>
<include name="**" />
<exclude name="tags/**" />
</javac>

</target>

<target name="all" depends="compilejava,jspc,compilejspjava,cleanup">
</target>

<target name="cleanup">
<delete includeEmptyDirs="true">
<fileset dir="${webapp.path}/WEB-INF/jspsrc"/>
<fileset dir="${webapp.path}/WEB-INF/work"/>
</delete>
</target>

</project>
[/code]

Tags: ant, eclipse, javac, jspc, Resin, Tomcat

Related posts