2011年7月31日星期日

ADF_095:如何处理BLOB类型数据之一:上传文件并保存到BLOB中

实验环境:JDeveloper 11.1.2.0.0。

1. 实验准备:创建Schema
(1)sqlplus system/welcome1 @create_neverland_user.sql
grant connect, resource to neverland identified by neverland;
Exit;
(2)sqlplus neverland/neverland @create_neveland_table.sql
DROP SEQUENCE uploaded_files_seq;
DROP TABLE uploaded_files;
CREATE TABLE uploaded_files(
id NUMBER PRIMARY KEY,
filename VARCHAR2(80),
content BLOB,
date_created DATE);
CREATE SEQUENCE uploaded_files_seq;
CREATE TRIGGER assign_file_id BEFORE INSERT ON uploaded_files FOR EACH ROW
BEGIN
SELECT uploaded_files_seq.NEXTVAL INTO :NEW.id FROM dual;
END;
.
/

2. 使用ADF BC创建Model层
(1)修改EO的ID字段的属性,因为使用Sequence,所以要改成Sequence类型。

(2)修改EO的DateCreate字段的属性,Insert时自动插入当前日期。

(3)定制化AM对应的java类,添加自定义方法:saveUploadedFileToBlob。
public void saveUploadedFileToBlob(BlobDomain content, String fileName) {
UploadedFilesViewImpl uploadedFileVO = (UploadedFilesViewImpl)getUploadedFilesView1();
UploadedFilesViewRowImpl newRow = (UploadedFilesViewRowImpl)uploadedFileVO.createRow();
uploadedFileVO.insertRow(newRow);
newRow.setContent(content);
newRow.setFilename(fileName);
getDBTransaction().commit();
}

(4)新增一个新的VO:Last5UploadedFilesView,用于显示最新上传的5个文件。
其中添加了一个自动计算字段:文件大小字段。



3. 创建一个新页面:upload_file_to_blob.jspx,核心代码如下:
(1)
<af:form id="f1" usesUpload="true">
<af:inputFile label="File Name:" id="if1" binding="#{myBackingBean.inputFileComponent}"
valueChangeListener="#{myBackingBean.inputFile_valueChangeListener}"/>
<af:commandButton text="Upload" id="cb1" actionListener="#{myBackingBean.uploadButton_actionListener}"/>
</af:form>
(2)拖放Last5UploadedFilesView,生成Table,设置Table的Partial Trigger指向Upload按钮。
(3)设置Last5UploadedFilesView1Iterator的Refresh属性=ifNeeded。
(4)增加一个action Binding:Last5UploadedFilesView1Iterator的Execute操作。

4. 对应的Manage Bean的核心代码如下:
// 点击Upload按钮时,先调用此方法,因为ValueChange事件先于actionListener事件 。
public void inputFile_valueChangeListener(ValueChangeEvent event) {
UploadedFile file = (UploadedFile)event.getNewValue();
if (file != null && file.getLength() > 0) {
String message =
"Successfully uploaded file '" + file.getFilename() + "' (" + file.getLength() + " bytes)";
popupMessage(event, message);
try {
AppModule am = (AppModule)getDefaultApplicationModule();
am.saveUploadedFileToBlob(writeInputStreamToBlobDomain(file.getInputStream()), file.getFilename());
} catch (Exception e) {
e.printStackTrace();
}
}
}

// 点击Upload按钮时,执行完valueChangeListener后,调用此方法
public void uploadButton_actionListener(ActionEvent actionEvent) {
if (this.getInputFileComponent().getValue() != null) {
// 清空InputFile,这样符合中国人的习惯。
inputFileComponent.setValue(null);
// 刷新VO:Last5UploadedFilesView,这样是为了保证从“底层”更新VO。
BindingContainer bindings = getBindings();
OperationBinding operationBinding = bindings.getOperationBinding("Execute");
operationBinding.execute();
} else {
popupMessage(actionEvent, Not_Valid_FileName_Message);
}
}

5. 为了保证只显示最新上传的5个文件,需要修改AM的Java类,Override方法:create:
protected void create() {
super.create();
getLast5UploadedFilesView1().setMaxFetchSize(5);
}

