2010年8月16日 星期一

[Struts2]-OGNL存取ActionContext內非Value Stack內之物件

參考前一篇[Struts2]-實作Interceptor
result頁面可以利用OGNL語法從ActionContext的ValueStack中取得欄位物件
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>

<html>
<head>
<title>Hello World</title>
</head>
<body>
<h2><s:property value="message"/></h2>
</body>
</html>
此時property tag中的value屬性就是利用OGNL語法從hello action中取得message field。
因為當action處理完後欄位資料後會將欄位物件置於ActionContext的ValueStack中,
此時的前端頁面就是利用OGNL從ValueStack中取出相對應的欄位值。

在ActionContext中,除了ValueStack內的物件外還有其它的scope物件可以存取,
如下圖:


 名称作用例子
parameters包含當前HTTP請求參數的Map#parameters.id[0]作用相當於request.getParameter("id")
request包含當前HttpServletRequest的屬性(attribute)的Map#request.userName相當於request.getAttribute("object name")
session包含當前HttpSession的屬性(attribute)的Map#session.userName相當於session.getAttribute("object name")
application包含當前應用的ServletContext的屬性(attribute)的Map#application.userName相當於application.getAttribute("object name")
attr用於按request > session > application順序訪問其屬性(attribute)#attr.userName相當於按顺序在以上三個範圍(scope)内讀取object name屬性,直到找到為止

接下來我們實際編寫一個action(OgnlAction.java)來存取這些scope物件。
package example;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.StrutsStatics;

public class OgnlAction extends ActionSupport {

  private ActionContext actionContext;
  private Map application;
  private Map session;
  private Map parameters;
  private HttpServletRequest request;
  private HttpServletResponse response;

  @Override
  public String execute() {
    actionContext = ActionContext.getContext();
    application = actionContext.getApplication();
    session = actionContext.getSession();
    parameters = actionContext.getParameters();

    request = (HttpServletRequest) actionContext.get(StrutsStatics.HTTP_REQUEST);
    //private HttpServletRequest request = ServletActionContext.getRequest();
    response = (HttpServletResponse) actionContext.get(StrutsStatics.HTTP_RESPONSE);

    application.put("message", "message from application");
    session.put("message", "message from session");
    parameters.put("message", "message from parameters");
    request.setAttribute("message", "message from request");
    return SUCCESS;
  }
}

我們可以利用ActionContext類別來取得這些scope物件,
另外也能使用ServletActionContext這個輔助類別來取得
HttpServletRequest、HttpServletResponse 。
這裡要注意的是,ActionContext物件必須要在execute method內取得,
如果在method外初始的話,此時內部的application等scope物件都還是null。

接下來編寫一個Ognl.jsp來當做前端頁面。
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Struts OGNL Demo</title>
</head>
<body>
<h3>訪問ActionContext物件</h3>
<p>parameters: <s:property value="#parameters.message" /></p>
<p>request.message: <s:property value="#request.message" /></p>
<p>session.message: <s:property value="#session.message" /></p>
<p>application.message: <s:property value="#application.message" /></p>
<p>attr.message: <s:property value="#attr.message" /></p>
<hr/>
</body>
</html>
這裡可以發現,OGNL預設就是取得ValueStack內的物件,如果要取得其它的scope物件,
前面則要加上#符號。

最後是struts.xml的設定
<struts>
<package name="example" namespace="/example" extends="struts-default">
<action name="Ognl" class="example.OgnlAction">
<result name="success">/example/Ognl.jsp</result>
</action>
</package>
</struts>
輸入連結後/example/Ognl.actiont可以看到下面的結果:
除了parameters外其餘的結果都如我們預料,
筆者猜測也許parameters是取得當前http request的參值,
無法事後更動。

當我們直接在請求頁面的連結後加入參數時,就正常了。

2010年8月15日 星期日

[Struts2]-實作Interceptor

