科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道如何在Java平台上使用脚本语言做Java开发(1)

如何在Java平台上使用脚本语言做Java开发(1)

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

本篇文章概括了一些脚本语言(如Groovy,Jruby,Jython)和Java语言的区别,论述了一些在Java平台上使用脚本将会面临的挑战。同时,本文介绍了一些方法,使你可以集成一些最初由Ruby或Python写成的脚本到你的Java代码中。

作者:Gregor Roth/梁婷婷 编译 来源:51CTO.com 2007年12月10日

关键字:

  • 评论
  • 分享微博
  • 分享邮件

在本页阅读全文(共4页)

从Java调用脚本类

可以在Java环境中通过使用脚本引擎来执行脚本。这样的脚本引擎同时允许执行专门的脚本方法或是函数。JSR 223定义了一个标准接口“Invokable”来实现这样的方法和功能。可以参看列表10,Ruby脚本的一个专门的函数“write”将被调用。

列表10:调用Ruby脚本的一个专门函数

String rubyScript = "def write(msg) \r\n" +
" puts msg \r\n" +
"end \r\n" +
"\r\n" +
" def anotherFunction() \r\n" +
" # do something \r\n" +
"end \r\n";

ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("jruby");
    engine.eval(rubyScript);
Invocable inv = (Invocable) engine;
inv.invokeFunction("write", new Object[] { "hello" });
// prints out:
// hello

一些脚本环境如Jython还支持一个编译器来产生Java类。这种情况下,一个Jython类可以像一个正常的Java类那样被调用。只是产生的Java类的自身能力是受限制的。

重写重载的Java方法

JRuby和Jython都支持实现Java接口和继承Java类。但是对于大多数流行的脚本语言来说,重写重载的Java方法就带来了一个新问题。怎样定义哪个重载的Java方法应该重写?如Rython2.x或Ruby不支持基于类型特征的方法重载。这需要处理重写的方法的所有重载方法。方法的实现需要对所有重载的方法做参数类型的检测。列表11给出了如何用Jython实现

列表11:使用Jython调用重载方法

from test import NonBlockingConnection
from java.lang import Integer
from java.lang import Long
class ExtendedNonBlockingConnection(NonBlockingConnection):
def write(self, *args):
# the signature to handle?
if len(args) == 1 and isinstance(args[0], Integer):
print 'writing overriden int ' + args[0].toString()
# no, let the super method do the stuff
else:
NonBlockingConnection.write(self, *args)

ExtendedNonBlockingConnection().write(Integer(777))
# prints out:
# writing overridden int 777

ExtendedNonBlockingConnection().write(Long(777))
# prints out:
# writing long 777

因为Groovy还支持静态类型声明,重载的方法可以通过参数类型进行声明,如列表12所示。

列表12:使用Groovy调用重载方法

import test.NonBlockingConnection
class ExtendedNonBlockingConnection extends NonBlockingConnection {
// overwrite the int method
def write(int i) {
println 'writing overridden int ' + i
}
}
new ExtendedNonBlockingConnection().write((int) 777)
// prints out:
// writing overridden int 777
new ExtendedNonBlockingConnection().write((long) 777)
// prints out:
// writing long 777

一个双向的完整的应用程序(SMTP服务器)

Java和脚本语言的紧密结合,通常需要通过脚本元素调用Java类,通过Java类调用脚本元素。因此,应用程序由基于脚本的类和基于Java的类这两部分无缝、双向地结合组成。

列表13给出了一个完整的基于脚本的应用程序,调用了Java类。它是一个用JRuby写的SMTP服务器,使用了一个非阻塞的Java网络类库。

列表13:JRuby写的SMTP服务器

include Java
RandomAccessFile = java.io.RandomAccessFile
DataConverter = Java::org.xsocket.DataConverter
MultithreadedServer = Java::org.xsocket.stream.MultithreadedServer
IConnection = Java::org.xsocket.stream.IConnection
IConnectHandler = Java::org.xsocket.stream.IConnectHandler
IDataHandler = Java::org.xsocket.stream.IDataHandler

class TestMessageSinkManager
def new_message_sink_channel()
file = java.io.File.create_temp_file('smtptest', 'mail')
return RandomAccessFile.new(file, 'rw').channel
end
end
class SmtpProtocolHandler
include IConnectHandler
include IDataHandler
   def initialize(domain, message_sink_manager)
@domain = domain
@msg_sink_mgr = message_sink_manager
@helo_pattern = Regexp.compile(/HELO.*/, Regexp::IGNORECASE)
@mail_from_pattern = Regexp.compile(/MAIL FROM:.*/, Regexp::IGNORECASE)
@rcpt_to_pattern = Regexp.compile(/RCPT TO:.*/, Regexp::IGNORECASE)
@data_pattern = Regexp.compile(/DATA.*/, Regexp::IGNORECASE)
@quit_pattern = Regexp.compile(/QUIT.*/, Regexp::IGNORECASE)
end
# new incoming (non blocking) connection
def onConnect(nbc)
nbc.flushmode = IConnection::FlushMode::ASYNC
nbc.attachment = { 'state' => 'CMD', 'msg_num' => 0 }
nbc.write("220 #{@domain} SMTP ready \r\n")
return true
end
# data received for the (non blocking) connection
def onData(nbc)
case nbc.attachment['state']
# message receiving mode: non-blocking streaming of the msg data
when 'MESSAGE'
# some validations have to be performed by the data sink
delimiter_found = nbc.read_available_by_delimiter("\r\n.\r\n",
                nbc.attachment['message_channel'])          