6. 运行页面,上传文件。


Project下载:UploadFileToBlob.7z

问题1:选择某些文件时,明明文件存在,但上传时却报告:java.io.IOException: 系统找不到指定的路径。
解决方法:
其实是web.xml中配置选项:org.apache.myfaces.trinidad.UPLOAD_MAX_MEMORY的问题。
该参数的含义是上载时可以使用的最大内存,改成5M后,就可以了。原来我给的是500K,无法上传大于1M的文件,会报出该异常。
另一个参数:org.apache.myfaces.trinidad.UPLOAD_MAX_DISK_SPACE的含义是允许上传的最大文件。我这里给的也是5M。可以根据需要修改。
为了保证上传效率,这两个参数需要配合着修改。

<context-param>
<!-- Maximum memory per request (in bytes) -->
<param-name>org.apache.myfaces.trinidad.UPLOAD_MAX_MEMORY</param-name>
<!-- Use 5,000K -->
<param-value>5120000</param-value>
</context-param>
<context-param>
<!-- Maximum disk space per request (in bytes) -->
<param-name>org.apache.myfaces.trinidad.UPLOAD_MAX_DISK_SPACE</param-name>
<!-- Use 5,000K -->
<param-value>5120000</param-value>
</context-param>
<context-param>
<!-- directory to store temporary files -->
<param-name>org.apache.myfaces.trinidad.UPLOAD_TEMP_DIR</param-name>
<!-- Use a TrinidadUploads subdirectory of /tmp -->
<param-value>/tmp/TrinidadUploads/</param-value>
</context-param>

参考文献:
1.http://thepeninsulasedge.com/blog/?p=6
2.http://thepeninsulasedge.com/blog/?p=50
3.http://blogs.oracle.com/smuenchadf/examples/#85
4.http://cn.forums.oracle.com/forums/thread.jspa?messageID=4013464
5.http://forums.oracle.com/forums/thread.jspa?threadID=983109
6.http://forums.oracle.com/forums/thread.jspa?threadID=934144
7.http://kr.forums.oracle.com/forums/thread.jspa?threadID=858909

Tools_014:使用UltraISO创建USB系统安装盘

使用光盘安装系统比较慢,如果你有一个8G的高速U盘,那么安装起来会快很多。
如何创建USB系统安装盘呢?——UltraISO可以帮助我们。
下载UltraISO后,安装后,启动,UltraISO是个共享软件,不注册也能使用。
重要步骤截图如下:
1. 使用UltraISO打开iso系统文件

2. 选择启动->写入硬盘映像

3. 选择U盘,一定要看仔细,不要选错哦。

4. 现在可以用该U盘装系统了,选择从U盘启动。

2011年7月27日星期三

Tips_008:如何解决DOS窗口中的中文乱码问题?

有时在DOS窗口中启动Server时,出现中文的地方会显示乱码。
这说明,Server日志中文输出正确,只是DOS窗口“不认识”
我们可以更改当前代码页,来让其正确显示中文。
命令格式:chcp nnnn。nnnn就是代码页代码。
chcp 936 代码页改为GBK。
chcp 437 代码页改为英文。

2011年7月26日星期二

WebCenter_024:OmniPortlet使用指南之二:传递参数

实际开发中,经常会遇到这种场景:选中表格中的一行,刷新旁边的某个Portlet。
本实验就是针对此种情况,其它场景大家可以举一反三。
开发工具:JDeveloper 11.1.1.5.0。
首先完成《OmniPortlet使用指南之一:访问CSV数据

