Friday, August 31, 2012

哦! Oracle Proxy User

這一篇解釋 Oracle Proxy User相當經典。http://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:21575905259251。

目前大部分網路應用系統開發(Web Application)在連接資料庫時,會建立一位資料庫連線使用者(讓我們假設此使用者的資料庫username為"MIDTIER"),然後所用經過網路應用系統認証(authenticate)的網路用戶,就會以MIDTIER連進資料庫,進行資料存取。

為了讓使用者MIDTIER能順利存取所需要的資料,一般會採取以下幾種的授權(authorize)方式:
1. 所有要被存取的資料全部建立於MIDTIER的使用者名下
2. 資料的擁有者為其他資料庫使用者,由其他使用者授權給MIDTIER使用

就安全與實務上來說,方法2為比較適合的方式;也是建議的方法。其優點除安全之外,在實務上也比較方管理,應用程式也比較容易移植在其他環境。

有個問題,在這樣的方式下,MIDTIER要給夠足夠的授權,才能夠滿足前端網路應用系統使用者的需求。讓我們假設:我們有2個不同授權的網路使用者-MANAGER與CLERK。

MANAGER與CLERK一經被網路應用程式認證,就能透過MIDTIER存取資料,因此MIDTIER的授權是要同時為MANAGER與CLERK授權的聯集。這會導致MIDTIER被過份授權。如果要想達到適當的授權,Oracle Proxy User為一種解決方案。(這裏有一種隱含的假設--MANAGER與CLERK的認證是由網路應用系統所認證而且資料庫"相信"網路應用系統的認證)

以下為展示Oracle Proxy User的做法:我們會在Oracle資料庫下建立幾個不同的USER:

MIDTIER為網路應用系統與資料庫之間連線的使用者或有人用為中間件軟體(middleware)建立Connection Pool的使用者:在這裏我們只會給予最少的權限"CREATE SESSION"(只能夠建立資料庫連線)。
MANAGER與CLERK為模擬經過網路應用系統認證過的使用者(因為經過網路應用系統認證所以資料庫不再認證)。
SCOTT為我們熟悉的USER,是真正表格的擁有者,為簡化展示會分別給予CLERK SELECT的授權於表格DEPT, EMP而MANAGER SELECT授權於表格DEPT, EMP, BONUS, SALGRADE。

SQL> connect system/oracle@orcl
Connected.
SQL> create user MIDTIER identified by MIDTIER;

User created.

SQL> create user MANAGER identified by whateveryouwanatoset;

User created.

SQL> create user CLERK identified by whateveryouwanatoset;

User created.

SQL> grant CREATE SESSION to MIDTIER;

Grant succeeded.

SQL> grant CREATE SESSION to MANAGER;

Grant succeeded.

SQL> grant CREATE SESSION to CLERK;

Grant succeeded.

SQL> grant SELECT on SCOTT.DEPT to CLERK;

Grant succeeded.

SQL> grant SELECT on SCOTT.DEPT to MANAGER;

Grant succeeded.

SQL> grant SELECT on SCOTT.EMP to CLERK;

Grant succeeded.

SQL> grant SELECT on SCOTT.EMP to MANAGER;

Grant succeeded.

SQL> grant SELECT on SCOTT.BONUS to MANAGEAR;
grant SELECT on SCOTT.BONUS to MANAGEAR
                               *
ERROR at line 1:
ORA-01917: user or role 'MANAGEAR' does not exist


SQL> grant SELECT on SCOTT.BONUS to MANAGER;

Grant succeeded.

SQL> grant SELECT on SCOTT.SALGRADE to MANAGER;

Grant succeeded.

SQL>

接下來設定MANAGER與CLERK可以透過MIDTIER登入Oracle 資料庫--MIDTIER可以代表MANAGER與CLERK登入資料庫。

SQL> alter user CLERK grant connect through MIDTIER;

User altered.

SQL> alter user MANAGER grant connect through MIDTIER;

User altered.

SQL>

上述的指令是說:MANAGER與CLERK可以透過MIDTIER所使用的資料連線連接資料庫:一旦前面的應用系統認證MANAGER或CLERK為其本人之後,Oracle資料庫就承認前端應用系統的認證並允許MIDTIER可以代表經過認證的MANAGER或CLERK,執行原本為MANAGER或CLERK所被授權的權利。


現在先使用MIDTIER連進Oracle 資料庫,並查看是否具備讀取SCOTT的資料表格。

SQL> connect MIDTIER/MIDTIER@ORCL
Connected.
SQL> select table_name from all_tables where owner='SCOTT';

no rows selected

接著使用PROXY USER連接的語法登入資料庫
SQL> connect MIDTIER[CLERK]/MIDTIER@ORCL
Connected.
SQL> select table_name from all_tables where owner='SCOTT';

TABLE_NAME
------------------------------
DEPT
EMP

SQL> select user from dual;

USER
------------------------------
CLERK

SQL> connect MIDTIER[MANAGER]/MIDTIER@ORCL
Connected.
SQL> select table_name from all_tables where owner='SCOTT';

TABLE_NAME
------------------------------
DEPT
EMP
BONUS
SALGRADE

SQL> select user from dual;

USER
------------------------------
MANAGER


首先先解釋"connect MIDTIER[CLERK]/MIDTIER@ORCL" -- CLERK已經先被我驗證過了,CLERK就是CLERK沒有問題, 我就是CLERK(我代表CLERK來登入資料庫),但是我是用MIDTIER的使用者與其密碼來登入資料庫;因為之前我們都已經同意,資料庫相信我對前端使用者的認証。因此當登入成功之後,我就是(代表)CLERK,我只被授權看到SCOTT的2個表格。


