2016年6月14日 星期二

【JavaScript】bower 自動化部署 (github安裝)

- bower官方說明:
ignore [array]: An array of paths not needed in production that you want Bower to ignore when installing your package.
- 實作後:
bower.json定義的ignore的屬性,只能排除自己的package,不包含相依的套件
利用bower.json定義需要安裝的相依套件,取出需要的檔案放至自己的目錄結構,當然還要過濾掉不要的檔案。
    分成兩個部分,來完成這個目標:
  1. 在github上建一個bower.json,安裝bower套件後,再利用postinstall複製到預想的目錄結構
  2. 從bower install 自建的github專案,在此專案的bower.json中的ignore就有作用,可刪除不要的檔案與目錄,只留下預想的目錄結構
下面實作lab

Bower custom github install

bower install <github>

ignore [array]: An array of paths not needed in production that you want Bower to ignore when installing your package.」

Description: Because ignore attribute only apply project file, not it’s dependencies project.
copy the latest version to custom structure directory using postinstall at git project
then bower install from git && ignore unwanted file

Collect Bower

Bower Init

generate bower.json

Command:
bower init

Example:
#]bower init
bower existing The existing bower.json file will be used and filled in
? name components
? description test bower install
? main file static/components/angular/angular.js
? keywords bower static file install
? authors yourName yourName@yourDomain
? license MIT
? homepage
? set currently installed components as dependencies? No
? add commonly ignored files to ignore list? Yes
? would you like to mark this package as private which prevents it from being accidentally published to the registry? No
{
  name: 'components',
  authors: [
    'yourName <yourName@yourDomain>'
  ],
  description: 'test bower install',
  main: 'static/components/angular/angular.js',
  license: 'MIT',
  homepage: '',
  directory: 'static/components/',
  ignore: [
    'LICENSE.md',
    'package.json',
    'test',
    'tests',
    '**/.*',
    'node_modules',
    'bower_components'
  ],
  dependencies: {
    angular: 'angular2#^1.5.6'
  },                                                                                                                                                                                                                                         
  keywords: [                                                                                                                                                                                                                                
    'bower',                                                                                                                                                                                                                                 
    'static',                                                                                                                                                                                                                                
    'file',                                                                                                                                                                                                                                  
    'install'                                                                                                                                                                                                                                
  ]                                                                                                                                                                                                                                          
}

Bower Dependencies

diff dependencies && devDependencies

install && save to production’s dependencies at bower.json Command:
bower install bootstrap --save
install && save to production’s devDependencies at bower.json Command:
bower install bootstrap --save-dev

Script Postinstall

postinstall script

STEP1:
create .bowerrc file
Code:
{
  "scripts": {
      "postinstall": "./.bower-postinstall.sh"
  }
}
STEP2:
create postinstall scripts
copy dependencies file to your custom directory ['css', 'js', 'fonts']
Code:
for dir in js fonts css; do
    if [ ! -d $dir ]; then
        mkdir $dir
    fi
done
#minimal=".min"
minimal=""

cp -af bower_components/angular/angular${minimal}.js js/angular${minimal}.js
cp -af bower_components/bootstrap/dist/js/bootstrap${minimal}.js js/bootstrap${minimal}.js
cp -af bower_components/bootstrap/dist/fonts/* fonts/
cp -af bower_components/bootstrap/dist/css/bootstrap-theme${minimal}.css css/bootstrap-theme${minimal}.css
cp -af bower_components/bootstrap/dist/css/bootstrap${minimal}.css css/bootstrap${minimal}.css
cp -af bower_components/jquery/dist/jquery${minimal}.js js/jquery${minimal}.js
cp -af bower_components/angular-google-maps/dist/angular-google-maps${minimal}.js js/angular-google-maps${minimal}.js
cp -af bower_components/lodash/dist/lodash${minimal}.js js/lodash${minimal}.js
cp -af bower_components/angular-simple-logger/dist/angular-simple-logger${minimal}.js js/angular-simple-logger${minimal}.js
STEP3:
run bower install
Command:
bower install

Bower Install

bower install from custom bower repository

Bowerrc

install directory && postinstall setup

STEP1:
create .bowerrc file
Code:
{
  "directory": "static",
  "scripts": {
      "postinstall": "./.bower-postinstall.sh"
  }
}
STEP2:
create postinstall scripts
Code:
rm -rf static/components/.bower.json
rm -rf static/components/bower.json

Bower Ignore

ignore unwanted file

Description:
Edit bower.json
Code:
{
  ...
  "ignore": [
    "**/.*",
    ".*",
    "README.md",
    "bower_components"
  ],
  ...
}