1. 实验准备:创建Schema
(1)sqlplus system/welcome1 @create_veeva_user.sql
grant connect, resource to veeva identified by veeva;
Exit;
(2)sqlplus veeva /veeva @create_contries_table.sql
create table countries (
country_id number primary key,
country_name varchar2(50),
continent varchar2(50),
currency varchar2(50)
);
insert into countries (country_id, country_name, continent, currency) values (1001,'France','Europe','Euro');
insert into countries (country_id, country_name, continent, currency) values (1002,'Italy','Europe','Euro');
insert into countries (country_id, country_name, continent, currency) values (1003,'USA','North America','US dollar');
insert into countries (country_id, country_name, continent, currency) values (1004,'Hungary','Europe','Hungarian forint');
insert into countries (country_id, country_name, continent, currency) values (1005,'Indonesia','Asia','Indonesian rupiah');
insert into countries (country_id, country_name, continent, currency) values (1006,'New Zealand','Oceana','New Zealand dollar');
insert into countries (country_id, country_name, continent, currency) values (1007,'Japan','Asia','Japanese yen');
insert into countries (country_id, country_name, continent, currency) values (1008,'Australia','Oceana','Australian dollar');
insert into countries (country_id, country_name, continent, currency) values (1009,'United Kingdom','Europe','British pound');
insert into countries (country_id, country_name, continent, currency) values (1010,'India','Asia','Indian rupee');
insert into countries (country_id, country_name, continent, currency) values (1011,'Brazil','South America','Brazilian real');
insert into countries (country_id, country_name, continent, currency) values (1012,'Kenya','Africa','Kenyan shilling');
commit;

2. 创建WebCenter Portal 应用

3. 在Portal应用中,新建一个ADF Model Project
不要在Portal Project创建Model层的东西,应该新建一个Model Project,从项目设计就体现出MVC的设计。
创建ADF Business Components from Tables:Contries。

4. 在Portal Project中,新建一个页面,拖放ContriesView,生成Read Only Table。

5. 查看页面的Bindings,展开左下角的Structure面板。
variables节点下的是页面变量(page variables),当拖放一个Portlet到页面时,JDeveloper会分析该Portlet有没有参数(portlet parameters)。
如果有,JDeveloper会自动创建对应的页面变量(page variables),就像我们在这里看到的情况:
OmniPortlet有5个参数,因此创建了5个对应的页面变量。
既然OmniPortlet中的参数全部来自于对应的页面变量,那剩下的事就变得简单了:
只要把组件中变量的值赋值给页面变量,页面变量会在赋值给对应的OmniPortlet中的参数。
好,下面我们就开始把组件中变量的值赋值给页面变量。


如果没有绑定CountryId,可以自己手工添加。

把#{bindings.CountryId.inputValue}赋值给OmniPortlet3_1_Param1的DefaultValue。

6. 启动并访问应用,修改OmniPortlet定制化选项中Filter。


7. 设置OmniPortlet的Parial Trigger属性,指向Table的id。

重新发布并运行Portal应用,发现点击表格的某一行时,右边的OmniPortlet并没有刷新。
经过反复检查,OmniPortlet配置没有错误,而是#{bindings.CountryId}无法成功赋值给OmniPortlet3_1_Param1的DefaultValue。
具体原因不明,日志中会报出一个警告:
无法将 PortletBinding OmniPortlet3_1上的参数Param1设置为非字符串或字符串数组的值。

花了一天的时间,查阅了大量文档和例子,都没有找到解决办法。
无奈之下,我写了一个Backing Bean,从BackingBean中获取#{bindings.CountryId}值,再赋给OmniPortlet3_1_Param1的DefaultValue。

Manage Bean的部分代码如下:
String currentCountryId = "1001";

public MyBackingBean() {
}

public String getCurrentCountryId() {
return JSFUtils.getManagedBeanValue("bindings.CountryId").toString();
}

public void setCurrentCountryId() {
currentCountryId =
JSFUtils.getManagedBeanValue("bindings.CountryId").toString();
}

现在,点击表格的某一行,右边的OmniPortlet会随着改变。
查看日志,那一行警告信息也消失了。


Project下载:Veeva_OmniPortlet_Parameter.7z

问题终于搞清楚了,都是CountryId字段类型惹的祸,换成CountryName就没有问题了。
如果你的JavaBean中使用Number型的字段,也会出同样的问题。
但问题是,CountryId本身就是Number型,如果一定要传CountryId字段的值,目前只能按照上面的方法通过一个ManageBean转发。
不知道有没有其它的好方法,我会继续关注此问题。