當使用"connect MIDTIER[MANAGER]/MIDTIER@ORCL",當登入成功之後,我就是(代表)MANAGER,我就被授權看到SCOTT的4個表格。

以上過程中(先排除討論MANAGER與CLERK如何被認証),都是使用MIDTIER的使用者名稱與密碼,MANAGER與CLERK的密碼完全不必知道也不用被送出;而且資料庫知道真正的前端使用者是誰(SELECT USER FROM DUAL),現在就容易做資料稽核了。

PROXY USER的機制被廣泛地運用在Web Application,利用MIDTIER建立connection pool,且MIDTIER除了資料庫連線能力之外,沒有其它的能力,保障了connection pool 資料庫連線的安全性與便利性。經過Web Application認証之後才以前端真正使用者的身份(如為MANAGER或CLEARK) 進行資料庫的操作;請注意我們並沒有重新進行資料庫連接,而是在取得資料庫connection pool的資源後,設定其真正的使用者,進行對資料庫的操作。

如此,我們可以保有使用connection pool的快速與方便而且同時具備了安全性;從資料庫的角度,也能稽核到真正進入資料庫的使用者。

Oracle JDBC connection pool Proxy User 使用程式範例,詳情可參考Proxy Authentication 於 Oracle® Database JDBC Developer's Guide

接下來面臨的問題:
如果面對的是大量的網際網路使用者,難到真的要將所有使用者都建立在資料庫上嗎?還是有其它的解決方案 -- 解決方案之一:Oracle Enterprise User Security

Wednesday, February 22, 2012

Oracle Database 呼叫外部地址定位網路服務(Web Service)

先至Oracle Database Web Service 下載 Web Services Callout Utility
按照說明(10.htm 或11.htm)至少完成 步驟1. Preparing the Oracle Client and Oracle Database Server

在完成上述步驟之後, 可以在$ORACLE_HOME/bin下找到"jpub"的script,複製一份(我的環境是 Oracle 11g, $ORACLE_HOME/jdk的版本是"1.5.0_30")
$ cp jpub jpub1.5
修改 jpub1.5, 以下為jpub1.5的內容

#! /bin/sh
#
# jpub - shell for invoking the JPublisher tool
# Usage: jpub <-J-javavm-options> <-jpub-options>
#
# %W% %E%
# 


unset JAVA_HOME
JAVA_HOME=$ORACLE_HOME/jdk
export JAVA_HOME


TMPCLASSPATH=$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$ORACLE_HOME/jdbc/lib/ojdbc5_g.jar:$ORACLE_HOME/jlib/orai18n.jar:$ORACLE_HOME/sqlj/lib/runtime12.jar:$ORACLE_HOME/jlib/orai18n.jar:$ORACLE_HOME/jlib/orai18n-mapping.jar:$ORACLE_HOME/sqlj/lib/translator.jar:$ORACLE_HOME/javavm/lib/aurora.zip:$ORACLE_HOME/sqlj/lib/dbwsa.jar:$ORACLE_HOME/rdbms/jlib/xdb.jar:$ORACLE_HOME/lib/xsu12.jar:$ORACLE_HOME/jlib/jndi.jar:$ORACLE_HOME/rdbms/jlib/aqapi.jar:$ORACLE_HOME/rdbms/jlib/jmscommon.jar:$ORACLE_HOME/lib/xmlparserv2.jar:$CLASSPATH
export TMPCLASSPATH
TMP_C_CLASSPATH=$TMPCLASSPATH
export TMP_C_CLASSPATH LD_LIBRARY_PATH=$ORACLE_HOME/lib32:$ORACLE_HOME/lib:$JAVA_HOME/lib
export LD_LIBRARY_PATH


switches=-Xmx1024M 


JRE64FLAG=
export JRE64FLAG
files=
switcharg=


javavm=$JAVA_HOME/bin/java


while [ $# -gt 0 ]
do
 case "$1" in
 -J-classpath) TMPCLASSPATH="$2"
 shift;;
 -classpath) TMP_C_CLASSPATH="$2"
 shift;;
 -J-*) switcharg=`echo $1 | cut -c3-`
 switches="$switches $switcharg";;
 -vm=*) javavm=`echo $1 | cut -c5-` ;;
 *) files="$files $1";;
 esac
 shift
done 
exec $javavm $JRE64FLAG -classpath $TMPCLASSPATH $switches oracle.jpub.java.Main -C-classpath=$TMP_C_CLASSPATH $files


然後執行(為一行的指令,若看到分行為版本格式的緣故)
$ jpub1.5 -httpproxy=proxy.my_company.com:80 -proxywsdl=http://address.tgos.nat.gov.tw/ADDR_WS/Multiple_MatchLocate/Multiple_MatchLocate.asmx?WSDL -endpoint=http://address.tgos.nat.gov.tw/ADDR_WS/Multiple_MatchLocate/Multiple_MatchLocate.asmx


Multiple ports available. To publish a particular port, use -proxyopts=port:Multiple_MatchLocateSoap12, or -proxyopts=port:Multiple_MatchLocateSoap.
Use the default port Multiple_MatchLocateSoap12.
tmp/src/genproxy/Multiple_MatchLocateSoap12ClientJPub.java
plsql_wrapper.sql
plsql_dropper.sql
plsql_grant.sql
plsql_revoke.sql
plsql_proxy.jar
Please run plsql_wrapper.sql in the user schema, load the generated jar file and run plsql_grant.sql in the SYS schema.


