2011年6月15日
最近在网上看到的相关材料都比较陈旧,也太简略,参看了一下其他人的内容,针对Hive2.1.1做点分享:
1)下载apache-hive-2.1.1-bin.tar.gz
2)解压缩,下面的命令行如启动报错,请自行查略Hive启动配置
3)启动
hiveserver2 (非必须,使用jdbc访问的时候才使用)
bin目录下
hive --service hiveserver2 -p10001来启动hiveserver2 服务(默认为10000端口)
nohup hive --service hiverserver2 -p10001可以在后台跑
4)
hive脚本运行流程bin目录下,使用命令方法为:
./hive <parameters> --service serviceName <service parameters>
举例:hive --debug :
查看bin/hive文件
流程中会判断$1=‘--debug’则$DEBUG=‘--debug’
if [ "$DEBUG" ]; then
if [ "$HELP" ]; then //如还有--help,就会执行debug_help方法。
debug_help
exit 0
else
get_debug_params "$DEBUG"
export HADOOP_CLIENT_OPTS="$HADOOP_CLIENT_OPTS $HIVE_MAIN_CLIENT_DEBUG_OPTS"//设置HIVE_MAIN_CLIENT_DEBUG_OPTS的参数中加入debug相应参数
fi
fi
if [ "$SERVICE" = "" ] ; then
if [ "$HELP" = "_help" ] ; then
SERVICE="help"
else
SERVICE="cli" //默认赋值cli
fi
fi
这个shell脚本很多变量应该是在其他sh文件中定义,其中$SERVICE_LIST就是其他很多sh文件的最开始形成的:export SERVICE_LIST="${SERVICE_LIST}${THISSERVICE} "
hive脚本最后的$TORUN "$@" ,默认情况下TORUN其实就是cli,即执行/ext/cli.sh脚本,该脚本中主要是调用/ext/util/execHiveCmd.sh 来执行最后的CliDriver。
【
shell脚本中的$*,$@和$#举例说:
脚本名称叫test.sh 入参三个: 1 2 3
运行test.sh 1 2 3后
$*为"1 2 3"(一起被引号包住)
$@为"1" "2" "3"(分别被包住)
$#为3(参数数量)
】即exec $HADOOP jar ${HIVE_LIB}/$JAR $CLASS $HIVE_OPTS "$@" //1
其中:
$HADOOP=$HADOOP_HOME/bin/hadoop 【hive脚本中定义HADOOP=$HADOOP_HOME/bin/hadoop】
$CLASS=org.apache.hadoop.hive.cli.CliDriver【传入的第一个参数,在cli.sh中有定义】
hadoop脚本(2.7.3为例)中最终会执行:
# Always respect HADOOP_OPTS and HADOOP_CLIENT_OPTS
HADOOP_OPTS="$HADOOP_OPTS $HADOOP_CLIENT_OPTS"
#make sure security appender is turned off
HADOOP_OPTS="$HADOOP_OPTS -Dhadoop.security.logger=${HADOOP_SECURITY_LOGGER:-INFO,NullAppender}"
export CLASSPATH=$CLASSPATH
exec "$JAVA" $JAVA_HEAP_MAX $HADOOP_OPTS $CLASS "$@" //2
hive的debug参数就是在启动hive脚本时放到HADOOP_OPTS中的
1和2处结合可得到最终的运行命令,查看一下运行结果:ps -ef|grep CliDriver
/usr/java/jdk1.8.0_101/bin/java -Xmx256m -Djava.net.preferIPv4Stack=true -Dhadoop.log.dir=.. -Dhadoop.log.file=hadoop.log -Dhadoop.home.dir=.. -Dhadoop.id.str=root -Dhadoop.root.logger=INFO,console -Djava.library.path=.. -Dhadoop.policy.file=hadoop-policy.xml -Djava.net.preferIPv4Stack=true -Xmx512m -Dproc_hivecli -XX:+UseParallelGC -agentlib:jdwp=transport=dt_socket,server=y,address=8000,suspend=y -Dlog4j.configurationFile=hive-log4j2.properties -Djava.util.logging.config.file=..
-Dhadoop.security.logger=INFO,NullAppender org.apache.hadoop.util.RunJar /yuxh/app/apache-hive-2.*/lib/hive-cli-2.*.jar org.apache.hadoop.hive.cli.CliDriver
appfuse3.5使用Hibernate4.3.6, 而Hibernate日志框架使用了jboss-logging,想在后台打出sql的参数一直无法生效。
检查配置文件,框架里面的两个配置文件,src/test/resources/log4j2.xml(单元测试时配置),src/main/resources/log4j2.xml(运行时配置)
搞清log4j2的配置后,各种修改(主要是
<Logger name="org.hibernate.SQL" level="trace"/>
<Logger name="org.hibernate.type" level="trace"/>)
用junit测试任然无法打印出真实参数。根据这些实践,确定log4j2是使用无误生效的,只是org.hibernate这部分的logger一直未起效
参考国内外网站,一直无人回答hibernate4的这个问题,有人指出这部分Hibernate官方文档只是提了一句,一直未更新相关内容。最后有人提到应该是 jboss-logging 的LoggerProviders这个类的问题,看实现对log4j2已经做支持。最后发现 jboss-logging使用的是3.2.0.beta,对比相关类的源代码,更改为3.2.0.Final,生效!
P.S 把这个问题提交给Appfuse官网,issue APF-1478,作者标志为4.0版本修复。
新电脑装上eclipse4.4.2,导入maven项目之后,依赖库总是有很多错误。最后搜索到可能是eclipse的bug(据说是
JAVA_HOME没有正确传递
),查看到eclipse默认的是安装的jre目录,修改到jdk目录下,依赖问题解决。
不过目前版本仍然没有解决pom文件的“Plugin execution not covered by lifecycle configuration”错误,暂时忽略不管吧。
本打算继承一个API中的Parent类(Parent继承自GrandParent类),重写其中的service方法,copy了Parent的service方法。不过发现Parent的service中也有super.service方法。当时考虑直接调用GrandParent的service方法。。。未遂(包括反射也不行)。正好看到老外写的一篇文章,翻译:
在Son类里面写一个test方法:
public void test() {
super.test();
this.test();
}
反编译之后:
public void test()
{
// 0 0:aload_0
// 1 1:invokespecial #2 <Method void Parent.test()>
// 2 4:aload_0
// 3 5:invokevirtual #3 <Method void test()>
// 4 8:return
}
使用ASM可以完成对GrandParent方法的调用
public class GrandParent {
public void test() {
System.out.println("test of GrandParent");
}
}
public class Parent extends GrandParent{
public void test() {
System.out.println("test of Parent");
}
}
public class Son extends Parent{
public void test() {
System.out.println("test of Son");
}
}
调用Son实例的test方法只会执行Son的test方法。而ASM可以修改class,先写一个Example类继承Son,重写test方法
1 import java.io.FileOutputStream;
2
3 import org.objectweb.asm.ClassWriter;
4 import org.objectweb.asm.MethodVisitor;
5 import org.objectweb.asm.Opcodes;
6
7 public class ASMByteCodeManipulation extends ClassLoader implements Opcodes {
8
9 public static void main(String args[]) throws Exception {
10 ClassWriter cw = new ClassWriter(0);
11 cw.visit(V1_1, ACC_PUBLIC, "Example", null, "Son", null);
12
13 // creates a MethodWriter for the (implicit) constructor
14 MethodVisitor mw = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null,null);
15 mw.visitVarInsn(ALOAD, 0);
16 mw.visitMethodInsn(INVOKESPECIAL, "Son", "<init>", "()V");
17 mw.visitInsn(RETURN);
18 mw.visitMaxs(1, 1);
19 mw.visitEnd();
20
21 // creates a MethodWriter for the 'test' method
22 mw = cw.visitMethod(ACC_PUBLIC, "test", "()V", null, null);
23 mw.visitFieldInsn(GETSTATIC, "java/lang/System", "out","Ljava/io/PrintStream;");
24 mw.visitLdcInsn("test of AI3");
25 mw.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println",
26 "(Ljava/lang/String;)V");
27 //Call test() of GrandParent
28 mw.visitVarInsn(ALOAD, 0);
29 mw.visitMethodInsn(INVOKESPECIAL, "GrandParent", "test", "()V");
30 //Call test() of GrandParent
31 mw.visitVarInsn(ALOAD, 0);
32 mw.visitMethodInsn(INVOKESPECIAL, "Parent", "test", "()V");
33 //Call test() of GrandParent
34 mw.visitVarInsn(ALOAD, 0);
35 mw.visitMethodInsn(INVOKESPECIAL, "Son", "test", "()V");
36 mw.visitInsn(RETURN);
37 mw.visitMaxs(2, 1);
38 mw.visitEnd();
39
40 byte[] code = cw.toByteArray();
41 FileOutputStream fos = new FileOutputStream("Example.class");
42 fos.write(code);
43 fos.close();
44
45 ASMByteCodeManipulation loader = new ASMByteCodeManipulation();
46 Class<?> exampleClass = loader.defineClass("Example", code, 0,
47 code.length);
48 Object obj = exampleClass.newInstance();
49 exampleClass.getMethod("test", null).invoke(obj, null);
50
51 }
52 }
输出:
test of AI3
test of GrandParent
test of Parent
test of Son
看看怎样实现的,11行定义一个新的类Example继承Son。22行,Example重写test方法,先打印“test of AI3”,再分别在29、32、35行调用
GrandParent、Parent、Son的test方法。 main方法中,45行创建Example的实例,再用反射调他的test方法。
使用invokespecial这种方式也有局限,只能从子类调用。否则报错:
Exception in thread "main" java.lang.VerifyError: (class: Example, method: test1 signature: (LAI2;)V) Illegal use of nonvirtual function call
使用Google calendar v3 API的时候,大量发现Builder使用。比如Credential类,查了查Builder模式的讲解,始终感觉代码的实现和标准定义不太相同。最后发现这种实现方式是《Effective java 2nd》中的一种实现(Item 2: Consider a builder when faced with many constructor parameters)。静态工厂和构造器都有一个通病:对于存在大量可选构造参数的对象,扩展性不好。经典的解决方案是提供多个构造函数,第一个构造函数只有必须的参数,第二个构造函数除了必须参数还有一个可选参数,第三个除了必须参数还有两个可选参数。。。这样下去知道最后一个可选参数出现(
telescoping constructor)。这种方案的问题是,当构建对象的时候很容易把其中两个参数的位置放反。。。。
(难发现的bug)。
另一种解决方案是JavaBean 模式,先调用无参构造函数再调用各个set方法来组装对象。这种方案的问题是不能强制一致性。如果没有set某些必须的参数的话,对象可能处于不一致(
inconsistent)的状态(难发现的bug)。另外一个缺点是JavaBean模式不能让类immutable,需要程序员额外工作保证线程安全。
第三种方式就是Builder设计模式。这种方式混合了
telescoping constructor模式的安全性和JavaBean模式的可读性。客户端调用有所有必填参数的构造器(或静态工厂),得到一个builder对象。然后调用builder对象的方法去set各个选填参数。最后调用无参的build方法产生一个immutable的对象实例。immutable对象有非常多优点而且可能很有用。builder的set方法都是返回builder本身,所以调用也是可以chained。如:
GoogleCredential credentialNew = new GoogleCredential.Builder().setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY).setClientSecrets(clientSecrets)
.addRefreshListener(new CredentialStoreRefreshListener(userID, new DBCredentialStore())).build()
.setAccessToken(accessToken).setRefreshToken(refreshToken)
客户端代码很好写,更重要的是易读。Builder模式模拟了在Ada和Python语言里的命名可选参数(
named optional parameters)。
同时Builder类设置为static也是对Item 22:Favor static member classes over nonstatic的实践
以典型的客户端-服务器端授权为例
一 基本流程
使用Google Calendar v3 ,如果以servlet作为代理,可以使用官方示例,自己写一个类A.java继承AbstractAuthorizationCodeServlet类,这个类主要用于跳转到google提供的授权页面,如果用户同意授权,则根据A类中的URL(这个必须和注册的google 回调路径相同,比如oauth_callback否则报错)重定向到B类,B.java 继承AbstractAuthorizationCodeCallbackServlet类,这个访问路径类似http://www.example.com/oauth_callback?code=ABC1234。这里我配置oauth_callback为servlet的访问路径,B类中的
onSuccess方法将根据获得的access Token(这是根据传过来的code获得的)做业务操作。
二 需要参数的情况
有些业务需要用户传参数,
直接传参数给A,再试图在B中获取是不行的!B类中只能获取某些
固定的参数,如code。要想传用户参数,我们可以在A中先获取,把几个参数组装为json格式字符串(还可以继续base64编码),把这个字符串作为state的值,再重定向到授权页面,同意后state参数可以传到B类,取值解析json字符串(或先base64解码),得到参数。
由于API中AuthorizationCodeRequestUrl有处理state的方法,而AbstractAuthorizationCodeServlet已经直接封装,为了使用setState,直接在A类中继承HttpServlet重写service方法,复制大部分AbstractAuthorizationCodeServlet的内容,稍作修改:
resp.sendRedirect(flow.newAuthorizationUrl()
.setState(json).setRedirectUri(redirectUri).build());
三 关于refresh token
默认情况下,用户授权之后token会有一个小时的有效期,之后你可以通过refresh token再重新获取token。所以,如果不需要用户再次授权,可以在第一次,保存好token、refresh token、ExpirationTime。实例中用了JDO来实现,自己如果使用数据库保存,可类似写一个类实现CredentialStore类。使用的时候,现在数据库中取出,再创建credential,如:
GoogleCredential credentialNew = new GoogleCredential.Builder().setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY).setClientSecrets(clientSecrets)
.addRefreshListener(new CredentialStoreRefreshListener(userID, new DBCredentialStore())).build()
.setAccessToken(accessToken).setRefreshToken(refreshToken)
.setExpirationTimeMilliseconds(expirationTimeMilliseconds);
在无效的情况下,Listener会自动去用refresh token请求。
json格式经常需要用到,google提供了一个处理json的项目:GSON,能很方便的处理转换java对象和JSON表达。他不需要使用annotation,也不需要对象的源代码就能使用。
以字符串为例介绍:
1 。构造json 字符串
例如要传送json格式的字符串
String appID = req.getParameter("appID");
String userID = req.getParameter("userID");
Map map = new HashMap();
map.put("appID", appID);
map.put("userID", userID);
Gson gson = new Gson();
String state = gson.toJson(map);
2.解析json字符串
JsonParser jsonparer = new JsonParser();//初始化解析json格式的对象
String state = req.getParameter("state");
String appID = jsonparer.parse(state).getAsJsonObject().get("appID").getAsString();
String userID = jsonparer.parse(state).getAsJsonObject().get("userID").getAsString();
通用协调时(UTC, Universal Time Coordinated),格林尼治平均时(GMT, Greenwich Mean Time) 由于历史原因,这两个时间是一样的。
北京时区是东八区,领先UTC八个小时,在电子邮件信头的Date域记为+0800。
转换中,最重要的公式就是:
UTC + 时区差 = 本地时间
public static Calendar convertToGmt(Calendar cal) {
Date date = cal.getTime();
TimeZone tz = cal.getTimeZone();
System.out.println("input calendar has date [" + date + "]");
// Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT
long msFromEpochGmt = date.getTime();
// gives you the current offset in ms from GMT at the current date
int offsetFromUTC = tz.getOffset(msFromEpochGmt);
System.out.println("offset is " + offsetFromUTC);
// create a new calendar in GMT timezone, set to this date and add the offset
Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
Calendar utcCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
gmtCal.setTime(date);
//根据东西时区,选择offsetFromUTC为正或负数
gmtCal.add(Calendar.MILLISECOND, offsetFromUTC);
utcCal.setTime(date);
utcCal.add(Calendar.MILLISECOND, offsetFromUTC);
System.out.println("Created GMT cal with date [" + gmtCal.getTime()
+ "==" + utcCal.getTime() + "]");
return gmtCal;
}
Andriod 到3.2版本为止,webview方式下使用<input type="file" />点击后都没有反应。实际上顶层是有隐含的接口没实现的,可以自己重写这个方法来实现。以phonegap为例:
public class App extends DroidGap {
private ValueCallback<Uri> mUploadMessage;
private final static int FILECHOOSER_RESULTCODE = 1;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.init();
// WebView wv = new WebView(this);
// wv.setWebViewClient(new WebViewClient());
this.appView.setWebChromeClient(new CordovaChromeClient(App.this) {
// For Android 3.0+
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
App.this.startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
}
// The undocumented magic method override
// Eclipse will swear at you if you try to put @Override here
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
App.this.startActivityForResult(Intent.createChooser(i, "File Chooser"), App.FILECHOOSER_RESULTCODE);
}
});
// setContentView(wv);
super.loadUrl("file:///android_asset/www/login.html");
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == mUploadMessage)
return;
Uri result = intent == null || resultCode != RESULT_OK ? null : intent.getData();
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
}
}
}
如果直接的webview方式,extends WebChromeClient即可。
参考:http://stackoverflow.com/questions/5907369/file-upload-in-webview
weather.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*,java.net.*"%>
<%
StringBuffer sbf = new StringBuffer();
//Access the page
try {
//如果网络设置了代理
System.setProperty("http.proxyHost", "xxx");
System.setProperty("http.proxyPort", "80");
URL url = new URL("http://www.google.com/ig/api?weather=london");
URLConnection urlConn = url.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(urlConn.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null)
sbf.append(inputLine);
in.close();
System.out.println("last="+sbf.toString());
} catch (MalformedURLException e) {
System.out.println("MalformedURLException"+e);
} catch (IOException e) {
System.out.println("IOException"+e);
}
%><%=sbf.toString()%>
前台js部分:
var childData = function(selector, arg)
{
return selector.find(arg).attr('data');
}
$.ajax({
type : "GET",
data : "where=" ,
url : "weather.jsp",
success : function(data) {
console.debug('data='+data);
forecast = $(data).find('forecast_information');
cCondition = $(data).find('current_conditions');
city = childData(forecast, 'city');
if (city != undefined) {
date = childData(forecast, 'forecast_date');
condition = childData(cCondition, 'condition');
tempC = childData(cCondition, 'temp_c');
humidity = childData(cCondition, 'humidity');
icon = childData(cCondition, 'icon');
$('#city').text(city);
$('#date').text(date);
$('#condition').text(condition);
$('#tempC').html(tempC + '° C');
$('#humidity').text(humidity);
$('#icon').attr({
'src' : 'http://www.google.com' + icon
});
$('#data').stop().show('fast');
} else {
$('#error').stop().show('fast');
}
}
});
1. Code first approach:可能不能完全发挥框架和web services的能量,但能完成目标。减少了学习曲线,不用非常透彻了解web services概念,只要对某个框架有一定了解就能完成任务。
2.Contract first approach:根据服务先写WSDL文件,写好之后使用框架的工具把WSDL转换为依赖框架的代码。
一 介绍
当客户端调用你的web service的时候,他会发送一个消息过来(可能是soap 消息),如:
<foo:concatRequest>
<s1>abc</s1>
<s2>123</s2>
</foo:concatRequest>
这时候如果有一个转换器把这个soap消息转换成java对象,然后调用你提供的java对象(方法)的话将会是非常方便的。几个最流行的库就是充当了这种转换器功能,比如CXF, Axis2 , Metro (jdk6自带的有)。
手动创建WSDL文件比较容易出错,可以使用eclipse进行可视化编辑。
二 生成服务代码
像CXF这样的 web service库可以创建转换器把进来的SOAP 消息转换为Java对象,然后作为参数传给方法。生成这些代码,只需创建一个main:
1 CXF方式:
public static void main(String[] args) {
WSDLToJava.main(new String[] {
"-server",
"-d", "src/main/java",
"src/main/resources/SimpleService.wsdl" });
System.out.println("Done!");
}
运行后会生成service endpoint interface(SEI),我们再写一个类(比如SimpleServiceImpl)来实现这个接口,写入自己的业务。还会生成传入消息对应的java对象。同时生成一个服务器类:
public class SimpleService_P1_Server {
protected SimpleService_P1_Server() throws Exception {
System.out.println("Starting Server");
Object implementor = new SimpleServiceImpl();
String address = "http://localhost:8080/ss/p1";
Endpoint.publish(address, implementor);
}
public static void main(String args[]) throws Exception {
new SimpleService_P1_Server();
System.out.println("Server ready...");
Thread.sleep(5 * 60 * 1000);
System.out.println("Server exiting");
System.exit(0);
}
}
运行这个类你的web service就可以服务了。
2 Axis2 方式
用类似的写main方法,或者配置eclipse的axis2插件可生成:在WSDL文件上,右键->web service->generate java bean skeleton
界面的上半部分针对服务端,可以根据需要调整生成的级别,下半部分是生成客户端。具体的级别可参考eclipse的帮助文档。一路下一步,最后根据命名空间生成包路径的文件,里面有XXSkeletonInterface.java 文件(如果生成的时候选择了生成接口的话),还有一个XXSkeleton实现了这个接口,也是我们需要修改这部分代码完成我们业务的地方。实际上有一个XXMessageReceiverInOut.java的类接收请求的消息,并调用XXSkeletonInterface。使用eclipse的axis2插件的时候,会自动在web-inf文件夹下生成service\xx(你的wsdl服务名),这下面还要一个meta-inf文件夹装有wsd文件和一个services.xml配置文件。services.xml文件可配置包括XXMessageReceiverInOut类在内的选项。
二 生成客户端代码
为了调用这些web service,同样可以用CXF这些库来生成在客户端运行的转换器(称为service stub)。当调用stub里的方法的时候,他会把你的数据/对象 转换为正确的XML格式,然后发送给真正的web service。当他收到响应的时候,又会把XML转回Java。
1 CXF 方式
和生成服务器端类似,使用方法
WSDLToJava.main(new String[] {
"-client",
"-d", "src/main/java",
"src/main/resources/SimpleService.wsdl" });
运行后会生成客户端代码:
public final class SimpleService_P1_Client {
private static final QName SERVICE_NAME = new QName("http://ttdev.com/ss",
"SimpleService");
private SimpleService_P1_Client() {}
public static void main(String args[]) throws Exception {
URL wsdlURL = SimpleService_Service.WSDL_LOCATION;
if (args.length > 0) {
File wsdlFile = new File(args[0]);
try {
if (wsdlFile.exists()) {
wsdlURL = wsdlFile.toURI().toURL();
} else {
wsdlURL = new URL(args[0]);
}
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
SimpleService_Service ss =
new SimpleService_Service(wsdlURL, SERVICE_NAME);
SimpleService port = ss.getP1();
{
System.out.println("Invoking concat...");
com.ttdev.ss.ConcatRequest _concat_parameters = null;
java.lang.String _concat__return = port.concat(_concat_parameters);
System.out.println("concat.result=" + _concat__return);
}
System.exit(0);
}
}
SimpleService_Service是创建的service stub,他模拟了客户端的服务。我们需要修改这个类中的_concat_parameters部分,加入参数:
com.ttdev.ss.ConcatRequest _concat_parameters = new ConcatRequest();
_concat_parameters.setS1("abc");
_concat_parameters.setS2("123");
现在就可以运行客户端代码了。SEI中有一些注解,可以修改,不细说。
2 Axis2 方式
和生成服务端类似,利用eclipse插件直接生成,包路径类似,有一个XXStub类,这个类里面有包括请求和应答消息在内的内部类。使用的时候,先对请求消息参数类按业务需求赋值,最后调用Stub的请求方法。可以使用Stub的构造函数指定目标endpoint。
有两种SOAP message风格,document 和RPC,他们定义了SOAP message body的格式。使用document风格时(包括wrapped和unwrapped),在wsdl中有一个非空的types部分,这个部分用XML Schema language定义了web service要用到的类型。wsgen工具从SIB(有SEI就足够了)中生成与XSD对应的java类。用java代码生成WSDL文件的时候需要一些java类,wsgen工具可以生成这些Java类,生成的这些java类被称为wsgen artifacts,底层的JWS类库会用到这些类,特别是JAX-B系列的包,会用来转换(marshal)java类实例(that is, Java in-memory objects)为XML类型的XML实例(满足XML Schema document的XML文档实例),
The inverse operation is used to convert (unmarshal) an XML document instance to an in-memory
object, an object of a Java type or a comparable type in some other language。因此wsgen工具生成的artifacts,支持了Java为基础的web service的互操作性。JAX-B类库提供了Java和XSD类型转换的底层支持。
For the most part, the wsgen utility can be used without our bothering to inspect the artifacts that it produces. For the most part, JAX-B remains unseen infrastructure.
wsgen artifacts实际上是wsdl message的数据类型,他们和XML Schema type绑定,每个message的XML Schema types从这些java类型得来的。注:在当前的jdk1.6.24中,已经融入wsgen自动生成的过程,不需手动调用。
wsgen工具可用来生成wsdl文件,如:% wsgen -cp "." -wsdl ch01.ts.TimeServerImpl 。这为TimeServer服务生成了wsdl。用wsgen生成的wsdl和通过访问发布的服务生成的wsdl 有个很大的区别:wsgen生成的没有endpoint,因为这个URL是在发布服务的时候决定的。其他地方两个wsdl是相同的。
wsimport(以前叫wsdl2java和 java2wsdl更形象)工具可使用WSDL生成用来帮助写客户端的artifacts .
1 先契约再编码方式
一个例子:得到一个tempConvert.wsdl文件,使用命令 wsimport -keep -p ch02.tc tempConvert.wsdl ,命令根据wsdl的portType生成一个SEI类,把SEI的interface换为class,再把方法改为实现就可变为SIB。把该SIB发布,再使用命令wsimport -keep -p clientTC http://localhost:5599/tc?wsdl,来生成客户端辅助类
2 编码优先
服务被发布之后,会自动生成WSDL供客户端使用。然而,使用annotations可以控制WSDL或WSDL-generated artifacts的生成。
来自
http://stackoverflow.com/questions/1099300/whats-the-difference-between-getpath-getabsolutepath-and-getcanonicalpath
C:\temp\file.txt" - this is a path, an absolute path, a canonical path
.\file.txt This is a path, It's not an absolute path nor canonical path.
C:\temp\myapp\bin\..\\..\file.txt
This is a path, and an absolute path, it's not a canonical path
Canonical path is always an absolute path.
Converting from a path to a canonical path makes it absolute (通常会处理改变当前目录,所以像. ./file.txt 变为c:/temp/file.txt). The canonical path of a file just "purifies" the path, 去除和解析类似“ ..\” and resolving symlinks(on unixes)
In short:
- getPath() gets the path string that the File object was constructed with, and it may be relative current directory.
- getAbsolutePath() gets the path string after resolving it against the current directory if it's relative, resulting in a fully qualified path.
- getCanonicalPath() gets the path string after resolving any relative path against current directory, and removes any relative pathing (. and ..), and any file system links to return a path which the file system considers the canonical means to reference the file system object to which it points.
Also, each of this has a File equivalent which returns the corresponding File object.
The best way I have found to get a feel for things like this is to try them out:
import java.io.File;
public class PathTesting {
public static void main(String [] args) {
File f = new File("test/.././file.txt");
System.out.println(f.getPath());
System.out.println(f.getAbsolutePath());
try {
System.out.println(f.getCanonicalPath());
}
catch(Exception e) {}
}
}
Your output will be something like:
test\..\.\file.txt
C:\projects\sandbox\trunk\test\..\.\file.txt
C:\projects\sandbox\trunk\file.txt
So, getPath()
gives you the path based on the File object, which may or may not be relative; getAbsolutePath()
gives you an absolute path to the file; and getCanonicalPath()
gives you the unique absolute path to the file. Notice that there are a huge number of absolute paths that point to the same file, but only one canonical path.
When to use each? Depends on what you're trying to accomplish, but if you were trying to see if two
Files
are pointing at the same file on disk, you could compare their canonical paths.