TestPortlet.7z

2011年7月25日星期一

WebCenter_023:OmniPortlet使用指南之一:访问CSV数据

OmniPortlet可以让开发人员快速把各种数据源(spreadsheet,XML,Web services,RSS feed,database,外部应用页面的数据),以Web页面的形式发布。
开发工具:JDeveloper 11.1.1.5.0。
重要步骤说明:

1. 创建一个WebCeneter Portal 应用。

2. 注册OmniPortlet


这里为了简单起见,使用的是内嵌的WebLogic Server上的OmniPortlet。
http://127.0.0.1:7101/portalTools/omniPortlet/providers/omniPortlet


3. 创建一个页面,拖放注册的OmniPortlet
注意,新页面要符合Portal Project的规范,应该放在和home.jspx同级的目录下,不要乱放。


4. 运行Portal应用,登录后,定制化OmniPortlet。
OmniPortlet是一个“半配置”的Portlet,需要运行时做定制化才能使用。


tours.csv文件内容如下:
tour_id,country_id,tour_name,tour_desc,duration,image_url,price
F1,1001,Evening in Paris,"Savor the ambiance of this romantic city. This tour starts with a moonlit, champagne dinner-dance aboard a bateau-mouche, cruising down the Seine. After a tour through the historic Montmartres district, you will take in a fabulous Moulin Rouge show featuring the French CanCan at this atmospheric cabaret. Tour price (includes a four course gourmet dinner; wine is extra): $129 (airfare not included)",1 special evening,evening_in_paris.jpg,$129.00
F2,1001,Canals of France,"Come aboard one of France's most authentic and beautiful boutique hotel barges. The itinerary features the Languedoc, one of Southern France's most fascinating regions, recently dubbed the next Provence. Our waterway is the picture-perfect 300 year old Canal du Midi, designated a UNESCO World Heritage Site and considered to be the prettiest canal in France. This tour rivals none for its picturesque scenery and relaxed pace. Tour price: $499 (based on double occupancy, airfare not included)","3 days, 2 nights",canals_of_france.jpg,$499.00
I1,1002,Viva Venice,"The romance of Venice, with its maze of canals, rustic piazzas, arched bridges, rustic palaces, charming gondoliers, and bustling water traffic, is simply like no other place on earth. This tour includes all the popular tourist sites such as St. Mark's square, the Doge's palace, and a visit to see Venetian glass blowers plying their trade on the island of Murano. Tour price: $999 (based on double occupancy, airfare not included)","3 days, 2 nights",viva_venice.gif,$999.00
I2,1002,Roman Holiday,"Revel in the culture and cuisine of the Eternal City, which is positively steeped in history. Our day tours, which include visits to St. Peter's Square, Vatican City, the Roman Forum, and the Colosseum, feature local guides who are 100% fluent in English. You'll also have plenty of time for sipping a cappucino at a sidewalk cafe and for doing your own shopping and exploring. Tour price: $899 (based on double occupancy, airfare not included)","3 days, 2 nights",rome_tour.gif,$899.00
I3,1002,Tuscany Treking,"A perfect family holiday--enjoy an exhillarating combination of walking, culture, and exploration. We've designed this tour for adults and kids alike, allowing plenty of time for meandering through medieval villages as well as strolling through vineyards and olive groves. Every night is spent at a different family-style hotel, where your luggage will await you. Tour price: $1,210 based on double occupancy, airfare not included).","10 days, 9 nights",tuscany_trek.gif,"$1,210.00 "
I4,1002,Mediterranean Sand & Sea,"Come, oh sun seekers, to bask in comfort and style on the sandy shores of the Mediterranean. Tour includes accommodation at a four star hotel, continental breakfasts, and free shuttle service to your choice of golden beaches. Tour price: $764 (based on double occupancy, airfare not included)","5 days, 4 nights",sun_and_sand.gif,$764.00
B1,1005,Bali Homestay,"Mingle with Balinese people, experience their daily way of life, while learning their language, cooking, and culture in a traditional Balinese village. Proceeds from this program are donated to participating villages to improve their education programs and facilities. We guarantee that, after a week as a welcome guest in a Balinese household, you will never be a stranger in Bali again! Tour price: $999 (airfare not included)","7 days, 6 nights",bali_homestay.jpg,$999.00
B2,1005,Bali Culture,"Balinese dance, painting, shadow puppets, cremation ceremony, and kite flying are just a few of the cultural attractions that make Bali the ideal tour destination. On this tour, you'll make Ubud village your travel hub where almost nightly a variety of dances are performed in village compounds. Ubud is world renouned for its wood carving, painting, and other handicrafts. The Ubud Traditional Art Market is an absolute must to see.Tour price: $764 (based on double occupancy, airfare not included)","5 days, 4 nights",bali_culture.jpg,$764.00
NZ1,1006,South Island Sojourn,"The South Island of New Zealand is a true adventure and outdoor paradise. On this combination adventure and sight-seeing tour, you'll explore the cities and towns, mountains, lakes, rivers, and sea coast of the South Island. You'll also have your choice of a variety of adventure experiences, including caving, rafting, canoeing and kayaking, surfing, jetboating, jet skiing, glacier walks, scenic flights, mountain biking, horse treks, fishing, and gold panning--who could ask for more?","5 days, 4 nights",milford_sound.gif,$750.00
A1,1008,Trasmania Trek,"More than 30% of the island state of Tasmania is protected by World heritage areas, National parks, and nature reserves. This makes Tasmania your perfect trekking destination. Imagine yourself walking in Australia's largest temperate rainforest, surrounded by the timeless grandeur of rugged mountain ranges, wild rivers, and cascading waterfalls. Here's your golden opportunity for getting away from it all. Trips available year round.","5 days, 4 nights",tasmania_trek.gif,$825.00
A2,1008,Outback Adventure,"This three day experience includes camping under the huge, starry desert skies, enjoying traditional Aussie meals around a camp fire (complete with a ""camp oven""), and, of course, a visit to such awe inspiring sights as Kings Canyon, the Olgas, and Ulhuru. Experience breath-taking views of Australia's most famous attraction, Ayres Rock (Uluru), which is a massive red rock that protrudes high from the seemingly barren lands that surround it. Here's your chance to view the rock both at sunset and dawn when the sun's reflection makes turns it bright red.","3 days, 2 nights",ayers_rock.gif,$395.00
N1,1010,Darjeeling Delight,"Known as the Queen of the Hill Stations, Darjeeling is internationally famous for its tea industry and the Darjeeling Himalayan Railway, a UNESCO World Heritage Site. Other sights include the Tenzing Norgay Himalayan Mountaineering Institute, which trains mountaineers and has a fascinating small museum, and the Padmaja Naidu Himalayan Zoological Park. A dawn visit to the Tiger Hills is a must for all Darjeeling visitors who want to see sunrise over the Mt Kanchenjungha.Tour price: $999 (based on double occupancy, airfare not included)","5 days, 4 nights",darjeeling_sojourn.gif,$999.00
N2,1010,Golden Triangle,"The Golden Triangle is an excellent introduction to India for first-time visitors. On this tour, you will view the ancient and modern attractions of Delhi, the unparalleled Taj Mahal in Agra, and the exotic forts and palaces of Jaipur. Without a doubt, the Golden Triangle tour is one of the most popular tourist circuits of India. Tour price: $1199 (based on double occupancy, airfare not included)","6 days, 5 nights",golden_triangle.gif,"$1,199.00 "