使用jpub(jpub操作手冊)的測試對象為台灣地址定位服務(內政部資訊中心提供的網路服務-Web Service-元件),其WSDL的位址:
http://address.tgos.nat.gov.tw/ADDR_WS/Multiple_MatchLocate/Multiple_MatchLocate.asmx?WSDL
Endpoint 網址:
http://address.tgos.nat.gov.tw/ADDR_WS/Multiple_MatchLocate/Multiple_MatchLocate.asmx?
依jpub 所需要輸入的參數對應,則會產生幾個PL/SQL wrapper package(應用程式套件)及 SOAP proxy jar檔。
依照指示,在使用呼叫的使用者下("MVDEMO")執行"plsql_wrapper.sql",並使用"loadjava"將"plsql_proxy.jar"載入資料庫:修改"plsql_grant.sql",將"<USER>"置換成要呼叫的使用者名稱("MVDEMO"),再以SYS執行"plsql_grant.sql"。
以上就完成了從Oracle DB 呼叫外部Web Service的PL/SQL程式界面。

Thursday, February 16, 2012

在Oracle 資料庫使用 Google Geocoding (地址定位)

Google Geocoding API 的說明網頁請參考:http://code.google.com/intl/zh-TW/apis/maps/documentation/geocoding/
我所關心的是輸入中文(台灣)的地址,Google Geocoding回傳座標,取得經緯度之後,轉換成Oracle SDO_GEOMETRY資料型態,更新該欄位。
首先,測試以下URL,以確定必要的資訊都已輸入
http://maps.googleapis.com/maps/api/geocode/xml?address=台北市忠孝西路一段66號&language=zh-tw&region=tw&sensor=true
其回應如下:
紅色框的地方就是我要的座標(預設為WGS84)。
確定了使用Google Geocoding的傳遞參數之後,接下來就要在資料庫中撰寫一函式,使用HTTP呼叫Google Geocoding API來取得座標並轉化成SDO_GEOMETRY資料型態。
在資料庫內要能執行HTTP的呼叫,必需進行以下的設定:

以SYS登入資料庫賦予使用者(這裏使用'MVDEMO'))能夠執行UTL_HTTP程式套件的權限
 grant execute on utl_http to mvdemo;

以SYSTEM登入建立對外存取網站ACL(Access Control List)清單
connect system/password
-- Step 1: Create the Access Control List and Its Privilege Definitions
 BEGIN
 DBMS_NETWORK_ACL_ADMIN.CREATE_ACL (
  acl          => 'maps_googleapis_com.xml',  -- ACL XML檔案,自己定義一個名稱
  description  => 'Google Maps API ACL', -- 說明該ACL XML檔案的用途
  principal    => 'MVDEMO',  -- 指定管理的使用者或角色(role),在這裏是以mvdemo使用者
  is_grant     => TRUE,
  privilege    => 'connect', -- 對外網站的權限
  start_date   => null,
  end_date     => null);
END;
/

將要可以對外連結的網站加入ACL
Step 2: Assign the Access Control List to One or More Network Hosts
BEGIN
 DBMS_NETWORK_ACL_ADMIN.ASSIGN_ACL (
  acl         => 'maps_googleapis_com.xml',   -- 要對應上一步剛剛建立的ACL XML檔案名稱
  host        => 'maps.googleapis.com',   -- Google Geo-coding的網站位址
  lower_port  => null, -- 允許對外連結的網路埠(port)的範圍;NULL表示不設限
  upper_port  => null);
