2007/10/16

Aachi And Ssipak 阿阿奇與西西帕克

這是一部韓國的動畫電影,畫面的處理加上科幻故事的背景,我覺得這是一部值得看看的作品。在滿地都是日本動畫的時代,看看韓國人的動畫也不錯。很明顯地,韓國在電玩工業的帶動下,內容與影視娛樂產業也成功地打出國際市場。故事的想法,跟「能源危機」有關,也帶有「美麗新世界」的影子。畫面精緻這不用多說,大家自己去看看就知道了。

故事的背景,在故事一開始就陳述出來:世界上所有形態的能源都用完了,人們通過糞便提煉新的能源,建造了一個新的城市,不久後城市的領導人,宣佈了兩項法規,以生產和控制新能源。首先在每個市民的肛門,安裝ID芯片,以監控他們的排便程度,其次,給排便的市民一條吃了會上癮的"果汁棒"作為回報,不久後糞便的數量就急速上升。

但是由於果汁棒,城裡到處都是癮君子,非法販賣果汁棒變得很普遍,而果汁棒的副作用,使得啞巴小個子變種人出現,這些變種人不能拉屎,也因為小弟弟縮得跟花生那樣不能生孩子,所以他們就被趕出那個城市,這些變種人組織了搶劫果汁棒的幫會,最後成為我們所知的"尿布幫"。

故事設定很合理地,讓城市的政府軍,依靠怪博士的幫忙,製造出一個變種人殺手「狗鼻子」,他存在的唯一目的,就是要把尿布幫趕盡殺絕。尿布幫的老大,因為跟政府軍搶奪果汁棒遇到狗鼻子而失敗,只好想辦法,到第四區準備從黑幫的手裡搶奪他們地下交易的貨源。

尿布幫的老大在黑幫的地盤遇到了吉米,他提供了一個驚人的主意,要是把一堆人的ID晶片塞到一個很會拉屎的人屁股裡,那麼只要拉了一次,就會一次拿到上百個果汁棒。他們找上了「美麗」,阿阿奇與西西帕克這兩個阿飛,救出「美麗」後,發現這個秘密,因此賺了不少錢。

尿布幫的老大跟政府軍,都想趕快找到「美麗」,而發生了追逐戰,「美麗」被尿布幫抓走了,他們又找上了怪博士幫忙製造軍隊,於是又是一場大混戰,最後是阿阿奇與西西帕克跟著「美麗」一起富有地浪跡天涯。

整個故事情節雖然荒誕,但是從全球「能源危機」所開始的想法,跟「駭客任務」裡所設定的,以人體來當作能源的想法有一點像,糞便供應能源的城市自給自足,就像是一個「美麗新世界」,城市的主人為了控制能源的來源,保障能源不會斷絕,就創造了類似「美麗新世界」中使用的「蘇麻」,來麻醉居民的神經。

為了抗拒城市的監控,產生了對抗的勢力"尿布幫",三方的勢力:政府軍、黑幫、尿布幫,中間夾著主角阿阿奇、西西帕克與美麗,他們之間的關係,就構成了這部電影,表面上看來,政府軍、尿布幫是壞人,但其實是沒有絕對的對錯。

政府軍為了居民的安危,奮力對抗反抗勢力尿布幫,為了保障能源供應,以ID晶片與果汁棒控制居民的行動,看起來有立意良善的初衷,但卻使用了卑劣的手段。尿布幫為了爭取自己的權利,致力搶奪果汁棒,成為這個城市的毒瘤,但他們也不願意這樣,完全是因為果汁棒產生的副作用,他們有不得不做的理由,但是選擇了使用最激烈的反抗手段。

尿布幫的成員似乎是殺也殺不完,而且全部的人,似乎只有老大一個人有腦袋,其他都是「我為人人、人人為我」的好榜樣,爭先恐後地,要為組織效力,尿布幫是個最具有向心力的組織。