设置一个初始过滤条件,并且设置一次最多返回两个结果。

Layout Style选择HTML。

不重复的表头部分
<table width="600">
重复的表行部分,注意OmniPortlet会自动分析CSV数据,索引方式为:“##列头名称##”。
特别注意,图片的路径写法:
<tr>
<td width="200">
<font class="PortletHeading1">##tour_name##</font>
</td>
</tr>
<tr>
<td>The duration of the <font class="PortletHeading2">##tour_name##</font> tour is <font class="PortletHeading2">##duration##</font>. Price from <font class="PortletHeading2">##price##</font>.
</td>
</tr>
<tr>
<td width="500">
<div style="float: right; clear: left;"><img src="./images/##image_url##" valign="top"></div>##tour_desc##
</td>
</tr>
不重复的表尾部分
</table>

5. 重新运行Portal应用,查看OmniPortlet,结果如下:登录后,定制化OmniPortlet。


Project下载:Veeva_OmniPortlet.7z

2011年7月24日星期日

WebCenter_022:如何获取WebCenter Portal 应用登录用户的信息?

Portal应用是由很多个Portlet组成的,每一个Portlet可以看成是一个“迷你Web应用”。
有时在Portlet中,我们需要根据登录用户的信息做一些事情,比如过滤一些查询条件。
由于Portal应用跟普通的Web应用不同,因此获取登录用户的信息的方式也不同。