END;
如果會透過 PROXY SERVER對外連接,不要忘了要加入允許存取網站的清單
BEGIN
 DBMS_NETWORK_ACL_ADMIN.ASSIGN_ACL (
  acl         => 'maps_googleapis_com.xml',
  host        => 'proxy.mycompany.com',  -- HTTP Proxy Server (http://host[:port]/)
  lower_port  => null,
  upper_port  => null);
END;

建立取得地址定位的函式
create or replace
function google_geocoding
( p_address in varchar2 default '台北市忠孝西路一段66號')
return sdo_geometry -- 回傳 Oracle 空間資料型態
deterministic
as
  http_req_url varchar2(32767);
  http_req utl_http.req;
  http_resp utl_http.resp;
  http_resp_html varchar2(32767);
  resp_xml XMLType;
  latitude number;
  longitude number;
begin
  UTL_HTTP.SET_PROXY (' proxy.mycompany.com ');  -- 設定 HTTP Proxy Server
  http_req_url:='http://maps.googleapis.com/maps/api/geocode/xml?language=zh-tw&region=tw&sensor=true&address=' || p_address; -- 準備送的URL參數
  http_req:= utl_http.begin_request(http_req_url); -- 開始進行HTTP 的傳送要求,預設方式為GET
-- 設定 HTTP Header
  UTL_HTTP.SET_HEADER(http_req, 'User-Agent', 'Mozilla/4.0');
  UTL_HTTP.SET_HEADER(http_req,'Content-Type','text/xml;charset=UTF-8');
  http_resp:= utl_http.get_response(http_req); -- 取得 HTTP 回傳
  utl_http.read_text(http_resp, http_resp_html); -- 讀取 HTTP 回傳內容
  utl_http.end_response(http_resp); -- 結束HTTP的請求
  -- 將讀取的HTTP 內容轉換成XML 文件
  resp_xml:= XMLType.createXML(http_resp_html);
  -- 讀取 Latitude, Longitude 的內容並將其轉換成數字
  latitude:= resp_xml.extract('/GeocodeResponse/result/geometry/location/lat/text()').GETNUMBERVAL();
  longitude:= resp_xml.extract('/GeocodeResponse/result/geometry/location/lng/text()').GETNUMBERVAL();
  return point(longitude,latitude); -- 傳回"點"空間位置
  EXCEPTION
    WHEN others THEN
      return point(NULL,NULL);
end;

測試:
select google_geocoding() from dual;
GOOGLE_GEOCODING()
---------------------------------------------------------------------------------------
MDSYS.SDO_GEOMETRY(2001,8307,MDSYS.SDO_POINT_TYPE(121.515241,25.046139,NULL),NULL,NULL)


select google_geocoding('台北市政府') from dual;
GOOGLE_GEOCODING('台北市政府')
-----------------------------------------------------------------------------------------MDSYS.SDO_GEOMETRY(2001,8307,MDSYS.SDO_POINT_TYPE(121.5644722,25.0372787,NULL),NULL,NULL)


select google_geocoding('不知道的地方') from dual;
GOOGLE_GEOCODING('不知道的地方')
----------------------------------------------------------------------------MDSYS.SDO_GEOMETRY(2001,8307,MDSYS.SDO_POINT_TYPE(NULL,NULL,NULL),NULL,NULL)

Monday, February 13, 2012

建立 Oracle Spatial WFS Part II

Oracle Web Feature Service 架構
如上圖,要特別指出的是,在Application Server上提供WFS Server API,資料是儲存在資料庫上的"Spatial Tables",所以要透過"Feature Mapping"或稱為Publish (發佈)提供原"Spatial Tables"額外的資訊以滿足WFS的要求,但是原"Spatial Tables"並不需要再多一份複製(所以是用"Feature Mapping"這個字眼)。
設定Oracle WFS 的步驟,如下圖
在這些設定步驟,我們會使用SDO_WFS_PROCESS與SDO_WFS_LOCK packages
1. Set capabilities info
設定 Server Capabilities 的方法有(任選一種方法就可以)
I. 將外部"WFScapabilitiesTemplate.xml" 載入資料庫
以SYSTEM登入資料庫,執行下列SQL (WFScapabilitiesTemplate.xml 放在'/u01/oracle/db/product/11.2.0.3/dbhome_1' 目錄下
create or replace directory WFS_XML
  as '/u01/oracle/db/product/11.2.0.3/dbhome_1';
begin
  SDO_WFS_PROCESS.insertCapabilitiesInfo(
    xmltype(
      bfilename('WFS_XML', 'WFScapabilitiesTemplate.xml'),
      nls_charset_id('AL32UTF8')
    )
  );
end;
/
commit;
II. 直接使用PL/SQL載入WFScapabilitiesTemplate.xml 的內容
範例
WFS GetCapabilities 測試URL:
http://localhost:7001/SpatialWS-SpatialWS-context-root/xmlwfsservlet?request=GetCapabilities&service=WFS&version=1.0.0
正確的設定應該會出下面的結果
2. Enable the schema
這個步驟相對簡單,只要透過MDSYS, SYSTEM或SYS執行"SDO_WFS_PROCESS.GrantMDAccessToUser"
SQL> execute SDO_WFS_PROCESS.GrantMDAccessToUser('MVDEMO')

3. Publish Table
必須要用SYSTEM或SYS來執行(但不包括MDSYS)Publish Tables的預儲程序(stored procedure)
範例:
declare
  featureDescriptorXML CLOB :=
    '<?xml version="1.0" ?>
       <FeatureType xmlns:mvdemons="http://www.myserver.com/mvdemo" 
         xmlns="http://www.opengis.net/wfs">
         <Name> mvdemons:Cities</Name>
         <Title>Cities</Title>
         <SRS>SDO:8307</SRS>
     </FeatureType>';
begin
  SDO_WFS_PROCESS.publishFeatureType(
    dataSrc           => 'MVDEMO.CITIES',
    ftNsUrl           => 'http://www.myserver.com/mvdemo',
    ftName            => 'Cities',
    ftNsAlias         => 'mvdemons' ,
    featureDesc       => xmltype(featureDescriptorXML),
    schemaLocation    => null,
    pkeyCol           => 'CITY',
    columnInfo        => MDSYS.StringList('PointMemberType'),
    pSpatialCol       => 'LOCATION',
    featureMemberNs   => null,
    featureMemberName => null,
    srsNs             => null,
    srsNsAlias        => null
  );
end;
/
其中 MVDEMO.CITIES
SQL> desc cities
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 LOCATION                                           MDSYS.SDO_GEOMETRY
 CITY                                               VARCHAR2(42)
 STATE_ABRV                                         VARCHAR2(2)
 POP90                                              NUMBER
 RANK90                                             NUMBER

說明
featureDescriptorXML為當收到 GetCapabilities要求時,所回傳XML的內容
dataSrc => 'MVDEMO.CITIES' 表示所對應旳Schema與Table名稱
ftName => 'Cities' 表示WFS的Feature Name
pkeyCol => 'CITY' 表示該表的的 Primary Key
columnInfo => MDSYS.StringList('PointMemberType') 表示所對應GML 圖徵)feature)的型態
pSpatialCol => 'LOCATION' 表示表格為SDO_GEOMETRY欄位型態的欄位名稱

4. Register table for updates
Oracle Spatial WFS 支援 WFS-T。要能支援WFS-T需要將能提供此項功能的資料表格進行註冊。透過該表格的擁有者(Owner)執行SDO_WFS_LOCK.registerFeatureTable
範例:
SQL> execute SDO_WFS_LOCK.registerFeatureTable('MVDEMO','CITIES')

5. Notify WFS server
新增的異動並不動自動送到WFS server,透過MDSYS, SYSTEM或SYS執行SDO_WFS_PROCESS. insertFtMDUpdated來通知WFS有新的更新異動;WFS 則會每10秒(預設)(定義在<wfs_cache_sync_interval>10000</wfs_cache_sync_interval>)來檢查更新的時間,如果有變動,則會載入新的WFS定義
範例:
SQL> execute SDO_WFS_PROCESS.InsertFtMDUpdated('http://www.myserver.com/mvdemo','Cities', sysdate)

