最近在網上看到的相關材料都比較陳舊,也太簡略,參看了一下其他人的內容,針對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"http://設置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();