在WebCenter Suite 11gR4中,我们可以这样获取用户的登录信息。
1.在portlet.xml文件中,增加属性com.oracle.portlet.useWsrpUserContextForUserAuthenticationInfo=true
选中要设置该属性的Portlet,增加一个container-runtime-option属性:useWsrpUserContextForUserAuthenticationInfo。


2.在ManageBean中获取用户信息
这里给出两种方式:
(1)如果你使用了ADF Portlet bridge来生成Portlet,你可以从ADFContext得到用户信息,这种方式比较快捷:
ADFContext.getCurrent().getSecurityContext().getUserName()
(2)其它情况,你可以从FacesContext得到用户信息,这是更通用的获取方式:
FacesContext.getCurrentInstance().getExternalContext().getRemoteUser()

3.通过Expression Language来获取用户信息
#{securityContext.userName}

参考文献:
1. http://download.oracle.com/docs/cd/E17904_01/webcenter.1111/e10148/jpsdg_java_adv.htm#BABBGBBE

说明:以上信息来自于Ken Zhao和Liao ShuangShuang转发的邮件,在此表示感谢。

WebCenter_021:开发WebCenter Portal 应用:天气预报的例子之三

接下来我们创建一个WebCenter Portal应用。
开发工具:JDeveloper 11.1.1.5.0。
重要步骤如下:
(1)

(2)创建一个WSRP Producer Registration


这里输入之二中的最后得到的那个WSDL地址:http://pmma-cn:8893/WeatherPortletApp/portlets/wsrp2?WSDL。

(3)成功的话,应该能在Connection中看到

(4)发布并运行WeatherPortalApp,登录后,修改Home页面,增加刚刚注册成功的Portlet Producer。

(5)最终的结果如下:

(6)注意,我们总共发布和运行了三个应用,它们各自运行在不同的Server上。


Project下载:WeatherPortalApp.7z
(完)

2011年7月23日星期六

WebCenter_020:开发WebCenter Portal 应用:天气预报的例子之二

接下来我们创建一个ADF Web应用,使用URL Service Data Control获取前面CSV格式的数据。
开发工具:JDeveloper 11.1.1.5.0。
重要步骤如下:
(1)

(2)创建一个taskflow
从本应用来看,实际不需要创建taskflow,可以直接创建页面,创建taskflow主要是考虑重用性。

(3)创建一个page。

(4)在Model Project中,创建URL Service Data Control


注意,这个URL是带参数的:zipcode。

给zipcode一个初始值。

(5)如果一切顺利,应该能够生成WeatherDataControl如下:
注意,必须保证jsp返回的结果是CSV格式,第一行是列头的名字,第一行不能是空行,切记。
我前后修改了weather.jsp很多次,都是因为第一行是空行的原因导致无法成功生成Data Control,呵呵。

(6)为weather.jsff添加内容:
拖放Data Control中的Return节点,生成Read Only Table,调整设置如下:

拖放Data Control中的zipcode参数节点,生成Select One Choice:
给初始值:

给一个静态的List,这里仅仅出于演示的需要,实际情况可以是动态的或Model Driven的。