6. Grant access rights
給予"匿名使用者"對資料表格適當的存取權限。以本範來說,MVDEMO.CITIES需要給予SELECT、INSERT、UPDATE、DELETE(為滿足WFS-T的功能)。
SQL> grant SELECT, INSERT, UPDATE, DELETE on cities to SpatialWsXmlUser;

7. Set Configuration params
主要是修改 WEB-INF/conf/wsconfig.xml 的設定內容(基本上使用預設就可以了)

<wfs_cache_sync_interval>10000</wfs_cache_sync_interval>
設定WFS檢查異動同步的時間間隔
<wfs_query_timeout>10</wfs_query_timeout>
預設查詢鎖定時間(單位是分鐘)
<wfs_lock_expiry>4</wfs_lock_expiry>
預設異動鎖定時間(單位是秒)
<cached_feature_types>
  <feature_type ns="http://www.myserver.com/mvdemo" name="Cities" />
  <feature_type ns="http://www.myserver.com/mvdemo" name="Counties" />
</cached_feature_types>
設定需要放置於WFS memory cache 的Features

Friday, February 10, 2012

Using PL/SQL To Load WFScapabilitiesTemplate.xml Directly


-- Run as MDSYS, SYSTEM or SYS


-- Load the capabilities from a PL/SQL variable
declare
  capabilitiesXML CLOB :=
'<WFS_Capabilities version="1.0.0" xmlns="http://www.opengis.net/wfs" xmlns:ogc="http://www.opengis.net/ogc" >
   <Service>
     <Name> Oracle WFS </Name>
     <Title> Oracle Web Feature Service </Title>
     <Abstract> Web Feature Service maintained by Oracle </Abstract>
     <OnlineResource> http://www.someserver.com/wfs/cwwfs.cgi? </OnlineResource>
   </Service>
   <Capability>
      <Request>
         <GetCapabilities>
            <DCPType>
               <HTTP>
                  <Get onlineResource="http://www.myserver.com/get?"/>
               </HTTP>
            </DCPType>
            <DCPType>
               <HTTP>
                  <Post onlineResource="http://www.myserver.com/post?"/>
               </HTTP>
            </DCPType>
         </GetCapabilities>
         <DescribeFeatureType>
            <SchemaDescriptionLanguage>
               <XMLSCHEMA/>
            </SchemaDescriptionLanguage>
            <DCPType>
               <HTTP>
                  <Post onlineResource="http://www.myserver.com/post?"/>
               </HTTP>
            </DCPType>
         </DescribeFeatureType>
         <GetFeature>
            <ResultFormat>
               <GML2/>
            </ResultFormat>
            <DCPType>
               <HTTP>
                  <Post onlineResource="http://www.myserver.com/post?"/>
               </HTTP>
            </DCPType>
         </GetFeature>
         <GetFeatureWithLock>
            <ResultFormat>
               <GML2/>
            </ResultFormat>
            <DCPType>
               <HTTP>
                  <Post onlineResource="http://www.myserver.com/post?"/>
               </HTTP>
            </DCPType>
         </GetFeatureWithLock>
         <Transaction>
            <DCPType>
               <HTTP>
                  <Post onlineResource="http://www.myserver.com/post?"/>
               </HTTP>
            </DCPType>
         </Transaction>
         <LockFeature>
            <DCPType>
               <HTTP>
                  <Post onlineResource="http://www.myserver.com/post?"/>
               </HTTP>
            </DCPType>
         </LockFeature>
      </Request>
   </Capability>
   <FeatureTypeList>
      <Operations>
         <Insert/>
         <Update/>
         <Delete/>
         <Query/>
         <Lock/>
      </Operations>
   </FeatureTypeList>
   <ogc:Filter_Capabilities>
      <ogc:Spatial_Capabilities>
         <ogc:Spatial_Operators>
            <ogc:BBOX/>
            <ogc:Equals/>
            <ogc:Disjoint/>
            <ogc:Intersect/>
            <ogc:Touches/>
            <ogc:Crosses/>
            <ogc:Within/>
            <ogc:Contains/>
            <ogc:Overlaps/>
            <ogc:Beyond/>
            <ogc:DWithin/>
         </ogc:Spatial_Operators>
      </ogc:Spatial_Capabilities>
      <ogc:Scalar_Capabilities>
         <ogc:Logical_Operators/>
         <ogc:Comparison_Operators>
            <ogc:Simple_Comparisons/>
            <ogc:Like/>
            <ogc:Between/>
            <ogc:NullCheck/>
         </ogc:Comparison_Operators>
         <ogc:Arithmetic_Operators>
            <ogc:Simple_Arithmetic/>
         </ogc:Arithmetic_Operators>
      </ogc:Scalar_Capabilities>
   </ogc:Filter_Capabilities>
</WFS_Capabilities>
';
begin
  SDO_WFS_PROCESS.insertCapabilitiesInfo (xmltype(capabilitiesXML));
end;
/

-- Commit the change
commit;

WFScapabilitiesTemplate.xml


<WFS_Capabilities version="1.0.0" xmlns="http://www.opengis.net/wfs" xmlns:ogc="http://www.opengis.net/ogc" >
   <Service>
  <Name> Oracle WFS </Name>
  <Title> Oracle Web Feature Service </Title>
  <Abstract> Web Feature Service maintained by Oracle </Abstract>
  <OnlineResource> http://www.someserver.com/wfs/cwwfs.cgi? </OnlineResource>