首先先來了解struts2 framework的運作機制
要部署struts2 framework我們必須在web.xml中加入這段
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
當 FilterDispatcher 根據 action name 所對應到 struts.xml 中的 Action class 之後,
Struts2 核心會先呼叫 Interceptor Stack 中的所有 interceptors。
接著再呼叫 Action class 中的 execute() method,完成後再呼叫 Result 幫助我們排版,
這過程中都會使用到 OGNL 到 ValueStack 中幫助我們取得必要的資料。

Interceptor Stack
這裡我們先將重點擺在Interceptor。Interceptor Stack 是在整個流程中第一個被呼叫的,
也是最後一個被呼叫的。Interceptor Stack 中包含一集合的 interceptors,
而 interceptors 的排列方式是以 stack 的方式排列。

下面我們先寫一個簡單無牽扯自訂interceptor的範例。
首先我們先新增一個Login.jsp頁面。
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>login</title>
</head>
<body>
<s:form action="/example/Hello" method="post">
<s:textfield name="user_name" label="User Name:"/>
<s:submit/>
</s:form>
</body>
</html>
這個頁面會呼叫hello action,所以我們接下來編寫一個HelloWorld.java來處理這個呼叫。
package example;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.StrutsStatics;

public class HelloWorld extends ActionSupport {
  private String user_name;
  private String message;

  @Override
  public String execute() throws Exception {
    message = "Hello:" + user_name;
    return SUCCESS;
  }

  public String getUser_name() {
    return user_name;
  }

  public void setUser_name(String user_name) {
    this.user_name = user_name;
  }

  public String getMessage() {
    return message;
  }

  public void setMessage(String message) {
    this.message = message;
  }
}
hello action被呼叫時會自動執行excute這個method,當處理完後會藉由return "String"告訴
framework要將結果導向那個result頁面。

接下來我們再編寫一個HellowWorld.jsp來作為輸出頁面。
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>

<html>
<head>
<title>Hello World</title>
</head>
<body>
<h2><s:property value="message"/></h2>
</body>
</html>

至於如何將Login.jsp與HelloWorld.java做連結以及利用result標韱來控制輸出頁面呢?
則是在struts.xml中設定。
<struts>
<package name="example" namespace="/example" extends="struts-default">
<action name="Hello" class="example.HelloWorld">
<result name="success">/example/HelloWorld.jsp</result>
<result name="login">/example/LOGIN.jsp</result>
</action>
</package>
</struts>
上述例子表示,
當action return "success"時會導向HelloWorld.jsp,
當return "login"時會導向Login.jsp。
這裡有兩個需要注意的地方:
1.在jsp頁面中呼叫action時,必須加入其package的namespace為prefix,
    如action="/example/Hello";
2.result的name屬性值是有區分大小寫的。


下面為這個範例的執行結果:
輸入名稱後
會出現歡迎頁面

接下來進入這篇的重點,我們要在這個流程中加入自訂的interceptor,
編寫一個AuthorizationInterceptor.java,
自訂的interceptor須繼承AbstractInterceptor介面。
package example;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

public class AuthorizationInterceptor extends AbstractInterceptor {
  @Override
  public String intercept(ActionInvocation ai) throws Exception {
    return ai.invoke();
  }
}
當interceptor發生作用時會呼叫intercept這個method。
而利用ActionInvocation的getInvocationContext()method,
我們可以得到儲存全文流程資料的ActionContext物件,這時就可以自由發揮了。

回頭看第一張framework流程圖,interceptor可以invoke原本request呼叫的action,
也可以直接return result頁面,
在這個例子中我們在interceptor中不做任何事,直接invoke action。

要讓自訂的interceptor發揮作用還需要在struts.xml中加以指明。
<struts>
<package name="example" namespace="/example" extends="struts-default">
<interceptors>
<interceptor name ="auth" class ="example.AuthorizationInterceptor"/>
</interceptors>
<action name="Hello" class="example.HelloWorld">
<interceptor-ref name ="auth"/>
<result name="success">/example/HelloWorld.jsp</result>
<result name="login">/example/LOGIN.jsp</result>
</action>
</package>
</struts>
表示呼叫hello action時必須先經過auth這個interceptor。