if delimiter_found
nbc.attachment['message_channel'].close()
nbc.attachment['state'] = 'CMD'
nbc.write("250 OK #{nbc.get_id()}.#{nbc.attachment['msg_num']} \r\n")
end
         # smtp-command mode: perform command 
else
# a BufferUnderflowException will been thrown, if delimiter not found
smtp_cmd_line = nbc.read_string_by_delimiter("\r\n")
case smtp_cmd_line
when @helo_pattern
nbc.write("250 #{@domain} SMTP Service \r\n")
when @mail_from_pattern
originator = smtp_cmd_line[10,9999].strip()
# ...here some validations should be performed (valid address, ...)
nbc.attachment['originator'] = originator
nbc.attachment['recipients'] = []
nbc.write("250 #{@originator} is syntactically correct\r\n")
               when @rcpt_to_pattern
rec = smtp_cmd_line[8,9999].strip()
# ...here some validations should be performed (max recipients, ...)
nbc.attachment['recipients'] = nbc.attachment['recipients'] << rec
nbc.write("250 #{rec} verified \r\n")
               when @data_pattern
# ...here some validation should be performed (recipients set, ...)
nbc.attachment['state'] = 'MESSAGE'
nbc.attachment['msg_num'] = nbc.attachment['msg_num'] + 1
nbc.attachment['message_channel'] =
                      @msg_sink_mgr.new_message_sink_channel()
time_stamp = "Received: FROM #{nbc.remote_address.canonical_host_name}
                     BY #{@domain}\r\n" + 
"id #{nbc.get_id()}.#{nbc.attachment['msg_num']};
                     " + Time.new.to_s() + "\r\n"
nbc.attachment['message_channel'].write(DataConverter.to_byte_buffer(time_stamp,
    'US-ASCII'))                  
nbc.write("354 Enter message, ending with \".\" \r\n")
               when @quit_pattern
nbc.write("221 SMTP service closing connection \r\n")
nbc.close()
               else 
nbc.write("500 Unrecognized command \r\n")
end
end
return true
end
end
server = MultithreadedServer.new(25, SmtpProtocolHandler.new('mSrv', 
    TestMessageSinkManager.new))
server.run()

在这个应用程序中,一个基于Java的服务器被实例化,监听将到来的SMTP网络连接。网络事件由基于JRuby的Handler处理。为了做这件事情,基于JRuby的Handler需要实现一个由Java网络类库定义的Java回调接口。

为了实现这个Java接口,JRuby类需要使用“include <java interface>”语句来声明支持所有接口。与基于Java的接口实现不同,返回类型或异常不需要由基于JRuby的方法实现定义。通过实现handler的一个回调函数,一个Java对象被转换成(以InonBlockingConnection实例)JRuby脚本。

对这个Java对象的访问就会被脚本环境截获到。因此,它可以就像是一个Ruby对象那样,在JRuby方法实现内部处理。原始的数据类型,如Java的Integer或Long都被映射成相应的Ruby类型。

如果一个网络事件发生,服务器完成handler的恰当的回调方法。这个可以工作成功,因为基于Ruby的handler对服务器来说就像是一个规则的Java类。JRuby runtime自动包装这个基于JRuby的handler,把它转化给Java服务器。Java服务器不需要获得本身的JRuby Handler,而是获得一个代理,支持所有由这个Handler实现的Java接口的方法。

总结

基于Java的脚本runtimes努力地把Java平台和你所选择的脚本语言结合在一起。在现在这个早期阶段,脚本runtime引擎有很大不同。在当前的JRuby或Jython的版本中,你可能会发现一些Java平台所没有的新特征,如注释支持。这对于架起Java语言和脚本语言之间的语义桥梁来说,同样是一种挑战,有些时候需要一些并不好看的解决方案。这就是说,大多数情况下,那些支持的特征对于使用Java平台,Java代码,脚本语言来写一个企业层应用程序来说,也是足够了。

在列表13中提到的那个双向、完整的应用程序例子是一个非阻塞的,多线程的SMTP服务器,是使用脚本语言类和Java类共同写成的。使用一个现有的Java网络类库处理低水平的、性能重要的、网络明确的任务,如线程或连接管理。控制任务通过脚本语言实现。显现出来的Java平台和脚本语言之间的协和作用使得写出高性能、可升级的应用程序成为了一种可能。所面临的挑战是如何为恰当的任务选择恰当的语言。

在脚本方面,你可以选择现有的语言,如JRuby或Jython,和一个适用于运行在Java平台上的脚本语言,如Groovy。第一组使得Java类看起来像是有规则的脚本。Groovy使用一个与Java代码很相似的语法,但是它更进化,每一个Groovy类是一个丰满的Java类。Groovy与其他脚本语言相比,对Java开发者来说更简单,它可以完全无缝地使用Java类库,不需要任何中转。

想要学习更多的关于在Java平台上使用脚本的方法,或是了解“polyglot”编程,或是对这篇文章中提到的语言感兴趣,可以参看资源部分

作者简历

Gregor Roth是一个软件架构师,工作于United Internet组,这是一个欧洲Internet服务提供商。他所感兴趣的领域包括:软件和系统结构,企业结构管理,面向对象设计,分布式计算,开发方法,当然还包括Java。他的职业生涯是开始于C和基于汇编的微型控制器应用软件。1997年,他开始设计和开发大型的、基于Java的分布式企业系统。

查看本文来源

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章