</Service>
   <Capability>
      <Request>
         <GetCapabilities>
            <DCPType>
               <HTTP>
                  <Get onlineResource="http://www.myserver.com/get?"/>
               </HTTP>
            </DCPType>
            <DCPType>
               <HTTP>
                  <Post onlineResource="http://www.myserver.com/post?"/>
               </HTTP>
            </DCPType>
         </GetCapabilities>
         <DescribeFeatureType>
            <SchemaDescriptionLanguage>
               <XMLSCHEMA/>
            </SchemaDescriptionLanguage>
            <DCPType>
               <HTTP>
                  <Post onlineResource="http://www.myserver.com/post?"/>
               </HTTP>
            </DCPType>
         </DescribeFeatureType>
         <GetFeature>
            <ResultFormat>
               <GML2/>
            </ResultFormat>
            <DCPType>
               <HTTP>
                  <Post onlineResource="http://www.myserver.com/post?"/>
               </HTTP>
            </DCPType>
         </GetFeature>
         <GetFeatureWithLock>
            <ResultFormat>
               <GML2/>
            </ResultFormat>
            <DCPType>
               <HTTP>
                  <Post onlineResource="http://www.myserver.com/post?"/>
               </HTTP>
            </DCPType>
         </GetFeatureWithLock>
         <Transaction>
            <DCPType>
               <HTTP>
                  <Post onlineResource="http://www.myserver.com/post?"/>
               </HTTP>
            </DCPType>
         </Transaction>
         <LockFeature>
            <DCPType>
               <HTTP>
                  <Post onlineResource="http://www.myserver.com/post?"/>
               </HTTP>
            </DCPType>
         </LockFeature>
      </Request>
   </Capability>
   <FeatureTypeList>
      <Operations>
         <Insert/>
         <Update/>
         <Delete/>
         <Query/>
         <Lock/>
      </Operations>
   </FeatureTypeList>
   <ogc:Filter_Capabilities>
      <ogc:Spatial_Capabilities>
         <ogc:Spatial_Operators>
            <ogc:BBOX/>
            <ogc:Equals/>
            <ogc:Disjoint/>
            <ogc:Intersect/>
            <ogc:Touches/>
            <ogc:Crosses/>
            <ogc:Within/>
            <ogc:Contains/>
            <ogc:Overlaps/>
            <ogc:Beyond/>
            <ogc:DWithin/>
         </ogc:Spatial_Operators>
      </ogc:Spatial_Capabilities>
      <ogc:Scalar_Capabilities>
         <ogc:Logical_Operators/>
         <ogc:Comparison_Operators>
            <ogc:Simple_Comparisons/>
            <ogc:Like/>
            <ogc:Between/>
            <ogc:NullCheck/>
         </ogc:Comparison_Operators>
         <ogc:Arithmetic_Operators>
            <ogc:Simple_Arithmetic/>
         </ogc:Arithmetic_Operators>
      </ogc:Scalar_Capabilities>
   </ogc:Filter_Capabilities>
</WFS_Capabilities>

建立 Oracle Spatial WFS Part I

環境
Oracle Database 11g Release 2 (11.2.0.2 以上)
Oraacle Weblogic 11g Release 1 (10.3.5)+
Oracle Spatial Web Service 架構圖如下:
不同於 Oracle WMS的架構是Spatial Web Service在Middleware上提供WS Server API,實際WS的處理則是在Oracle Database內;管理與設定工具也是建置於資料庫內(PL/SQL API)。
建立Oracle Spatial Web Service的主要步驟:
1. Deploy SDOWS Web 應用程式
2. 設定相關資料庫使用者帳號
3. 設定Middleware連接資料庫的data sources
4. 設定一般Web Service的參數
5. 重新啓動 SDO web Service

Deploy Spatial Web Service
實際上將"sdows.ear"佈署到middleware中間件應用伺服器。我們使用的middleware是WebLogic 10.3.5;建議使用最新的sdows.ear,sdows.ear會跟著資料庫軟體的安裝,放置於$ORACLE_HOME/md/jlib的目錄下。
我是將$ORACLE_HOME/md/jlib/sdows.ear複製至安裝的目錄"/home/oracle"
[以Linux為範例,先解開sdows.ear]
$ cp $ORACLE_HOME/md/jlib/sdows.ear /home/oracle/.
$ cd /home/oracle
[oracle@localhost ~]$ mv sdows.ear sdows.ear1
[oracle@localhost ~]$ mkdir sdows.ear
[oracle@localhost ~]$ cd sdows.ear
[oracle@localhost sdows.ear]$ /usr/java/default/bin/jar xvf ../sdows.ear1
[oracle@localhost sdows.ear]$ mv sdows.war sdows.war1
[oracle@localhost sdows.ear]$ mkdir sdows.war
[oracle@localhost sdows.ear]$ cd  sdows.war
[oracle@localhost sdows.war]$  /usr/java/default/bin/jar xvf ../sdows.war1

設定相關資料庫者帳號
Oracle資料庫預設管理者的帳號都是被鎖定的,需要被解開且重新設定其密碼
以資料庫系統管理者 (system)登入,執行下列SQL

-- 解除鎖定帳號
alter user mdsys account unlock;
alter user spatial_csw_admin_usr account unlock;
alter user spatial_wfs_admin_usr account unlock;

-- 設定管理者帳號的密碼(需要記住密碼,待會在設定WebLogic data sources時需要用到)
alter user mdsys identified by <password>;
alter user spatial_csw_admin_usr identified by <password>;
alter user spatial_wfs_admin_usr identified by <password>;

接下來要設定"匿名使用者",預設資料庫使用者名稱"SpatialWsXmlUser"。該使用者會處理所有傳入XML/HTTP的要求,透過MDSYS進行"proxy authentication",因此SpatialWsXmlUser的密碼跟本不重要(因為完全用不到),但不要設成很好猜到。若要修改"匿名者"的預設使用者名稱,則要一起修改"WSConfig.xml"所對應的使用者。
 -- 建立 "匿名使用者"使用者proxy authentication