我們再一次的執行這個範例,確發現了意外的結果。
Login.jsp所輸入的使用者名稱無法帶到所呼叫的hello action,導致HelloWorld.jsp
頁面輸出時user_name欄位值為null。
這個原因是因為當我們自訂的interceptor呼叫invoke method時,整個flow會回到
framework的開頭階段,此時hello action並非由Login.jsp直接呼叫,
導致Login.jsp中的textfield欄位自動調用HellowWorld.java中的setUser_name()的機制失效。

此時hello action(HelloWorld.java)就必須自行由request中取出Login.jsp傳來的parameter。
將HellowWorld.java的execute method內容改寫如下:
public String execute() throws Exception {
ActionContext actionContext = ActionContext.getContext();
HttpServletRequest request= 
(HttpServletRequest) actionContext.get(StrutsStatics.HTTP_REQUEST);
message = "Hello:" + request.getParameter("user_name");
return SUCCESS;
}
接著再一次執行修改過後的範例,就又可以看到正確的輸出結果了。

2010年8月5日 星期四

[AJAX]-動態清單

這裡要記錄如何用Ajax來達到動態清單的功能
Ajax 是 Asynchronous JavaScript  and XML 的簡稱,這指出了 Ajax 的核心觀念
(Asynchronous)與所使用到的主要兩個技術(JavaScript、XML)。
要達到 Asynchronous 利用的就是 JavaScript 中的XMLHttpRequest 物件。

XMLHttpRequest:
在 JavaScript 中利用new XMLHttpRequest()語法建立。
包含下列幾個標準屬性-

onreadystatechange 
參考至callback函式,readyState每次改變時,都會呼叫onreadystatechange所參考的函式。

readyState 
會有0到4的數值,分別表示不同的請求狀態:
 0 = 未初始化的連線(uninitialized),還沒呼叫open()
 1 = 載入中(loading),呼叫open(),還沒呼叫send()
 2 = 已載入(loaded),呼叫send(),請求header/status準備好
 3 = 互動中(interactive),正在與伺服器互動中
 4 = 請求完成(completed),完成請求

responseText 
伺服器傳來的請求回應文字,會設定給這個屬性。

responseXML
伺服器傳來的請求回應如果是XML,會成為DOM設定給這個屬性。

status 
伺服器回應的狀態碼,例如200是OK,404為Not Found…

statusText 
伺服器回應的狀態文字。
接下來我們實際寫個web頁面來發送XMLHttpRequest
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>動態載入清單</title>
<script type="text/javascript" src=dynamicList.js></script>
</head>
<body>
語言…
<br/>
<select id="langs" onchange="refreshList();">
<option value="func">功能說明</option>
<option value="java">Java</option>
<option value="c#">C#</option>
</select>
<br/><br/>
相關技術…
<br/>
<select id="techs" size="6" style="width:300px;"/>
</body>
</html>
主要功能是選擇 id 為 langs 的 select html 元件時,會利用 Ajax 去後端 server 取得
相關資料,非同步的更動 id 為 techs 的 select html元件。
另外主要功能的JavaScript是利用
<script type="text/javascript" src=dynamicList.js></script>來引入。
var xmlHttp;

window.onload = refreshList;

function createXMLHttpRequest() {
  if(window.XMLHttpRequest) {
    xmlHttp = new XMLHttpRequest();
  }
  else if(window.ActiveXObject) {
    xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
  }
}

function prepareXML() {
  var xml = "<langs>";
  var lang = document.getElementById("langs").value;
  xml = xml + "<lang>" + lang + "<\/lang>";
  xml = xml + "<\/langs>";
  return xml;
}

function refreshList() {
  var xml = prepareXML();
  var url = "refreshservlet";

  createXMLHttpRequest();
  xmlHttp.onreadystatechange = handleStateChange;
  xmlHttp.open("POST", url);
  xmlHttp.setRequestHeader("Content-Type", "text/xml");
  xmlHttp.send(xml);  
}