「美麗」被塞了多個ID晶片,是故事中段的一個小高潮,這的確是對付政府軍的一個好方法,至於究竟合不合理,為甚麼「美麗」跑了,尿布幫老大不再另外重新抓個新的屎人,反而執意要找美麗,這麼厲害的「狗鼻子」為甚麼會只有一隻,不像怪博士最後幫忙尿布幫做的怪物,可以有那麼多隻,這點缺陷的小細節就不重要了。

韓國動畫 - Aachi And Ssipak
Aachi & Ssipak
Aachi And Ssipak『阿阿奇與西西帕克』

2007/10/15

惡童 Tekkonkinkreet

改編自「松本大洋」的漫畫,二宮和也、蒼井優聲演,Michael Arias導演。

惡童 (Tekkonkinkreet)這篇文章用了幾個字來描述「惡童」這部令人難以理解的作品:黑與白、朝與夜、善與惡,被稱為「貓」的「黑」和「白」是兩個住在「地獄之街」寶町的小孩,他們兩個在故事一開始,就好好地教訓了「朝」與「夜」,這個象徵比喻,或許是要說明,「黑」和「白」在橫行在白天與夜晚的寶町,無人不知無人不曉,所有人都明白,在寶町就得好好地注意「黑」這個不定時炸彈。

做了十多年黑幫的木村,因為成家立室而要過新的人生,為了保護家人,他只能遵照老大「蛇」的指示,解決掉他的老爹「耗子」鈴木桑,黑幫與「蛇」的「兒童城」計畫,一步一步地改變了寶町。大家都因為這種改變,而有不同的適應方式。「蛇」帶著數個講「鳥語」的怪人幫他做事,主要是要解決掉「貓」,也就是「黑」,「黑」為了生存下去,選擇跟「白」分開,讓他接受警察的保護,自己一個人嘗試要解決這個難題。

「黑」和「白」用完全不同的人生態度,面對改變中的寶町,成天做夢的「白」,沒有被外界影響自己的信念,嗜血的「黑」則是為了生存及目標而改變自己的理念,在光與暗之間彷徨,引出了另一個「黑」,也就是完全黑暗的「鼬」。但是小白與小黑是一個互相補足的平衡,互相補完兩者心理上的不足,亦即是小白所說的「我擁有小黑所欠缺的螺絲,他擁有我欠缺的螺絲」。很幸運地,「黑」擺脫了「鼬」的糾纏,又回到一起生活互相幫助,那麼純淨,衝突與平衡的「黑」與「白」。

2007/10/8

秒速5公分 - 新海誠

對新海誠認識的上一步作品是「雲之彼端,約定的地方」,「秒速5公分」同樣也是一部音樂與唯美畫面完美結合的動畫電影。

多數的評價,是認為故事平庸無奇,但畫面與音樂的處理,讓這部動畫依然能站穩一席之地,的確如此,我看到了一個莫名其妙的憂鬱少年,慢慢地長大後,還是一樣憂鬱,姑且不論這樣子從頭到尾的憂鬱合不合理,但這樣的少年,確實也讓我想起自己有些時候,那種莫名其妙的陰沉感覺。

有些時候,確實就會發覺自己一直沒來由地心情低落,很像是一般人所說得低潮期,做什麼事都提不起勁,只有憂鬱再憂鬱。

三個段落的故事,分別是櫻花抄、太空人與秒速5公分,第一個段落,描述憂鬱少年遠野貴樹,跟青梅竹馬篠原明裡的分離,後來約定見面的過程,暴風雪似乎讓他們的距離隨著時間越拉越遠。第二段落,遠野貴樹沒有收件者的短訊習慣,明顯說明了,空間的距離讓他與明裡漸行漸遠,沒有聯絡了,而同學澄田花苗暗戀的過程,這個憂鬱少年一點也沒察覺,到了火箭發射的那一瞬間,澄田花苗終於明白,貴樹的眼光,一直落在遙遠的那一方。