Bower Custom Git Install

install from custom repository

Description:
bower will install to your path <directory>/<bower-project-name>
<dirctory> default is bower_components, it can be override by .bowerrc file
Command:
bower install git@<githubFQDN>:yourName/bower-lab.git

Bower File Result

finally file structure

Result:
#] ls static/components/
total 0
drwxr-xr-x 1 yourAccount yourGroup  20 Jun 13 13:45 .
drwxr-xr-x 1 yourAccount yourGroup  54 Jun 13 13:45 ..
drwxr-xr-x 1 yourAccount yourGroup  64 Jun 13 13:45 css
drwxr-xr-x 1 yourAccount yourGroup 326 Jun 13 13:45 fonts
drwxr-xr-x 1 yourAccount yourGroup 172 Jun 13 13:45 js

2016年6月1日 星期三

【Android】Sqlite 建資料表失敗


  • 問題:
  • Caused by: android.database.sqlite.SQLiteException: near "group": syntax error (code 1): , while compiling: DROP TABLE IF EXISTS group
  • 解決:
  • 參考:sqlite保留字
    The SQL standard specifies a huge number of keywords which may not be used as the names of tables, indices, columns, databases, user-defined functions, collations, virtual table modules, or any other named object.
在Android使用Sqlite做Content Provider,group table跟本沒建起來,所以在升級的時候也找不到table。因為用到的sqlite保留字,原來保留字不僅欄位不行,table名稱不行,自訂的function不行…反正就遇到就知道了 XD

【Travel】東京-富士山行_[5天4夜]_廉航晚去早回攻略 PartⅢ


星期日富士山行程是重點行程,巴士是準點發車,為了不出差錯。前一天先去決濟, 順便定位一下新宿南口的位置,去 4F 的 9 號櫃檯抽號碼牌結帳。隔天很慶幸有先買單,當日六點多起來, 7 點準點在飯店 1F提供的早餐處用餐,搭一站地鐵再走到新宿南口 8 點 5 分到,還有 10 分鐘可以上廁所,剛上車還有空位,還以為沒坐滿, 原來有些乘客是在不同站上車,幾站後就是全部滿坐,而且這是劃位坐,千萬不要去現場才買,最好一個月前先預約,以免買不到票。
到河口湖後,可以租車搭巴士騎腳踏車
  1. 如果想快速遊五湖可以租車
  2. 如果想在幾個點玩可以搭巴士
  3. 想固定兩三個點玩久一點就騎腳踏車
三選一,我是選擇騎踏實地的腳踏車,在河口湖駅對向巷子進入,左手邊就有一家,當天租是一台1500日幣,還有附locker可以鎖車,這樣停在路邊也比較安心。
整個行程非常的eazy,騎腳踏車走三個點,非常的輕鬆愉快

搭纜車到天上山公園 -> ほうとう不動 河口湖北本店 -> 音樂之森美術館

  • 先在河口湖駅內找一下天上山公園的纜車割引卷,原本720日幣,就可以650日幣購入
  • 中午我們就到ほうとう不動,點了名物ほうとう,一份1080日幣,很有飽足感
  • 下午就到「音樂之森美術館」,待一下午,順便買了音樂盒,這裡還有整點的水舞,整體來說還不錯,不過就是門票貴了些(1500日幣/人)

晚上的回程巴士,竟然遇到了塞車,多花了一個多小時才到新宿,不知道是不是星期日特有的 XD


2016年5月27日 星期五

【Travel】東京-富士山行_[5天4夜]_廉航晚去早回攻略 PartⅡ