function handleStateChange() {
  if(xmlHttp.readyState == 4) {
    if(xmlHttp.status == 200) {
      clearList();
      updateList();
    }
  }
}

// 清除上一次的顯示結果
function clearList() { 
  var techs = document.getElementById("techs");
  while(techs.childNodes.length > 0) {
    techs.removeChild(techs.childNodes[0]);
  }
}

// 以回應更新資料
function updateList() {
  var results = xmlHttp.responseXML.getElementsByTagName("tech");
  var techs = document.getElementById("techs");

  var option = null;
  for(var i = 0; i < results.length; i++) {
    option = document.createElement("option");
    option.appendChild(document.createTextNode(results[i].firstChild.nodeValue));
    techs.appendChild(option);
  }
}
由上面我們可以看到將XMLHttpRequest的onreadystatechange屬性
註冊到handleStateChange method,並利用post機制傳送XML格式的資料。
而所謂的XML格式資料其實就是符合XML規格的字串資料。
因為非同步的資料皆由XMLHttpRequest所控制,所以server回傳資料
也是由 XMLHttpRequest 的 responseXML 屬性取得。

接下來我們用一個servlet來接收並回傳由client端發送來的資料。
public class RefreshServlet extends HttpServlet {
  private static Map inMemoryDB = new HashMap();

  public void init() throws ServletException {
    inMemoryDB.put("func", new String[] { "非同步", "動態清單" });
    inMemoryDB.put("java", 
    new String[] { "JSP", "J2EE", "Spring", "Hibernate" });
    inMemoryDB.put("c#", new String[] { "ASP.Net", "ADO.Net" });
  }

  protected void doPost(HttpServletRequest request, 
    HttpServletResponse response) throws ServletException, IOException {
    String xml = readXMLFromRequestBody(request);
    Document xmlDoc = null;

    try {
      DocumentBuilder builder = 
      DocumentBuilderFactory.newInstance().newDocumentBuilder();
      xmlDoc = builder.parse(new ByteArrayInputStream(xml.getBytes()));
    } catch (ParserConfigurationException e) {
      System.out.println(e);
    } catch (SAXException e) {
      System.out.println(e);
    }

    String responseXML = prepareXMLResponse(xmlDoc);

    response.setContentType("text/xml;charset=utf-8");
    response.getWriter().print(responseXML);
    response.getWriter().close();
  }

  private String readXMLFromRequestBody(HttpServletRequest request) {
    StringBuffer xml = new StringBuffer();

    try {
      BufferedReader reader = request.getReader();
      String line = null;
      while ((line = reader.readLine()) != null) {
        xml.append(line);
      }
    } catch (Exception e) {
      System.out.println("XML讀取有誤…" + e.toString());
    }
    return xml.toString();
  }

  private String prepareXMLResponse(Document xmlDoc) {
    NodeList langNodes = xmlDoc.getElementsByTagName("langs");
    String lang = langNodes.item(0).getFirstChild().getTextContent();

    StringBuffer xml = new StringBuffer();
    xml.append("<techs>");
    String[] techs = (String[])inMemoryDB.get(lang);
    for (int i = 0; i < techs.length; i++) {
      xml.append("<tech>");
      xml.append(techs[i]);
      xml.append("</tech>");
    }
    xml.append("</techs>");
    return xml.toString();
  }
}

因為XML也是字串資料,所以我們先用reader讀入
BufferedReader reader = request.getReader();
String line = null; 
while ((line = reader.readLine()) != null) {
     xml.append(line);
 }
接著利用 DocumentBuilder 來解析每個tag的資料
DocumentBuilder builder =
     DocumentBuilderFactory.newInstance().newDocumentBuilder(); 

xmlDoc = builder.parse(new ByteArrayInputStream(xml.getBytes()));

最後也是利用 StringBuffer 將查詢到的資料組成XML格式的字串資料回傳。

2010年7月28日 星期三

[PERL]-Gtk2Perl on Windows