拖放Data Control中的loadData方法节点,生成Button。
然后设置Select One Choice的AutoSummit属性为true,并把Table的Partial Trigger属性指向Select One Choice的id。
此时,可以隐藏Button,因为改变zipcode后,会自动提交(不需要点击按钮),并刷新表格。

(7)如果将来需要修改URL Connection路径,可以改动connections.xml
URL Connection 路径保存在[application_name]\.adf\META-INF\connections.xml文件中:
如果有改动可以直接修改该文件。
<?xml version = '1.0' encoding = 'UTF-8'?>
<References xmlns="http://xmlns.oracle.com/adf/jndi">
<Reference name="WeatherURLConnection" className="oracle.adf.model.connection.url.HttpURLConnection" xmlns="">
<Factory className="oracle.adf.model.connection.url.URLConnectionFactory"/>
<RefAddresses>
<XmlRefAddr addrType="WeatherURLConnection">
<Contents>
<urlconnection name="WeatherURLConnection" url="http://pmma-cn:7001/WeatherJSPApp/weather.jsp"/>
</Contents>
</XmlRefAddr>
</RefAddresses>
</Reference>
</References>

(8)创建一个新page:weather.jspx,把weather-task-flow拖放到其中。

(9)在JDeveloper中的内嵌的WebLogic Server中,访问该应用:


(10)现在要Portletize该ADF应用,右键weather-task-flow,选择Create Portlet。


(11)发布该ADF Web应用到WC_CustomServicesProducer Server上。
创建Web WAR Deployment Profile的最简单的方法是:右键web.xml。然后Deploy。



(12)访问http://pmma-cn:8893/WeatherADFApp/info

点击WSRP V2 WSDL,会得到该Portlet的WSDL:http://pmma-cn:8893/WeatherPortletApp/portlets/wsrp2?WSDL。
记住这个地址,后面会用到。

Project下载:WeatherADFApp.7z
(未完待续)

WebCenter_019:开发WebCenter Portal 应用:天气预报的例子之一

首先完成《创建自己的WebCenter Portal/Portlet Server》。
因为这里例子涉及到三个应用,分别部署在三个Server上,这三个应用的开发人员可能不同,这正是开发Portal应用的魅力:用标准的方式集成大家开发的应用。

这里我们假设用户已经开发好了一个天气预报的Web应用。开发工具:JDeveloper 11.1.1.5.0。
该应用非常简单,只有一个普通的jsp页面:weather.jsp,不过大家可以把它想象成一个复杂的应用,比如从后台访问数据库获取数据,然后再展示到前台。

weather.jsp内容如下:根据zipcode参数值,返回该地区的天气状况,是以逗号间隔的CSV格式。
之所以用这种格式,是为了展示ADF URL Service Data Control功能,在下一篇文章将会介绍。