pre-dayMystays Haneda Hotel
first-day新宿(東新宿歌舞伎町 APA 飯店 check-in)
second-day富士山
third-day台場(東新宿歌舞伎町 APA 飯店 check-out)
post-day大江戶溫泉物語
頭尾兩天搞定,再來進入主題,中間三天玩什麼?目標是去富士山河口湖,主題明確就很好規劃,去富士山的巴士是從新宿出發,當然也可以坐地鐵,但要轉車實在很累.. 自由行要玩得 graceful 當然最好一鏡到底,所以三天就是住東新宿,第一天新宿第二天富士山第三天台場

住宿可以選擇在新宿或附近的東新宿,因為兩者差一站價格有差,選在東新宿可以省一點錢。另外星期六的房價,不知日本星期六房價這麼誇張,快要是平日的兩倍,在台灣兩倍價格我遇到是出現在除夕到初三,若可以避開星期六,訂房則可以省一天的錢。
  1. 第一天:
  2. pre-day 住 mystrays 就 11 點在 checkout睡到超值,直接前往東新宿(東新宿歌舞伎町 APA 飯店 )免費寄放行李, 並跟櫃台人員說明大約晚上7點check-in,這樣省去寄放行李費用,又可以很沒有時間壓力下,在新宿玩一整個下午
  3. 第二天:
  4. 富士山的巴士要先預約新宿~富士五湖線   一個月前左右開放預約,像要訂5/15的巴士,就大約4/15訂就可以選到日期。要選往返價錢一個人 3,500 日幣
  5. 第三天:
  6. 11點在東新宿歌舞伎町 APA 飯店 checkout,直接前往大江戶溫泉(百合海鷗線「テレコムセンター」站) 放行李,他的門口有寄放行的置物櫃,一個400日幣,再前往台場逛。為何要先去寄放行李,因為在台場寄物 兩件1,600日幣(800×2),晚上去大江戶溫泉再付兩個櫃子800日幣(400×2),扣掉多坐兩站再折返回台場的交通費,少說也省下 1,000 日幣 (1,600-494[註1]=1,106)
[註1]:多坐兩站多62,テレコムセンター → 台場=185,兩個人乘2,算式: (62+185)*2=494

這樣解決住宿與去富士山的交通問題,小結一下 part1 的機票與這篇的住宿費,hotels.com 訂房集滿 10 晚送一晚,這一晚的價格可以扣 10 晚的均價,所以星期六 double 價格就壓下來,感覺像平日價一樣,這樣想心情愉快許多。這樣機+ 酒10,400+3,512+5,517=19,429,兩個人加總控制在二萬元內


東京5 Day 4 Night攻略,未完成,待續…

2016年5月25日 星期三

【Travel】東京-富士山行_[5天4夜]_廉航晚去早回攻略 PartⅠ

3 月時樂桃航空有4周年感恩回饋促銷,看起來真得很便宜。兩個人去回,掛一件行李,只要10,200 NT

3/3 號時立刻就訂了 5 月 的5 天 4 夜的東京行,但航班晚去早回,等於只有三天時間可以玩。但如何解決頭一天與最後一天的住宿問題,是首要的課題,這就是傳說中的紅眼班機。
  1. one night
  2. 去程的班機時間是晚上 8:45 到日本是凌晨 1 點了,一開始是看到有機場的膠囊旅館,3 月訂 5 月算是早鳥,早鳥優惠一個人也要 1500 台幣左右,兩個人加起了也要 3000 台幣,那不如住飯店,居住品質遠勝過限制諸多的膠囊旅館,所以放棄膠囊旅館開始搜尋羽田機場附近的飯店,當然要 24 小時櫃檯才能 check in 。坦白說,選擇性還真小,因為我要便宜、要離機場近,找到了 Hotel Mystays Haneda(羽田滿意飯店),3,500 台幣搞定,當然晚上已經沒有免費的接駁車,寫信去飯店問凌晨二點還接不接客,他還幫我解決了交通的疑惑,告知此時刻沒有接駁車,只能搭 taxi ,價錢是 1300 日幣,實際驗證後還真得是 一千三日幣分毫不差,讓小弟見證奇跡,好生感動。
    by the way, 我到羽田機場,看到一堆來睡機場, 為何不直接睡機場?
    如果你帶女朋友去,想活著回來,花點錢肯定值得 XD~

  1. last night
  2. 回程班機一大早5:55,等於4點多就要到機場,是可以玩到很晚去睡機場,但就太克難,整個就像去東京受苦受難,一點都不graceful。當然有為廉航而生的解決方案就是大江戶溫泉物語的「羽田空港 早朝便フライト前泊プラン」方案,只要3300日幣(入場加上幫你訂去機場的bus),可以在那休息,像我們是晚上在那用餐,泡湯,再到2F的休息室的沙發坐椅睡,3點50在到門口外等bus,3:50準時出發,20分鐘就到機場了。