最近有個案子是用Perl開發Gtk
因為手頭上沒有Unix like環境
所以必須在windows上架構Gtk2Perl的環境

首先要先安裝ActivePerl這樣才能在windows上編譯及執行Perl
下載位置:ActivePerl
注意,這裡建議安裝ActivePerl 5.8.9.827版本比較穩定,
更新的版本在install ppm上會有些不可預期的問題。


再來利用ppm指令載入Gtk相關的模組
ppm install http://gtk2-perl.sourceforge.net/win32/ppm/ExtUtils-Depends.ppd
ppm install http://gtk2-perl.sourceforge.net/win32/ppm/ExtUtils-PkgConfig.ppd
ppm install http://gtk2-perl.sourceforge.net/win32/ppm/Glib-1080.ppd
ppm install http://gtk2-perl.sourceforge.net/win32/ppm/Gtk2-1080.ppd

ppm install http://gtk2-perl.sourceforge.net/win32/ppm/Gtk2-GladeXML-1.005.ppd

接著要安裝GTK+ Development Enviroment
下載位置:Glade/Gtk+ for Windows
這個安裝檔包含glade3,一個Gtk所見即所得的工具

整個Gtk2Perl的開發環境在此已建構完成
接下來實際拉個畫面來試試看
打開glade3拖拉個畫面如下,並另存新檔檔名為GUI.glade:

接著建個文字檔Gtk2Perl.pl,內容如下
use Glib qw/TRUE FALSE/;
use Gtk2 '-init';
use Gtk2::GladeXML;

my $gladexml = Gtk2::GladeXML->new("GUI.glade");
$gladexml->signal_autoconnect_from_package('main');

my $window = $gladexml->get_widget('window1');
$window->signal_connect( 'delete_event' => sub{Gtk2->main_quit();});
$window->show_all();

Gtk2->main();

由上面的程式碼可知我們就是用Gtk2::GladeXML這個模組來載入glade編寫出來的.glade檔,
接著直接點擊Gtk2Perl.pl就可以看到執行畫面:

注意,如果執行期發生如下的error
Can't locate object method "signal_connect" via package "Gtk2::Window"
請找出Gtk2.pm檔案,在開頭加上push @Gtk2::Object::ISA, 'Glib::Object';這行即可

最後簡單介紹兩個元件的使用心得
Gtk2::ComboBox,下面的程式碼(非完整)
利用Gtk2::ListStore來做為combobox model的資料來源,
使用Gtk2::CellRendererText來呈現Combobox的欄位,
$FFComboBox->add_attribute ($fy_renderer, "text", ID_COLUMN);
表示render由model的ID_COLUMN欄位取得"text"文字資料
$fy_model = Gtk2::ListStore->new('Glib::String');
foreach (reverse(keys(%fy_all))) {
$fy_model->set($fy_model->append, ID_COLUMN, $_);
}
$FFComboBox->clear();
$FFComboBox->set_model($fy_model);
$fy_renderer = Gtk2::CellRendererText->new;
$FFComboBox->pack_start ($fy_renderer, false);
$FFComboBox->add_attribute ($fy_renderer, "text", ID_COLUMN);
if ($FFComboBox->get_active() == -1) {
$FFComboBox->set_active(0);
}


Gtk2::Dialog,下面是說明一般是如何接受Dialog(與繼承Dialog的模組)的回傳動作,
$config_dialog->set_response_sensitive (0, FALSE);
是說將0註冊為這個dialog的response_id,
然後$config_dialog->run()時會等待該dialog有所動作,並回傳該動作的response_id,
至於dialog的動作就是由button元件觸發,所以button元件會有一個response_id屬性,
用來註明該button動作時會回傳的response_id值,
最後就是利用if (0 eq $config_dialog->run())來判斷按下的是dialog中的那一個按鈕
my $config_dialog = shift @_;
my $text_view = shift @_;

$config_dialog->set_response_sensitive (0, FALSE);
$config_dialog->show();
if (0 eq $config_dialog->run()){
$config_dialog->hide();
}

