2011年6月24日
最近在網(wǎng)上看到的相關(guān)材料都比較陳舊,也太簡略,參看了一下其他人的內(nèi)容,針對Hive2.1.1做點(diǎn)分享:
1)下載apache-hive-2.1.1-bin.tar.gz
2)解壓縮,下面的命令行如啟動報錯,請自行查略Hive啟動配置
3)啟動
hiveserver2 (非必須,使用jdbc訪問的時候才使用)
bin目錄下
hive --service hiveserver2 -p10001來啟動hiveserver2 服務(wù)(默認(rèn)為10000端口)
nohup hive --service hiverserver2 -p10001可以在后臺跑
4)
hive腳本運(yùn)行流程bin目錄下,使用命令方法為:
./hive <parameters> --service serviceName <service parameters>
舉例:hive --debug :
查看bin/hive文件
流程中會判斷$1=‘--debug’則$DEBUG=‘--debug’
if [ "$DEBUG" ]; then
if [ "$HELP" ]; then //如還有--help,就會執(zhí)行debug_help方法。
debug_help
exit 0
else
get_debug_params "$DEBUG"
export HADOOP_CLIENT_OPTS="$HADOOP_CLIENT_OPTS $HIVE_MAIN_CLIENT_DEBUG_OPTS"http://設(shè)置HIVE_MAIN_CLIENT_DEBUG_OPTS的參數(shù)中加入debug相應(yīng)參數(shù)
fi
fi
if [ "$SERVICE" = "" ] ; then
if [ "$HELP" = "_help" ] ; then
SERVICE="help"
else
SERVICE="cli" //默認(rèn)賦值cli
fi
fi
這個shell腳本很多變量應(yīng)該是在其他sh文件中定義,其中$SERVICE_LIST就是其他很多sh文件的最開始形成的:export SERVICE_LIST="${SERVICE_LIST}${THISSERVICE} "
hive腳本最后的$TORUN "$@" ,默認(rèn)情況下TORUN其實(shí)就是cli,即執(zhí)行/ext/cli.sh腳本,該腳本中主要是調(diào)用/ext/util/execHiveCmd.sh 來執(zhí)行最后的CliDriver。
【
shell腳本中的$*,$@和$#舉例說:
腳本名稱叫test.sh 入?yún)⑷齻€: 1 2 3
運(yùn)行test.sh 1 2 3后
$*為"1 2 3"(一起被引號包住)
$@為"1" "2" "3"(分別被包住)
$#為3(參數(shù)數(shù)量)
】即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【傳入的第一個參數(shù),在cli.sh中有定義】
hadoop腳本(2.7.3為例)中最終會執(zhí)行:
# 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參數(shù)就是在啟動hive腳本時放到HADOOP_OPTS中的
1和2處結(jié)合可得到最終的運(yùn)行命令,查看一下運(yùn)行結(jié)果: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的參數(shù)一直無法生效。
檢查配置文件,框架里面的兩個配置文件,src/test/resources/log4j2.xml(單元測試時配置),src/main/resources/log4j2.xml(運(yùn)行時配置)
搞清log4j2的配置后,各種修改(主要是
<Logger name="org.hibernate.SQL" level="trace"/>
<Logger name="org.hibernate.type" level="trace"/>)
用junit測試任然無法打印出真實(shí)參數(shù)。根據(jù)這些實(shí)踐,確定log4j2是使用無誤生效的,只是org.hibernate這部分的logger一直未起效
參考國內(nèi)外網(wǎng)站,一直無人回答hibernate4的這個問題,有人指出這部分Hibernate官方文檔只是提了一句,一直未更新相關(guān)內(nèi)容。最后有人提到應(yīng)該是 jboss-logging 的LoggerProviders這個類的問題,看實(shí)現(xiàn)對log4j2已經(jīng)做支持。最后發(fā)現(xiàn) jboss-logging使用的是3.2.0.beta,對比相關(guān)類的源代碼,更改為3.2.0.Final,生效!
P.S 把這個問題提交給Appfuse官網(wǎng),issue APF-1478,作者標(biāo)志為4.0版本修復(fù)。
新電腦裝上eclipse4.4.2,導(dǎo)入maven項(xiàng)目之后,依賴庫總是有很多錯誤。最后搜索到可能是eclipse的bug(據(jù)說是
JAVA_HOME沒有正確傳遞
),查看到eclipse默認(rèn)的是安裝的jre目錄,修改到j(luò)dk目錄下,依賴問題解決。
不過目前版本仍然沒有解決pom文件的“Plugin execution not covered by lifecycle configuration”錯誤,暫時忽略不管吧。
本打算繼承一個API中的Parent類(Parent繼承自GrandParent類),重寫其中的service方法,copy了Parent的service方法。不過發(fā)現(xiàn)Parent的service中也有super.service方法。當(dāng)時考慮直接調(diào)用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方法的調(diào)用
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");
}
}
調(diào)用Son實(shí)例的test方法只會執(zhí)行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
看看怎樣實(shí)現(xiàn)的,11行定義一個新的類Example繼承Son。22行,Example重寫test方法,先打印“test of AI3”,再分別在29、32、35行調(diào)用
GrandParent、Parent、Son的test方法。 main方法中,45行創(chuàng)建Example的實(shí)例,再用反射調(diào)他的test方法。
使用invokespecial這種方式也有局限,只能從子類調(diào)用。否則報錯:
Exception in thread "main" java.lang.VerifyError: (class: Example, method: test1 signature: (LAI2;)V) Illegal use of nonvirtual function call
使用Google calendar v3 API的時候,大量發(fā)現(xiàn)Builder使用。比如Credential類,查了查Builder模式的講解,始終感覺代碼的實(shí)現(xiàn)和標(biāo)準(zhǔn)定義不太相同。最后發(fā)現(xiàn)這種實(shí)現(xiàn)方式是《Effective java 2nd》中的一種實(shí)現(xiàn)(Item 2: Consider a builder when faced with many constructor parameters)。靜態(tài)工廠和構(gòu)造器都有一個通病:對于存在大量可選構(gòu)造參數(shù)的對象,擴(kuò)展性不好。經(jīng)典的解決方案是提供多個構(gòu)造函數(shù),第一個構(gòu)造函數(shù)只有必須的參數(shù),第二個構(gòu)造函數(shù)除了必須參數(shù)還有一個可選參數(shù),第三個除了必須參數(shù)還有兩個可選參數(shù)。。。這樣下去知道最后一個可選參數(shù)出現(xiàn)(
telescoping constructor)。這種方案的問題是,當(dāng)構(gòu)建對象的時候很容易把其中兩個參數(shù)的位置放反。。。。
(難發(fā)現(xiàn)的bug)。
另一種解決方案是JavaBean 模式,先調(diào)用無參構(gòu)造函數(shù)再調(diào)用各個set方法來組裝對象。這種方案的問題是不能強(qiáng)制一致性。如果沒有set某些必須的參數(shù)的話,對象可能處于不一致(
inconsistent)的狀態(tài)(難發(fā)現(xiàn)的bug)。另外一個缺點(diǎn)是JavaBean模式不能讓類immutable,需要程序員額外工作保證線程安全。
第三種方式就是Builder設(shè)計模式。這種方式混合了
telescoping constructor模式的安全性和JavaBean模式的可讀性。客戶端調(diào)用有所有必填參數(shù)的構(gòu)造器(或靜態(tài)工廠),得到一個builder對象。然后調(diào)用builder對象的方法去set各個選填參數(shù)。最后調(diào)用無參的build方法產(chǎn)生一個immutable的對象實(shí)例。immutable對象有非常多優(yōu)點(diǎn)而且可能很有用。builder的set方法都是返回builder本身,所以調(diào)用也是可以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語言里的命名可選參數(shù)(
named optional parameters)。
同時Builder類設(shè)置為static也是對Item 22:Favor static member classes over nonstatic的實(shí)踐
以典型的客戶端-服務(wù)器端授權(quán)為例
一 基本流程
使用Google Calendar v3 ,如果以servlet作為代理,可以使用官方示例,自己寫一個類A.java繼承AbstractAuthorizationCodeServlet類,這個類主要用于跳轉(zhuǎn)到google提供的授權(quán)頁面,如果用戶同意授權(quán),則根據(jù)A類中的URL(這個必須和注冊的google 回調(diào)路徑相同,比如oauth_callback否則報錯)重定向到B類,B.java 繼承AbstractAuthorizationCodeCallbackServlet類,這個訪問路徑類似http://www.example.com/oauth_callback?code=ABC1234。這里我配置oauth_callback為servlet的訪問路徑,B類中的
onSuccess方法將根據(jù)獲得的access Token(這是根據(jù)傳過來的code獲得的)做業(yè)務(wù)操作。
二 需要參數(shù)的情況
有些業(yè)務(wù)需要用戶傳參數(shù),
直接傳參數(shù)給A,再試圖在B中獲取是不行的!B類中只能獲取某些
固定的參數(shù),如code。要想傳用戶參數(shù),我們可以在A中先獲取,把幾個參數(shù)組裝為json格式字符串(還可以繼續(xù)base64編碼),把這個字符串作為state的值,再重定向到授權(quán)頁面,同意后state參數(shù)可以傳到B類,取值解析json字符串(或先base64解碼),得到參數(shù)。
由于API中AuthorizationCodeRequestUrl有處理state的方法,而AbstractAuthorizationCodeServlet已經(jīng)直接封裝,為了使用setState,直接在A類中繼承HttpServlet重寫service方法,復(fù)制大部分AbstractAuthorizationCodeServlet的內(nèi)容,稍作修改:
resp.sendRedirect(flow.newAuthorizationUrl()
.setState(json).setRedirectUri(redirectUri).build());
三 關(guān)于refresh token
默認(rèn)情況下,用戶授權(quán)之后token會有一個小時的有效期,之后你可以通過refresh token再重新獲取token。所以,如果不需要用戶再次授權(quán),可以在第一次,保存好token、refresh token、ExpirationTime。實(shí)例中用了JDO來實(shí)現(xiàn),自己如果使用數(shù)據(jù)庫保存,可類似寫一個類實(shí)現(xiàn)CredentialStore類。使用的時候,現(xiàn)在數(shù)據(jù)庫中取出,再創(chuàng)建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格式經(jīng)常需要用到,google提供了一個處理json的項(xiàng)目:GSON,能很方便的處理轉(zhuǎn)換java對象和JSON表達(dá)。他不需要使用annotation,也不需要對象的源代碼就能使用。
以字符串為例介紹:
1 。構(gòu)造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();
通用協(xié)調(diào)時(UTC, Universal Time Coordinated),格林尼治平均時(GMT, Greenwich Mean Time) 由于歷史原因,這兩個時間是一樣的。
北京時區(qū)是東八區(qū),領(lǐng)先UTC八個小時,在電子郵件信頭的Date域記為+0800。
轉(zhuǎn)換中,最重要的公式就是:
UTC + 時區(qū)差 = 本地時間
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);
//根據(jù)東西時區(qū),選擇offsetFromUTC為正或負(fù)數(shù)
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" />點(diǎn)擊后都沒有反應(yīng)。實(shí)際上頂層是有隱含的接口沒實(shí)現(xiàn)的,可以自己重寫這個方法來實(shí)現(xiàn)。以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 {
//如果網(wǎng)絡(luò)設(shè)置了代理
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:可能不能完全發(fā)揮框架和web services的能量,但能完成目標(biāo)。減少了學(xué)習(xí)曲線,不用非常透徹了解web services概念,只要對某個框架有一定了解就能完成任務(wù)。
2.Contract first approach:根據(jù)服務(wù)先寫WSDL文件,寫好之后使用框架的工具把WSDL轉(zhuǎn)換為依賴框架的代碼。
一 介紹
當(dāng)客戶端調(diào)用你的web service的時候,他會發(fā)送一個消息過來(可能是soap 消息),如:
<foo:concatRequest>
<s1>abc</s1>
<s2>123</s2>
</foo:concatRequest>
這時候如果有一個轉(zhuǎn)換器把這個soap消息轉(zhuǎn)換成java對象,然后調(diào)用你提供的java對象(方法)的話將會是非常方便的。幾個最流行的庫就是充當(dāng)了這種轉(zhuǎn)換器功能,比如CXF, Axis2 , Metro (jdk6自帶的有)。
手動創(chuàng)建WSDL文件比較容易出錯,可以使用eclipse進(jìn)行可視化編輯。
二 生成服務(wù)代碼
像CXF這樣的 web service庫可以創(chuàng)建轉(zhuǎn)換器把進(jìn)來的SOAP 消息轉(zhuǎn)換為Java對象,然后作為參數(shù)傳給方法。生成這些代碼,只需創(chuàng)建一個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!");
}
運(yùn)行后會生成service endpoint interface(SEI),我們再寫一個類(比如SimpleServiceImpl)來實(shí)現(xiàn)這個接口,寫入自己的業(yè)務(wù)。還會生成傳入消息對應(yīng)的java對象。同時生成一個服務(wù)器類:
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);
}
}
運(yùn)行這個類你的web service就可以服務(wù)了。
2 Axis2 方式
用類似的寫main方法,或者配置eclipse的axis2插件可生成:在WSDL文件上,右鍵->web service->generate java bean skeleton
界面的上半部分針對服務(wù)端,可以根據(jù)需要調(diào)整生成的級別,下半部分是生成客戶端。具體的級別可參考eclipse的幫助文檔。一路下一步,最后根據(jù)命名空間生成包路徑的文件,里面有XXSkeletonInterface.java 文件(如果生成的時候選擇了生成接口的話),還有一個XXSkeleton實(shí)現(xiàn)了這個接口,也是我們需要修改這部分代碼完成我們業(yè)務(wù)的地方。實(shí)際上有一個XXMessageReceiverInOut.java的類接收請求的消息,并調(diào)用XXSkeletonInterface。使用eclipse的axis2插件的時候,會自動在web-inf文件夾下生成service\xx(你的wsdl服務(wù)名),這下面還要一個meta-inf文件夾裝有wsd文件和一個services.xml配置文件。services.xml文件可配置包括XXMessageReceiverInOut類在內(nèi)的選項(xiàng)。
二 生成客戶端代碼
為了調(diào)用這些web service,同樣可以用CXF這些庫來生成在客戶端運(yùn)行的轉(zhuǎn)換器(稱為service stub)。當(dāng)調(diào)用stub里的方法的時候,他會把你的數(shù)據(jù)/對象 轉(zhuǎn)換為正確的XML格式,然后發(fā)送給真正的web service。當(dāng)他收到響應(yīng)的時候,又會把XML轉(zhuǎn)回Java。
1 CXF 方式
和生成服務(wù)器端類似,使用方法
WSDLToJava.main(new String[] {
"-client",
"-d", "src/main/java",
"src/main/resources/SimpleService.wsdl" });
運(yùn)行后會生成客戶端代碼:
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是創(chuàng)建的service stub,他模擬了客戶端的服務(wù)。我們需要修改這個類中的_concat_parameters部分,加入?yún)?shù):
com.ttdev.ss.ConcatRequest _concat_parameters = new ConcatRequest();
_concat_parameters.setS1("abc");
_concat_parameters.setS2("123");
現(xiàn)在就可以運(yùn)行客戶端代碼了。SEI中有一些注解,可以修改,不細(xì)說。
2 Axis2 方式
和生成服務(wù)端類似,利用eclipse插件直接生成,包路徑類似,有一個XXStub類,這個類里面有包括請求和應(yīng)答消息在內(nèi)的內(nèi)部類。使用的時候,先對請求消息參數(shù)類按業(yè)務(wù)需求賦值,最后調(diào)用Stub的請求方法。可以使用Stub的構(gòu)造函數(shù)指定目標(biāo)endpoint。
有兩種SOAP message風(fēng)格,document 和RPC,他們定義了SOAP message body的格式。使用document風(fēng)格時(包括wrapped和unwrapped),在wsdl中有一個非空的types部分,這個部分用XML Schema language定義了web service要用到的類型。wsgen工具從SIB(有SEI就足夠了)中生成與XSD對應(yīng)的java類。用java代碼生成WSDL文件的時候需要一些java類,wsgen工具可以生成這些Java類,生成的這些java類被稱為wsgen artifacts,底層的JWS類庫會用到這些類,特別是JAX-B系列的包,會用來轉(zhuǎn)換(marshal)java類實(shí)例(that is, Java in-memory objects)為XML類型的XML實(shí)例(滿足XML Schema document的XML文檔實(shí)例),
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為基礎(chǔ)的web service的互操作性。JAX-B類庫提供了Java和XSD類型轉(zhuǎn)換的底層支持。
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實(shí)際上是wsdl message的數(shù)據(jù)類型,他們和XML Schema type綁定,每個message的XML Schema types從這些java類型得來的。注:在當(dāng)前的jdk1.6.24中,已經(jīng)融入wsgen自動生成的過程,不需手動調(diào)用。
wsgen工具可用來生成wsdl文件,如:% wsgen -cp "." -wsdl ch01.ts.TimeServerImpl 。這為TimeServer服務(wù)生成了wsdl。用wsgen生成的wsdl和通過訪問發(fā)布的服務(wù)生成的wsdl 有個很大的區(qū)別:wsgen生成的沒有endpoint,因?yàn)檫@個URL是在發(fā)布服務(wù)的時候決定的。其他地方兩個wsdl是相同的。
wsimport(以前叫wsdl2java和 java2wsdl更形象)工具可使用WSDL生成用來幫助寫客戶端的artifacts .
1 先契約再編碼方式
一個例子:得到一個tempConvert.wsdl文件,使用命令 wsimport -keep -p ch02.tc tempConvert.wsdl ,命令根據(jù)wsdl的portType生成一個SEI類,把SEI的interface換為class,再把方法改為實(shí)現(xiàn)就可變?yōu)镾IB。把該SIB發(fā)布,再使用命令wsimport -keep -p clientTC http://localhost:5599/tc?wsdl,來生成客戶端輔助類
2 編碼優(yōu)先
服務(wù)被發(fā)布之后,會自動生成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 (通常會處理改變當(dāng)前目錄,所以像. ./file.txt 變?yōu)閏:/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.