東京5 Day 4 Night攻略,未完成,待續…

2016年5月24日 星期二

【Andriod】Google maps api key setup

  • Issue:
  • pub API key Caused by: java.lang.RuntimeException: API key not found. Check that <meta-data android:name="com.google.android.geo.API_KEY" android:value="your API key"> is in the <application> element of AndroidManifest.xml
  • Solution:
  • 使用 andriod project 申請 google map api ,需要三個步驟:
  1. 建立憑證來 Signed APK
  2. 在憑證取得 sha1 指紋
  3. google dev console 中開啟用 google map api ,貼上你的 project name 與步驟 1 的 sha1 指紋
說明:
  1. 建立憑證來 Signed APK
  2. 參考來源:developer.android.com
    可以手動指令或使用android studio ide來建立
    1. 方法一:手動輸入指令
    2. 
      keytool -genkey -v -keystore  -alias  -keyalg RSA -keysize 2048 -validity 10000
               
    3. 方法二:android studio IDE
    4. Build -> Generate Signed APK
  3. 在憑證取得 sha1 指紋
  4. 指令:keytool -list -v -keystore <key_store_path>
    快速完成:
    
    keytool -list -v -keystore <key_store_path> | grep SHA1 | awk -F"SHA1: " '{print $2}'
    
  5. google dev console 中開啟用 google map api ,貼上你的 project name 與步驟 1 的 sha1 指紋
  6. 進入google dev consoleGoogle Maps Android API啟用Google Maps Android API
    建立API 金鑰後,記下你的「API 金鑰」,並新增「套件名稱和指紋」
    輸入「套件名稱」與「SHA-1 憑證指紋」後,5分鐘內生效
最後進行部署android project 設定android project使用google map api key
  1. 編輯AndroidManifest.xml
  2. 
    <meta-data android:name="com.google.android.geo.API_KEY" android:value="@string/google_maps_key"></meta-data></key_store_path></key_alias></key_store_path></application></meta-data>
    
  3. 設定google_maps_key的value,編輯res/values/strings.xml
  4. 
    <string name="google_maps_key">YOUR_API_KEY</string>
    

2016年5月19日 星期四

【JAVA】LDAPS整合AD驗證

參考來源:ActiveDirectoryAuthentication
原作者是使用ldap,下面修改為ldaps驗證
為了安全考量,有一份堅持,
原作者是用nsLookup去取得AD的主機列表,
下面是先hardcode加上一台AD主機位址,並且加上憑證
當然憑證要匯到JDK的ca路徑
範例網域:contoso.com,當然要換成你要用的Domain


package com.uitox.shared.util;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

import javax.naming.CommunicationException;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.security.auth.login.AccountException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;

public class ActiveDirectoryAuthentication {
 private static final String CONTEXT_FACTORY_CLASS = "com.sun.jndi.ldap.LdapCtxFactory";

 private String ldapServerUrls[]={"ldap://fdc.contoso.com:636"};

 private int lastLdapUrlIndex;

 private final String domainName;

 public ActiveDirectoryAuthentication(String domainName) {
  this.domainName = domainName.toUpperCase();

  try {
//   ldapServerUrls = nsLookup(domainName);  
  } catch (Exception e) {
   e.printStackTrace();
  }
  lastLdapUrlIndex = 0;
 }