參考文件:

2010年7月22日 星期四

GTK+ with Dev-C++ on Windows

前言:
        說到C/C++開發環境,就不能不提到GCC。GCC是GNU Compiler Collection (GNU編譯器總集)的縮寫,為GNU計畫中一套多種程式語言編譯器的集合,在諸多Unix-like與Mac OS X中
都成為其內建的程式開發環境。

        其實,GCC最初的名稱為GNU C Compiler (GNU C 語言編譯器)。在當時,GCC還只是一個專門處理 C 語言的編譯器。而在後來 GCC 擴展之後,慢慢的也可以處理C++、Fortran、
Ada、Java、Objective-C 等語言。發展至今,就是現在我們所看到的編譯器總集了。

        雖然在Unix-like與Mac OS X系統中都已經內建了GCC的環境,但是若要在Windows系統下擁有相同的環境,你可能就需要安裝MinGW了。

        MinGW(Minimalist GNU for Windows),又稱Mingw32,為包含了GCC、GDB(GNU Debugger)、binutils等工具的GNU工具組(toolchain)移植到windows平台上的版本。包括一系列表頭檔(Win32API)、函式庫和可執行檔案。 MinGW是從Cygwin(1.3.3版)基礎上發展而來,但是用MinGW開發的程式不需要額外的第三方DLL支援就可以直接在Windows下執行,而且也不一定必須遵從GPL許可證。

進入正題:
下載Dev-C++ 並安裝
Bloodshed Dev-C++ 4.9.9.2 with MinGW for Windows
下載GTK+ Development Enviroment 並安裝
GTK+ Development Enviroment 2.12.9-2 for Windows

接下來檢查GTK+ Path 是否有正確設定於Dev-C++

執行Dev-C++
點選工具列的「Tools -> Compiler Options」,開啟Compiler Options視窗,
點選「Directories」頁籤,點選「Libraries」頁籤,檢查是否含有以下路徑:C:\GTK\LIB

點選「C Includes」「C++ Includes」頁籤,檢查是否含有以下路徑:
C:\GTK\INCLUDE
C:\GTK\INCLUDE\GTK-2.0
C:\GTK\INCLUDE\GLIB-2.0
C:\GTK\INCLUDE\PANGO-1.0
C:\GTK\INCLUDE\CAIRO
C:\GTK\INCLUDE\ATK-1.0
C:\GTK\INCLUDE\GTKGLEXT-1.0
C:\GTK\LIB\GTK-2.0\INCLUDE
C:\GTK\LIB\GLIB-2.0\INCLUDE
C:\GTK\LIB\GTKGLEXT-1.0\INCLUDE
C:\GTK\INCLUDE\LIBGLADE-2.0
C:\GTK\INCLUDE\LIBXML2
 
建立新專案,在新產生的檔案內輸入第一個GTK+的程式碼:
#include 

int main(int argc, char *argv[])
{
GtkWidget *window;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Hello! GTK+!");
gtk_widget_show(window);
gtk_main();
return 0;
}
在Project頁籤內的專案圖示上按右鍵點選「Project Options」點選「Parameters」頁籤
在欄位「Compiler」,輸入以下參數:
-mms-bitfields -IC:\GTK\include\gtk-2.0 -IC:\GTK\lib\gtk-2.0\include -IC:\GTK\include\atk-1.0 -IC:\GTK\include\cairo -IC:\GTK\include\pango-1.0 -IC:\GTK\include\glib-2.0 -IC:\GTK\lib\glib-2.0\include -IC:\GTK\include\libpng12
 
在欄位「Linker」,輸入以下參數:
-LC:\GTK\lib -lgtk-win32-2.0 -lgdk-win32-2.0 -latk-1.0 -lgdk_pixbuf-2.0 -lpangowin32-1.0 -lgdi32 -lpangocairo-1.0 -lpango-1.0 -lcairo -lgobject-2.0 -lgmodule-2.0 -lglib-2.0 -lintl
 
 
compile後執行:
 