create user spatialwsxmluser identified by whatever;
-- 給予"匿名使用者"連接資料庫的系統權限
grant create session to spatialwsxmluser;
-- 透過MDSYS資料庫使用者進行 proxy authentication
alter user spatialwsxmluser grant connect through mdsys;
-- 給予"匿名使用者"接受處理WFS與CSW外部要求的角色能力
grant wfs_usr_role to spatialwsxmluser;
grant csw_usr_role to spatialwsxmluser;

移除不必要角色密碼的設定(選項)

alter role WFS_USR_ROLE not identified;
alter role CSW_USR_ROLE not identified;
alter role SPATIAL_WFS_ADMIN not identified;
alter role SPATIAL_CSW_ADMIN not identified;

設定Middleware連接資料庫的data sources
這裏我們使用WebLogic 10.3.5 做為應用伺服器(middleware application server)。以下們要建立的data sources與相關資訊:
Data Source Name JNDI  Name Database User
Usage
WfsProxyConnection jdbc/WfsProxyConnectionCoreDS mdsys WFS User connections
WFS_ADMIN_CONN_NAME jdbc/WFS_ADMIN_CONN_NAME spatial_wfs_admin_usr WFS Administration
CatalogProxyConnection jdbc/CatalogProxyConnectionCoreDS mdsys CSW User connections
CSW_ADMIN_CONN_NAME jdbc/CSW_ADMIN_CONN_NAME spatial_csw_admin_usr CSW Administration
OpenLsProxyConnection jdbc/OpenLsProxyConnectionCoreDS mdsys OpenLS User connections
 在WebLogic Console (http://localhost:7001/console, for example),點選左邊選單Services->data Sources
在中間"Summary of JDBC Data Sources","Data Sources" 點選 'New","Generic Data Source";分別設定 "Name","JNDI Name","Database Type";Database Type 選 Oracle,按下一步
選擇Oracle JDBC Driver -- "Oracle's Driver (Thin) for Service connections: Versions: 9.0.1 and later";按下一步
點掉(unchecked) "Supports Global Transactions",按下一步
 設定Oracle JDBC連線資訊,按下一步
 按下"測試連線設定"(選項)
 結果顯示連線成功,則表示資料庫連線設定正確,按下一步
 設定Data Source至指定的 application servers,按下"完成"。
依次將要設定的Data Source 設定完成。

設定一般Web Service 的參數
如果都使用預設值,基本上沒有什麼要特別設定的。如果需要可以更改log的設定值,以方便追踪與除錯。WSConfig.xml位於 WEB-INF/conf/WSConfig.xml

<logging log_level="finest" log_thread_name="true"
         log_time="true">
   <log_output name="System.err" />
   <log_output name="log/ws.log" />
</logging>
如果有變動"匿名使用者"的預設使用者,也在此一併修改。

重新啓動 SDO Web Service
停止SDO Web Service:按下Deployments -> 點選 sdows -> 點 "Stop" -> "Force Stop Now"
啓動SDO Web Service:按下Deployments -> 點選 sdows -> 點 "Start" -> "Servicing all requests"


Thursday, February 09, 2012

建立 Oracle Spatial/Location WMS

簡單地介紹如何將儲存在Oracle Spatial/Location 的空間資料發佈成WMS (Web Map Service)
需要具備的環境
1. Oracle Database (已經將空間資料載入於SDO_GEOMETRY欄位的表格)
2. Oracle Mapviewer (透過 Oracle Mabuilder 將空間資料表格已定義成BASE_MAP,THEME)
Oracle WMS 是由Oracle Mapviewer提供,架構圖簡化如下:
1. 啓動WMS的設定
進入Oracle Mapviewer的設定,並移除<wms_cnfig>的註解
修改成適當的主機名稱,Port,通訊協定 (protocol),default_datasource (這個設定很重要)以及 public_datasource

<wms_config
  host="www.oracle.com"
  port="80"
  protocol="http“
  default_datasource="mvdemo"
  public_datasources="mvdemo"

>
...
</wms_config>

在這裏用的data_source是"mvdemo",應該在之前Mapviewer的設定上已經談過了,這裏就略過。附加說明:如果沒有設定default_datasource,則預設值為"wms"。

2. 設定其它在<wms_config>的內容
3. 設定SDO與EPSG座標參數對應表(選項,如果要使用EPSG座標代號就一定要設定)
修改<wms_config>的內容設定

<wms_config> ...
  <sdo_epsg_mapfile>
    ../conf/epsg_srids.properties
  </sdo_epsg_mapfile>
</wms_config>
epsg_srids.properties 的內容格式為 sdo_srid=epsg_srid
範例
82208=32601
82271=32602
81989=27700
27700-27700

這樣就大致設定完成
接下來就可以進行測試
GetCapabilities
http://127.0.0.1:7001/mapviewer/wms?VERSION=1.1.1&REQUEST=GetCapabilities&SERVICE=WMS
http://127.0.0.1:7001/mapviewer/wms?VERSION=1.3.0&REQUEST=GetCapabilities&SERVICE=WMS
(請注意URL並沒有換行)
GetMap
http://127.0.0.1:7001/mapviewer/wms?VERSION=1.1.0&REQUEST=GetMap&FORMAT=image/png&SRS=EPSG:4326&BBOX=-127,25,-78,40&WIDTH=800&HEIGHT=600&LAYERS=THEME_DEMO_STATES,THEME_DEMO_STATES_LINE,CUSTOMERS
http://127.0.0.1:7001/mapviewer/wms?VERSION=1.3.0&REQUEST=GetMap&FORMAT=image/png&CRS=EPSG:4326&BBOX=-127,25,-78,40&WIDTH=800&HEIGHT=600&LAYERS=THEME_DEMO_STATES,THEME_DEMO_STATES_LINE,CUSTOMERS
(BBOX 不要超出WMS可以提供的範圍)
以下為使用"BASE_MAP"再加套上"LAYERS"
http://127.0.0.1:7001/mapviewer/wms?VERSION=1.1.0&REQUEST=GetMap&FORMAT=image/jpeg&SRS=SDO:8307&BBOX=-127,25,-78,40&WIDTH=800&HEIGHT=600&BASEMAP=DEMO_MAP&DATASOURCE=mvdemo&LAYERS=CUSTOMERS
http://127.0.0.1:7001/mapviewer/wms?VERSION=1.3.0&REQUEST=GetMap&FORMAT=image/jpeg&CRS=SDO:8307&BBOX=-127,25,-78,40&WIDTH=800&HEIGHT=600&BASEMAP=DEMO_MAP&DATASOURCE=mvdemo&LAYERS=CUSTOMERS

GetFeatureInfo

http://127.0.0.1:7001/mapviewer/wms?VERSION=1.1.0&REQUEST=GetFeatureInfo&query_Layers=THEME_DEMO_STATES&SRS=SDO:8307&BBOX=-124,32,-116,41&WIDTH=800&HEIGHT=600&X=10&Y=10
http://127.0.0.1:7001/mapviewer/wms?VERSION=1.3.0&REQUEST=GetFeatureInfo&query_Layers=THEME_DEMO_STATES&CRS=SDO:8307&BBOX=-124,32,-116,41&WIDTH=800&HEIGHT=600&I=10&J=10
其結果
<GetFeatureInfo_Result>
<ROWSET name="THEME_DEMO_STATES">
<ROW num="1">
<ROWID>
AAASeoAABAAAVghAAE
</ROWID>
</ROW>
</ROWSET>
</GetFeatureInfo_Result>




Thursday, February 02, 2012

SELECT COUNT(*)

最近在客戶那邊遇到了一個效能上的問題。客戶有一個程式會對資料表不斷地新增資料,有另外一支程式會來處理新增進入資料表格的資料,並對處理的結果異動其狀態;但最後不會將該資料刪除。也就是說該資料表格會愈長愈大。
客戶也會要知道,目前待處理的資料筆數還有多少,最簡單的方式就是 SELECT COUNT(*)....一開始當然速度是在可以接受的情況下,但隨著時間,資料筆數愈來愈大,每次 SELECT COUNT(*)的時間就愈來愈長...。然後加入自動更新(Refresh),從5秒自動更新變成10秒, 30秒,....;再加上同時之間有許多使用者會同時上線,然後你會看到資料庫主機CPU都花在USER I/O,速度愈變愈慢....
很熟悉嗎? 如何改善執行效率呢?
我的想法是:沒辦法,除非改變程式 SELECT COUNT(*)的架構,不然 USER I/O是無法避免的。怎麼做--利用一資料表格來儲存計數 -- SELECT COUNT(*) -- 的結果,新增資料的程式則異動該值+1;處理異動的程式則異動該值-1;查詢筆數的程式則直接取得該值。

建立計數儲存表格
create table table_row_counts (table_name varchar2(40), row_counts number);

設定初始值、讀取數值、新增數值與異動...等
當然直接UPDATE是一種方法,在這我使用 stored procedure

create or replace
PACKAGE ROW_COUNTS AS
  procedure init_rows;
  procedure add_row;
  procedure reduce_row;
  function get_rows return number;
END ROW_COUNTS;
/

create or replace
package body row_counts as

function get_rows return number as
rowsn number;
begin
select row_counts into rowsn from table_row_counts where table_name = 'EMP';
return rowsn;
end;

procedure add_row as
begin
update table_row_counts set row_counts = row_counts + 1 where table_name = 'EMP';
commit;
end;

procedure init_rows as
begin
update table_row_counts set row_counts = (select count(*) from emp ) where  table_name = 'EMP';
commit;
end;

procedure reduce_row as
begin
update table_row_counts set row_counts = row_counts - 1 where table_name = 'EMP';
commit;
end;

end;


/

Monday, January 02, 2012

Oracle 資料庫校能調校 -- 使用ADDM/SQL Tuning Advisor來解決SQL校能問題

By leverage "Oracle By Example - Chapter 10: Monitoring and Tuning the Database"

從Oracle DB Console, Performance 看效能,如下圖:
我們期望CPU的部分是佔主要的部分,其它顏色的部分是愈少愈好。上圖,很顯然CPU的部分所佔的比例太少,其它wait events太多,有調校的空間。
如果問題發生的時間夠長,ADDM自然會自動進行分析,其分析的結果可以在"Home"頁籤下的 "ADDM Performance Analysis" 找到;如果ADDM還尚未執行,則可以在 "Performance" -> "Average Active Sessions" 點選 "View ADDM Run" 圖示。
點選對資料庫時間造成最大影響的連結
可以看到對資料庫時間造成影響的原因。範例為"Top SQL Statements"
點選該SQL連結
再按下"Schedule SQL Tuning Advisor"按鈕;我們想省點力氣,讓Oracle ADDM告訴我們怎麼調整SQL
完成 SQL Tuning Advisor 的工作
這個時候Advisor會告訴我們該如何調整SQL。範例非常適用於套裝軟體(無法變更SQL指令),但是Oracle 找到另一種更好執行的方式,因此建議使用"SQL Profile"來變動原執行計劃的方式。直接下實施"Implement"
按下"Yes"
SQL Profile 已經建立
再檢視OEM,應該就會看到校能改善