 public boolean authenticate(String username, String password)
   throws LoginException {
  if (ldapServerUrls == null || ldapServerUrls.length == 0) {
   throw new AccountException("Unable to find ldap servers");
  }
  if (username == null || password == null
    || username.trim().length() == 0
    || password.trim().length() == 0) {
   throw new FailedLoginException("Username or password is empty");
  }
  int retryCount = 0;
  int currentLdapUrlIndex = lastLdapUrlIndex;
  do {
   retryCount++;
   try {
    Hashtable env = new Hashtable();
    env.put(Context.INITIAL_CONTEXT_FACTORY, CONTEXT_FACTORY_CLASS);
    env.put(Context.PROVIDER_URL,
      ldapServerUrls[currentLdapUrlIndex]);
    env.put(Context.SECURITY_PROTOCOL, "ssl");

    System.setProperty("javax.net.ssl.trustStore","/jre/lib/security/cacerts");
    System.setProperty("javax.net.ssl.trustStorePassword","changeit");

    env.put(Context.SECURITY_PRINCIPAL, username + "@" + domainName);
    env.put(Context.SECURITY_CREDENTIALS, password);
    DirContext ctx = new InitialDirContext(env);
    ctx.close();
    lastLdapUrlIndex = currentLdapUrlIndex;
    return true;
   } catch (CommunicationException exp) {
    // TODO you can replace with log4j or slf4j API
    exp.printStackTrace();
    // if the exception of type communication we can assume the AD
    // is not reachable hence retry can be attempted with next
    // available AD
    if (retryCount < ldapServerUrls.length) {
     currentLdapUrlIndex++;
     if (currentLdapUrlIndex == ldapServerUrls.length) {
      currentLdapUrlIndex = 0;
     }
     continue;
    }
    return false;
   } catch (Throwable throwable) {
    throwable.printStackTrace();
    return false;
   }
  } while (true);
 }

 private static String[] nsLookup(String argDomain) throws Exception {
  try {
   Hashtable env = new Hashtable();
   env.put(Context.INITIAL_CONTEXT_FACTORY,
     "com.sun.jndi.dns.DnsContextFactory");
   env.put("java.naming.provider.url", "dns:");
   DirContext ctx = new InitialDirContext(env);
   Attributes attributes = ctx.getAttributes(
     String.format("_ldap._tcp.%s", argDomain),
     new String[] { "srv" });
   // try thrice to get the KDC servers before throwing error
   for (int i = 0; i < 3; i++) {
    Attribute a = attributes.get("srv");
    if (a != null) {
     List domainServers = new ArrayList();
     NamingEnumeration enumeration = a.getAll();
     while (enumeration.hasMoreElements()) {
      String srvAttr = (String) enumeration.next();
      // the value are in space separated 0) priority 1)
      // weight 2) port 3) server
      String values[] = srvAttr.toString().split(" ");
      domainServers.add(String.format("ldap://%s:%s",
        values[3], values[2]));
     }
     String domainServersArray[] = new String[domainServers
       .size()];
     domainServers.toArray(domainServersArray);
     return domainServersArray;
    }
   }
   throw new Exception("Unable to find srv attribute for the domain "
     + argDomain);
  } catch (NamingException exp) {
   throw new Exception("Error while performing nslookup. Root Cause: "
     + exp.getMessage(), exp)
  }
 }
}
主要改造說明
  1. ldapServerUrls直接加入主機 22行處改成,加上DC主機,ex:ldap://fdc.contoso.com:636
    
     private String ldapServerUrls[]={"ldap://fdc.contoso.com:636"};
    
    註解掉原nsLookup查表,因為我hardcode了 XD
  2. 指定憑證CA檔 先將企業內部root CA匯入,至/jre/lib/security/cacerts
    然後指定CA檔,讓java知道就行了
    
        System.setProperty("javax.net.ssl.trustStore","<JDK ROOT>/jre/lib/security/cacerts");
        System.setProperty("javax.net.ssl.trustStorePassword","changeit");
    
最後,在寫一個驗證的Class,作登錄就大功告成。

public class UserCredentialManager {
        ...
 public synchronized void login(String name, String password){
  boolean IsAuth = false;
  ActiveDirectoryAuthentication ADAuth = new ActiveDirectoryAuthentication("CONTOSO.COM");
  try {
   IsAuth = ADAuth.authenticate(name, password);
  } catch (LoginException e) {
   e.printStackTrace();
  }
  
  if(IsAuth == true){
   user = name;
  }
  
 }
        ...
}