compiler參數需要加入-mms-bitfields 的原因:
For reference, here is what the gcc info file has to say about -mms-bitfields:

5.34.3 i386 Variable Attributes
-------------------------------
Two attributes are currently defined for i386 configurations:`ms_struct' and `gcc_struct'
`ms_struct'
`gcc_struct'
If `packed' is used on a structure, or if bit-fields are used it
may be that the Microsoft ABI packs them differently than GCC
would normally pack them. Particularly when moving packed data
between functions compiled with GCC and the native Microsoft
compiler (either via function call or as data in a file), it may
be necessary to access either format.
Currently `-m[no-]ms-bitfields' is provided for the Microsoft
Windows X86 compilers to match the native Microsoft compiler.

2010年7月21日 星期三

[MS-Server]-JDBC連接SQL Server 2005

首先下載三個元件
1.Microsoft SQL Server 2005 Express Edition SP3
    安裝時要選混合認証,並為管理者帳號sa設立一組密碼。
2.SQL Server Management Studio 2005 SP3
3.SQL Server 2005 driver for JDBC
    解壓縮後得到的sqljdbc.jar置於jre/lib/ext下
    筆者實際安裝目錄:C:\Program Files\Java\jre6\lib\ext
    並在classpath下加入這行:C:\Program Files\Java\jre6\lib\ext\sqljdbc.jar
    這樣Class.forName時才找的到driver(後面程式碼會說明)。

接下來為了能夠透過TCP/IP來連接sql server還必需多做下列這個動作
   a.打開SQL Server Configuration Manager -> SQLEXPRESS的協議 -> TCP/IP
   b.右鍵單擊啟動TCP/IP
   c.雙擊進入內容,把IP地址中的IP all中的TCP端口設置為1433
   d.重新啟動SQL Server 2005服務中的SQLEXPRESS服務器
   e.關閉SQL Server Configuration Manager

開下來利用SQL Server Management Studio工具新增一個資料庫"Test"
新增兩個欄位及增加一筆值組如下:
field1   field2
----------------
data1   data2

接下來就是實際的存取範利程式碼
public static void main(String[] args) {
  Connection conn = null;
    try {
      Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
      String url = "jdbc:sqlserver://localhost:1433;DatabaseName=Test";
            
      String user = "sa";
      String password = "50321177";
      conn = DriverManager.getConnection(url, user, password);
      PreparedStatement ps = null;
      ResultSet rs = null;
      String sql = "SELECT * FROM table1";
      ps = conn.prepareStatement(sql);
      rs = ps.executeQuery();
      while (rs.next()) {
        System.out.println(rs.getString("field1"));
        System.out.println(rs.getString("field2"));
      }
      conn.close();
    } catch (Exception e) {
      e.printStackTrace();
  }
}

接下來是額外的補充
在SQL Server 2000 中加載driver和URL路徑的語法是
String driverName = "com.microsoft.jdbc.sqlserver.SQLServerDriver ";
String dbURL = "jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=sample ";
而SQL Server 2005 中加載driver和URL路徑的語法是
String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver ";
String dbURL = "jdbc:sqlserver://localhost:1433; DatabaseName=sample ";

2010年7月10日 星期六