<%
  if (request.getParameter("zipcode") == null || request.getParameter("zipcode").equals("")) {
      out.println("dayOfWeek,hiTemp,img,lowTemp,precip,sky,zipCode");
      out.println("Monday,55,Rainy.gif,45,0,Rainy,94065 Tuesday,85,Sunny.gif,80,0,Sunny,94065");
      out.println("Wednesday,61,PartlyCloudy.gif,50,0,Partly Cloudy,94065");
      out.println("Thursday,80,PartlyCloudy.gif,70,0,Cloudy,94065");
      out.println("Friday,63,Rainy.gif,50,0,Rainy,94065 Saturday,70,Rainy.gif,55,0,Rainy,94065");
      out.println("Sunday,68,PartlySunny.gif,50,0,Partly Sunny,94065");
  } else if  (request.getParameter("zipcode").equals("94065")){  
      out.println("dayOfWeek,hiTemp,img,lowTemp,precip,sky,zipCode");
      out.println("Monday,55,Rainy.gif,45,0,Rainy,94065 Tuesday,85,Sunny.gif,80,0,Sunny,94065");
      out.println("Wednesday,61,PartlyCloudy.gif,50,0,Partly Cloudy,94065");
      out.println("Thursday,80,PartlyCloudy.gif,70,0,Cloudy,94065");
      out.println("Friday,63,Rainy.gif,50,0,Rainy,94065 Saturday,70,Rainy.gif,55,0,Rainy,94065");
      out.println("Sunday,68,PartlySunny.gif,50,0,Partly Sunny,94065");
  } else if (request.getParameter("zipcode").equals("60001")) {
      out.println("dayOfWeek,hiTemp,img,lowTemp,precip,sky,zipCode");
      out.println("Monday,55,Rainy.gif,45,0,Rainy,60001");
      out.println("Tuesday,85,Rainy.gif,80,0,Rainy,60001");
      out.println("Wednesday,61,PartlyCloudy.gif,50,0,Partly Cloudy,60001");
      out.println("Thursday,80,PartlyCloudy.gif,70,0,Cloudy,60001");
      out.println("Friday,63,Rainy.gif,50,0,Rainy,60001");
      out.println("Saturday,70,Rainy.gif,55,0,Rainy,60001");
      out.println("Sunday,68,PartlyCloudy.gif,50,0,Partly Sunny,60001");
  } else if (request.getParameter("zipcode").equals("90001")) {
    out.println("dayOfWeek,hiTemp,img,lowTemp,precip,sky,zipCode");
    out.println("Monday,70,PartlyCloudy.gif,55,0,Partly Cloudy,90001");
    out.println("Tuesday,80,PartlyCloudy.gif,70,0,Cloudy,90001");
    out.println("Wednesday,85,PartlyCloudy.gif,80,0,Sunny,90001");
    out.println("Thursday,68,PartlySunny.gif,50,0,Partly Sunny,90001");
    out.println("Friday,63,PartlySunny.gif,50,0,Partly Sunny,90001");
    out.println("Saturday,61,PartlyCloudy.gif,50,0,Partly Cloudy,90001");
    out.println("Sunday,55,Rainy.gif,45,0,Rainy,90001");
  } else if (request.getParameter("zipcode").equals("94401")) {
    out.println("dayOfWeek,hiTemp,img,lowTemp,precip,sky,zipCode");
    out.println("Monday,55,Sunny.gif,45,0,Sunny,94401");
    out.println("Tuesday,85,Sunny.gif,80,0,Sunny,94401");
    out.println("Wednesday,61,Sunny.gif,50,0,Sunny,94401");
    out.println("Thursday,80,Sunny.gif,70,0,Cloudy,94401");
    out.println("Friday,63,PartlyRainy.gif,50,0,Partly Sunny,94401");
    out.println("Saturday,70,PartlySunny.gif,55,0,Partly Sunny,94401");
    out.println("Sunday,68,PartlySunny.gif,50,0,Partly Sunny,94401");
  } else if (request.getParameter("zipcode").equals("94402")) {
    out.println("dayOfWeek,hiTemp,img,lowTemp,precip,sky,zipCode");
    out.println("Monday,55,Rainy.gif,45,0,Rainy,94402");
    out.println("Tuesday,85,Rainy.gif,80,0,Rainy,94402");
    out.println("Wednesday,61,PartlyCloudy.gif,50,0,Partly Cloudy,94402");
    out.println("Thursday,80,PartlyCloudy.gif,70,0,Cloudy,94402");
    out.println("Friday,63,Sunny.gif,50,0,Sunny,94402");
    out.println("Saturday,70,Sunny.gif,55,0,Sunny,94402");
    out.println("Sunday,68,PartlySunny.gif,50,0,Partly Sunny,94402");
  }
%>

应用重要开发步骤如下:
(1)

(2)

(3)

(4)创建Web WAR Deployment Profile的最简单的方法是:右键web.xml。

(5)

(6)发布到Admin Server上,这里我们假定Admin Server是部署传统Java EE应用(JSP、Servlet、EJB)的地方。

(7)访问http://pmma-cn:7001/WeatherJSPApp/weather.jsp。
应该能够返回一个CSV格式的数据,看起来有些“丑陋”,是不是?

查看页面源码,你会发现这是一个CSV格式的数据,该数据将作为我们的数据源,后面主要工作就是怎样把这个CSV数据,以表格的形式呈现在Portal应用中。


Project下载:WeatherJSPApp.7z
(未完待续)