第三個段落,明裡已經結婚了,貴樹與明裡已經是完全不再相交的兩條線,永遠存放在心理的某一個角落,交往了三年的水野,即使還想跟貴樹貼近,這個憂鬱少年還停留在對明裡的思念中,直到最後山崎正義的這首情歌「One more time One more chance」。


秒速5釐米
秒速5公分 - Wiki
山崎正義-One more time,One more chance
新海誠

2007/10/5

Using ldap api to connect MS Active Directory

當系統需要透過外部的帳號倉庫做帳號認證時,我們以Spring外接一段程式,處理系統登入與修改密碼的程序,連接外部的LDAP或AD。

當我遇到MS AD時,我必須要先把他安裝起來,這是我遇到的第一個困難,我參考了JNDI AD 備忘記的程序,先把MS Windows 2003標準版安裝在VM上,然後設定了AD,接下來設定了IIS,並安裝CA,把SSL搞定跟key store搞定,其中我以ldap browser測試過,可以連接AD成功,瀏覽整個AD Tree的資料。

後來就參考了java如何結合(呼叫)Win2000 ActiveDirectory,做出了AD認證的程式,這個認證有點奇怪,因為只要是合法的帳號,就可以瀏覽所有的使用者節點資料。

接下來就是修改密碼,參考如何使用 JNDI change win 2000 AD user password,可以處理修改密碼的程序,但前提是MS IIS export的key必須要先import到JVM的cacerts這個key store裡面,得用SSL才能運作。

搜尋所有使用者帳號的查詢,就是透過ldap的searchFilter功能,針對Users,把一些非一般使用者帳號的條件放進去,將不合用的帳號過濾掉,拉出資料的查詢結果,就完成了(查詢結果回傳的ADUser物件,只是一個包含了id與name欄位的data bean物件)。

安裝是最麻煩的地方......。

public class ADClient {
private static Logger logger = Logger.getLogger(ADClient.class.getName());

public static boolean checkAuth(String ldapserver, String userdn, String password)
throws NamingException {
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
// env.put(Context.PROVIDER_URL, "ldap://192.168.2.200");
env.put(Context.PROVIDER_URL, ldapserver);
env.put(Context.SECURITY_AUTHENTICATION, "Simple");
// env.put(Context.SECURITY_PRINCIPAL, "CN=" + uid + ",CN=Users,DC=msdiv,DC=com,DC=tw");
env.put(Context.SECURITY_PRINCIPAL, userdn);
env.put(Context.SECURITY_CREDENTIALS, password);

DirContext ctx = null;
boolean bLogin = false;
try {
ctx = new InitialDirContext(env);
bLogin = true;
} catch (javax.naming.AuthenticationException authe) {
logger.error("Error: ", authe);
bLogin = false;
} finally {
if (ctx != null) {
try {
ctx.close();
} catch (Exception Ignore) {
}
}
}
return bLogin;
}

public static void changePwd(String ldapserver, String admindn, String adminpassword,
String userdn, String newPassword, String keystorePath, String keystorePassword)
throws NamingException, UnsupportedEncodingException {
Hashtable env = new Hashtable();
// String admindn = "cn=administrator,CN=Users,DC=msdiv,DC=com,DC=tw";
// String adminpassword = "adtest1111";
// String userdn = "CN=test,CN=Users,DC=msdiv,DC=com,DC=tw";
// String newPassword = "test2222";

// System.out.println("java.home=" + System.getProperty("java.home"));
// String keystore = System.getProperty("java.home") + "\\lib\\security\\cacerts";
// String keystore = "C:\\Program Files\\Java\\jdk1.6.0\\jre\\lib\\security\\cacerts";

System.setProperty("javax.net.ssl.trustStore", keystorePath);
System.setProperty("javax.net.ssl.trustStorePassword", keystorePassword);
System.setProperty("UseSunHttpHandler", "true");

env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, admindn);
env.put(Context.SECURITY_CREDENTIALS, adminpassword);
env.put(Context.SECURITY_PROTOCOL, "ssl");
// String ldapURL = "ldaps://192.168.2.200";
env.put(Context.PROVIDER_URL, ldapserver);

LdapContext ctx = null;
try {
logger.info("AD is trying to reset Password for: " + userdn);
ctx = new InitialLdapContext(env, null);
String newQuotedPassword = "\"" + newPassword + "\"";
byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE");
ModificationItem[] mods = new ModificationItem[1];
mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute(
"unicodePwd", newUnicodePassword));
ctx.modifyAttributes(userdn, mods);
logger.info("AD Reset Password for: " + userdn + " ok.");
} finally {
if (ctx != null) {
if (ctx != null) {
try {
ctx.close();
} catch (Exception Ignore) {
}
}
}
}

}