[C#]-讀取excel檔案寫入MS SQL Server

最近寫案子有用到C#去讀取excel檔案再寫入MS SQL Server,
這是滿常用到的功能所以在這裡記錄一下。

首先是利用oledb讀取excel檔,再利用OleDbDataAdapter將資料填入DataSet。
OleDbDataAdapter 是 DataSet 和資料來源之間的橋接器,用來擷取和儲存資料。OleDbDataAdapter 會提供這個橋接器,方法是使用 Fill 從資料來源將資料載入 DataSet,並使用 Update 將 DataSet 中所做的變更傳送至資料來源。

OLE DB簡介
OLE DB 是一種以 COM 為基礎、用來存取資料的應用程式發展介面 (Application Programming Interface,API)。OLE DB 可存取以任何格式所儲存的資料 (資料庫、試算表、文字檔等等),只要該格式能夠使用 OLE DB 提供者 (Provider)。每個 OLE DB 提供者都會公開特定資料來源類型 (例如 SQL Server 資料庫、Microsoft Access 資料庫或 Microsoft Excel 試算表) 的資料。

常用的Provider列表
Database                Provider
----------------------------------------------------
Ms SQL server       Provider=SQLOLEDB
Oracle                   Provider=MSDAORA
Ms Access 2003    Provider=Microsoft.jet.OLEDB.4.0
Ms Access 2007    Provider=Microsoft.ACE.OLEDB.12.0
Ms Excel 2003       Provider=Microsoft.jet.OLEDB.4.0
Ms Excel 2007       Provider=Microsoft.ACE.OLEDB.12.0
IBM DB2                Provider=DB2OLEDB

private void button1_Click(object sender, EventArgs e)
{
  // Show the dialog and get result.
  DialogResult result = openFileDialog1.ShowDialog(); 
  if (result == DialogResult.OK) // Test result.
  {
    // get excel file name
    string fileName = openFileDialog1.FileName;
    textBox1.Text = fileName;
    button2.Enabled = true;
    // import excel into datagridview
    string excelConn = "Provider = Microsoft.Jet.OLEDB.4.0 ; 
           Data Source = " + textBox1.Text + ";
           Extended Properties = 'Excel 8.0;HDR=YES'";
    string strExcelSelect = "SELECT * FROM [Sheet1$]";
    OleDbDataAdapter adapter = 
          new OleDbDataAdapter(strExcelSelect, excelConn);
    DataSet dataSet = new DataSet();
    adapter.Fill(dataSet, "ExcelInfo");
    dataGridView1.DataSource = dataSet.Tables["ExcelInfo"].DefaultView;
  }
}
再來是將資料從datagridview中匯入MS SQL Server
雖然 .NET沒有類似JAVA的preparedstatement,但有類似的應用IDbDataParameter。
private void button2_Click(object sender, EventArgs e)
{
  string strDBInsert = "INSERT INTO customers VALUES
       (@name,@hPhone,@oPhone,@addr,@delivery,@midwife,@source)";
  string strConn = "Data Source = 6A-783500-NB\\SQLEXPRESS;
                   Integrated Security = True";
  SqlConnection sqlConn = new SqlConnection(strConn);
  sqlConn.Open();
  SqlCommand sqlCmd = new SqlCommand(strDBInsert,sqlConn);
  progressBar1.Step = 100/dataGridView1.Rows.Count;
  for (int i = 0; i < dataGridView1.Rows.Count; i++)
  {
    try
    {
      sqlCmd.Parameters.Clear();
      sqlCmd.Parameters.AddWithValue
          ("@name", Convert.ToString(dataGridView1[0, i].Value));
      sqlCmd.Parameters.AddWithValue
          ("@hPhone", Convert.ToString(dataGridView1[1, i].Value));
      sqlCmd.Parameters.AddWithValue
          ("@oPhone", Convert.ToString(dataGridView1[2, i].Value));
      sqlCmd.Parameters.AddWithValue
          ("@addr", Convert.ToString(dataGridView1[3, i].Value));
      sqlCmd.Parameters.AddWithValue
          ("@delivery", Convert.ToString(dataGridView1[4, i].Value));
      sqlCmd.Parameters.AddWithValue
          ("@midwife", Convert.ToString(dataGridView1[5, i].Value));
      sqlCmd.Parameters.AddWithValue
          ("@source", Convert.ToString(dataGridView1[6, i].Value));
      sqlCmd.ExecuteNonQuery();
      progressBar1.PerformStep();
      }
    catch (Exception e1)
    {
      continue;
    }
    dataGridView1.Rows[i].DefaultCellStyle.BackColor =
                                          Color.CornflowerBlue;  
  }
  progressBar1.Value = progressBar1.Maximum;
  sqlConn.Close();
}