public static ArrayList queryAD(String ldapserver, String admindn,
String adminpassword, int searchScope, String searchBaseDn, String searchFilter) throws NamingException {
ArrayList adusers=new ArrayList();

Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, ldapserver);
env.put(Context.SECURITY_AUTHENTICATION, "Simple");
// env.put(Context.SECURITY_PRINCIPAL, "CN=Administrator,CN=Users,DC=msdiv,DC=com,DC=tw");
// env.put(Context.SECURITY_CREDENTIALS, "adtest1111");
env.put(Context.SECURITY_PRINCIPAL, admindn);
env.put(Context.SECURITY_CREDENTIALS, adminpassword);

DirContext ctx = null;
boolean bLogin = false;
try {
ctx = new InitialDirContext(env);
bLogin = true;

// Create the search controls
SearchControls searchCtls = new SearchControls();

// Specify the search scope
// searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
// searchCtls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
searchCtls.setSearchScope(searchScope);

// specify the LDAP search filter
// String searchFilter = "(& (objectClass=user) (!(adminCount=*))
// (!(isCriticalSystemObject=*)) (userPrincipalName=*) )";

// Specify the Base for the search
// String searchBaseDn = "CN=Users,DC=msdiv,DC=com,DC=tw";

// initialize counter to total the group members
int totalResults = 0;

// Specify the attributes to return
// String returnedAtts[] = { "sAMAccountName", "givenName", "mail" };
String returnedAtts[] = { "sAMAccountName", "name", "userPrincipalName" };
searchCtls.setReturningAttributes(returnedAtts);

// Search for objects using the filter
NamingEnumeration answer = ctx.search(searchBaseDn, searchFilter, searchCtls);

// Loop through the search results

String id = "";
String name = "";
while (answer.hasMoreElements()) {
totalResults++;
SearchResult sr = (SearchResult) answer.next();

logger.info(totalResults + ">" + sr.getName());

// Print out the groups

Attributes attrs = sr.getAttributes();
if (attrs != null) {

try {
for (NamingEnumeration ae = attrs.getAll(); ae.hasMore();) {
Attribute attr = (Attribute) ae.next();
logger.debug(" Attribute: " + attr.getID());
if (attr.getID().equals("sAMAccountName")) {
for (NamingEnumeration e = attr.getAll(); e.hasMore();) {
id = (String) e.next();
logger.debug(" value: " + id);
}
} else if (attr.getID().equals("name")) {
for (NamingEnumeration e = attr.getAll(); e.hasMore();) {
name = (String) e.next();
logger.debug(" value: " + name);
}
} else {
for (NamingEnumeration e = attr.getAll(); e.hasMore();) {
logger.debug(" value: " + e.next());
}
}
}
} catch (NamingException e) {
logger.error("Can not retrieve data from AD!");
logger.error("Error: ", e);
throw e;
}
}

if( !id.equals("")) {
adusers.add(new ADUser(id, name));
}
}
logger.info("Total results: " + totalResults);
} finally {
if (ctx != null) {
try {
ctx.close();
} catch (Exception Ignore) {
}
}
}
return adusers;
}
}