2009年3月29日星期日

WLS_068:使用UserTransaction实现全局事务之二:在EJB上增加事务属性

开发运行环境:JDeveloper 11.1.2.4 + Oracle XE Database 11gR2
本文最后一次修改日期:2013-06-16

上一个例子中,方法doTran1和doTran2是在一个类中,实际情况中,这两个方法往往来自不同的类,比如可能来自两个不同的EJB。这时,如何保证全局的事务呢?

测试环境:1个WebLogic Server,两个Oracle XE数据库,其中WebLogic Server上建立了两个DataSource指向两个Oracle XE数据库;两个EJB,部署到同一个WebLogic Server上。

1. 创建EJB




右键选择增加的方法:doTran1,选择暴露到Remote和Local Interface中。
同时,注意到我们可以在doTran1方法上增加事务属性,这里我们选择TransactionAttributeType.REQUIRED。

EJB 完整代码如下(另一个EJB也是同样的逻辑):

package model;

import java.sql.Connection;

import java.sql.Statement;

import javax.annotation.Resource;

import javax.ejb.SessionContext;
import javax.ejb.Stateless;

import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;

import javax.sql.DataSource;

@Stateless(name = "DoTran1SessionEJB", mappedName = "XATest-Model-DoTran1SessionEJB")
public class DoTran1SessionEJBBean implements DoTran1SessionEJB, DoTran1SessionEJBLocal {
    @Resource
    SessionContext sessionContext;

    @Resource(name = "jdbc/hr1DS")
    DataSource hr1DS;

    public DoTran1SessionEJBBean() {
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void doTran1() throws Exception {
        System.out.println("Hello doTran1");
        Connection hr1 = hr1DS.getConnection();
        hr1.setAutoCommit(false);
        Statement stm1 = hr1.createStatement();
        stm1.execute("DELETE FROM jobs WHERE job_id='CEO1'");
        stm1.execute("INSERT INTO jobs VALUES('CEO1', 'CEO1', 1,2)");
        stm1.close();
        if (hr1 != null)
            hr1.close();
    }
}

注意这里使用了依赖注入的方式“注入了”DataSource。

2. 创建每个EJB测试的Client类,确保测试成功。

3. 根据每个EJB测试的Client类,创建一个测试两个EJB的Client类。

package model;

import java.util.Hashtable;

import javax.naming.CommunicationException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import javax.transaction.UserTransaction;

public class DoTranSessionEJBBeanClient {
    public DoTranSessionEJBBeanClient() {
        super();
    }

    public static void main(String[] args) {
        UserTransaction tran = null;
        try {
            final Context context = getInitialContext();
            tran = (UserTransaction)context.lookup("javax.transaction.UserTransaction");
            tran.begin();

            DoTran1SessionEJB doTran1SessionEJB =
                (DoTran1SessionEJB)context.lookup("XATest-Model-DoTran1SessionEJB#model.DoTran1SessionEJB");
            doTran1SessionEJB.doTran1();

            DoTran2SessionEJB doTran2SessionEJB =
                (DoTran2SessionEJB)context.lookup("XATest-Model-DoTran2SessionEJB#model.DoTran2SessionEJB");
            doTran2SessionEJB.doTran2();

            tran.commit();
        } catch (CommunicationException ex) {
            System.out.println(ex.getClass().getName());
            System.out.println(ex.getRootCause().getLocalizedMessage());
            System.out.println("\n*** A CommunicationException was raised.  This typically\n*** occurs when the target WebLogic server is not running.\n");
        } catch (Exception e) {
            e.printStackTrace();
            try {
                tran.rollback();
            } catch (Exception ex) {
                System.out.println("Rollback Exception: " + ex);
            }
        }
    }

    private static Context getInitialContext() throws NamingException {
        Hashtable env = new Hashtable();
        // WebLogic Server 10.x connection details
        env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
        env.put(Context.PROVIDER_URL, "t3://127.0.0.1:7101");
        return new InitialContext(env);
    }
}

4. 测试
测试结果应该和上一个实验一样。

Project 下载:XATest.7z

WLS_067:使用UserTransaction实现全局事务之一:基本用法

开发运行环境:WebLogic 12.1.1 开发版 + Oracle XE Database 11gR2

本文最后一次修改日期:2013-06-16

测试环境:1个WebLogic Server,两个Oracle XE数据库,其中WebLogic Server上建立了两个DataSource指向两个Oracle XE数据库。

测试代码如下:

import java.util.Hashtable;

import java.sql.Connection;
import java.sql.Statement;
import javax.sql.DataSource;

import javax.naming.Context;
import javax.naming.InitialContext;

import javax.transaction.UserTransaction;

import oracle.jdbc.xa.client.OracleXADataSource;

public class UserTransactionXATest {
    private static Context context = null;

    public static void main(String[] args) {
        UserTransactionXATest test = new UserTransactionXATest();
        UserTransaction tran = null;
        context = getInitialContext();

        try {
            tran = (UserTransaction)context.lookup("javax.transaction.UserTransaction");
            tran.begin();
            test.doTran1();
            //int divideBy0 = 5/0;
            test.doTran2();
            tran.commit();
        } catch (Exception e) {
            e.printStackTrace();
            try {
                tran.rollback();
            } catch (Exception ex) {
                System.out.println("Rollback Exception: " + ex);
            }
        }
    }

//        public static void main(String[] args) {
//            UserTransactionXATest test = new UserTransactionXATest();
//            context = getInitialContext();
//            try {
//                test.doTran1();
//                test.doTran2();
//            } catch (Exception e) {
//                e.printStackTrace();
//            }
//        }

    public void doTran1() throws Exception {
        Connection hr1 = null;
        hr1 = getConnection("jdbc/hr1DS");
        //hr1 = getXAConnection("192.168.0.101");
        hr1.setAutoCommit(false);
        Statement stm1 = hr1.createStatement();
        stm1.execute("DELETE FROM jobs WHERE job_id='CEO1'");
        stm1.execute("INSERT INTO jobs VALUES('CEO1', 'CEO1', 1,2)");
        stm1.close();
        if (hr1 != null)
            hr1.close();
    }

    public void doTran2() throws Exception {
        Connection hr2 = null;
        hr2 = getConnection("jdbc/hr2DS");
        //hr2 = getXAConnection("192.168.0.103");
        hr2.setAutoCommit(false);
        Statement stm2 = hr2.createStatement();
        stm2.execute("DELETE FROM jobs WHERE job_id='CEO2'");
        stm2.execute("INSERT INTO jobs VALUES('CEO2', 'CEO2', 1,2)");
        stm2.close();
        if (hr2 != null)
            hr2.close();
    }

    private static Context getInitialContext() {
        try {
            Hashtable env = new Hashtable();
            env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
            env.put(Context.PROVIDER_URL, "t3://127.0.0.1:7001");
            //env.put(Context.SECURITY_PRINCIPAL, "weblogic");
            //env.put(Context.SECURITY_CREDENTIALS, "welcome1");
            return new InitialContext(env);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private static Connection getConnection(String jndi) throws Exception {
        DataSource ds = (DataSource)context.lookup(jndi);
        Connection conn = ds.getConnection();
        return conn;
    }

    public static Connection getXAConnection(String ip) throws Exception {
        OracleXADataSource oxaDS = new OracleXADataSource();
        oxaDS.setDatabaseName("XE");
        oxaDS.setServerName(ip);
        oxaDS.setPortNumber(1521);
        oxaDS.setUser("hr");
        oxaDS.setPassword("hr");
        oxaDS.setURL("jdbc:oracle:thin:@" + ip + ":1521:XE");
        return oxaDS.getXAConnection().getConnection();
    }
}

(1)测试1:代码如上。
结果:不抛出异常:hr1和hr2的数据都成功正确插入各自的数据库库了。

(2)测试2: 暂停HR2 DataSource,这时会抛出异常:Caused by: java.sql.SQLException: Internal error: Cannot obtain XAConnection weblogic.common.resourcepool.ResourceDisabledException: Pool HR2 JDBC Data Source is Suspended, cannot allocate resources to applications.

结果:hr1和hr2的数据都没有插入各自的数据库库了,和我们预期的一样。 因为doTran2()方法抛出异常,导致doTran1()中的事务回滚了。

这时,笔者有一个疑问,如果不使用UserTransaction会怎样呢?
(3)测试3:在测试2的基础上,注释main函数(使用了UserTransaction),打开另一个main函数(没有使用UserTransaction)。
结果:hr1和hr2的数据依然没有插入各自的数据库库了, doTran2()方法抛出异常,依然导致doTran1()中的事务回滚了。
似乎使用UserTransaction和不使用UserTransaction没有什么区别。

(4)测试4:在测试3的基础上,设置doTran1和doTran2中的setAutoCommit(true);,取代原来的设置false。
结果:hr1的数据插入数据库库了,而hr2的数据没有插入数据库。虽然doTran2()方法抛出异常,但并没有影响doTran1()中的事务。

(5)测试5:在测试4的基础上,注释main函数(没有使用UserTransaction),打开另一个main函数(使用了UserTransaction)。
结果:hr1和hr2的数据都没有插入各自的数据库库了,和我们预期的一样。
doTran2()方法抛出异常,导致doTran1()中的事务回滚了。也就是说,UserTransaction的全局事务还是起作用了。

参考文献:
1. http://www.oschina.net/question/234345_51230

2009年3月28日星期六

WLS_066:设置transport-guarantee=CONFIDENTIAL保护敏感资源

开发运行环境:WebLogic 12.1.1 开发版

本文最后一次修改日期:2013-06-08

为了保护Web应用中的敏感数据,防止资源的非法访问和保证传输的安全性,Java Servlet 2.2规范定义了安全约束(Security-Constraint)元件,它用于指定一个或多个Web资源集的安全约束条件;用户数据约束(User-Data-Constraint)元件是安全约束元件的子类,它用于指定在客户端和容器之间传输的数据是如何被保护的。
用户数据约束元件还包括了传输保证(Transport-Guarantee)元件,它规定了客户机和服务器之间的通信必须是以下三种模式之一:NONE、INTEGRAL、CONFIDENTIAL。NONE表示被指定的Web资源不需要任何传输保证;Integral表示客户机与服务器之间传送的数据在传送过程中不会被篡改;Confidential表示数据在传送过程中被加密。大多数情况下,INTEGRAL或CONFIDENTIAL是使用SSL实现。

本文就是通过实验验证当设置transport-guarantee=CONFIDENTIAL后,原来被保护的资源,将自动从HTTP协议,转到HTTPS协议。
当然,前提是你已经配置了SSL,关于如何配置SSL请参考《WebLogic Server基本管理之十:配置单向SSL认证》和《WebLogic Server高级管理之九:配置双向SSL认证》。

1. 配置SSL

2. 在web.xml中设置transport-guarantee=CONFIDENTIAL
这里使用《JMeter_003:压力测试指南之三:测试Form安全认证的Web应用》中用到的HelloFormAuth.war应用。

3. 重新发布应用
访问:http://localhost:7001/HelloFormAuth/index.jsp,会发现首先提示“网站证书有问题”,确定风险并继续后,请求将自动Redirect到https://localhost:7002/HelloFormAuth/index.jsp。
这说明设置transport-guarantee=CONFIDENTIAL后,原来被保护的资源,将自动从HTTP协议转到HTTPS协议。

参考文献:
1. http://wenku.baidu.com/view/1c48b00a76c66137ee0619c5.html
2. http://blog.sina.com.cn/s/blog_68ef797801016f9z.html HTTPS
3. http://blog.mc-thias.org/?title=tomcat_and_ssl_redirection&more=1&c=1&tb=1&pb=1
4. http://stackoverflow.com/questions/7790141/security-constraint-configuration-inside-web-xml-for-tomcat

2009年3月27日星期五

WLS_065:使用WebLogic Security API获取Users和Groups

开发运行环境:Weblogic 12.1.1 开发版
本文最后一次修改日期:2013-05-11

import java.security.Principal;

import java.util.Set;

import javax.naming.Context;

import javax.security.auth.Subject;

import weblogic.jndi.Environment;

import weblogic.security.Security;
import weblogic.security.principal.WLSGroupImpl;
import weblogic.security.principal.WLSUserImpl;

public class WebLogicSecurityUtils {

    public static void main(String[] args) {
        try {
            Environment env = new Environment();
            env.setProviderUrl("t3://localhost:7001");
            env.setSecurityPrincipal("john");
            env.setSecurityCredentials("welcome1");
            Context ctx = env.getInitialContext();

            Subject subject = Security.getCurrentSubject();
            Set allPrincipals = subject.getPrincipals();
            for (Principal principal : allPrincipals) {
                if (principal instanceof WLSGroupImpl) {
                    System.out.println("Group: " + principal.getName());
                }
                if (principal instanceof WLSUserImpl) {
                    System.out.println("User: " + principal.getName());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

输出如下:
User: john
Group: Administrators
Group: employees

说明:这里我把用户john加在了两个组中:Administrator和employees。

参考文献:
1. http://blog.whitehorses.nl/2010/01/29/weblogic-web-application-container-security-part-1/

2009年3月26日星期四

WLS_064:使用JMX获取Users和Groups

开发运行环境:Weblogic 12.1.1 开发版
本文最后一次修改日期:2013-05-03

import javax.naming.*;
import weblogic.jndi.Environment;

import weblogic.security.providers.authentication.DefaultAuthenticatorMBean;
import weblogic.management.MBeanHome;
import weblogic.management.security.authentication.AuthenticationProviderMBean;
import weblogic.management.security.authentication.*;

public class WebLogicJMXUtils {
    public WebLogicJMXUtils() {
        super();
    }

    public static void main(String[] args) {
        MBeanHome home = null;
        try {
            Environment env = new Environment();
            env.setProviderUrl("t3://localhost:7001");
            env.setSecurityPrincipal("weblogic");
            env.setSecurityCredentials("welcome1");
            Context ctx = env.getInitialContext();
            home = (MBeanHome)ctx.lookup(MBeanHome.ADMIN_JNDI_NAME);

            weblogic.management.security.RealmMBean rmBean =
                home.getActiveDomain().getSecurityConfiguration().getDefaultRealm();

            AuthenticationProviderMBean[] authenticationBeans = rmBean.getAuthenticationProviders();
            DefaultAuthenticatorMBean defaultAuthenticationMBean = (DefaultAuthenticatorMBean)authenticationBeans[0];
            UserReaderMBean userReaderMBean = (UserReaderMBean)defaultAuthenticationMBean;
            GroupReaderMBean groupReaderMBean = (GroupReaderMBean)defaultAuthenticationMBean;
            UserRemoverMBean userRemoverMBean = (UserRemoverMBean)defaultAuthenticationMBean;
            GroupRemoverMBean groupRemoverMBean = (GroupRemoverMBean)defaultAuthenticationMBean;
//            userRemoverMBean.removeUser("");
//            groupRemoverMBean.removeGroup("");
       
            String userCurName = userReaderMBean.listUsers("*", 100);
            while (userReaderMBean.haveCurrent(userCurName)) {
                String user = userReaderMBean.getCurrentName(userCurName);
                System.out.println("\n User: " + user);
                userReaderMBean.advance(userCurName);
            }

            String cursorName = groupReaderMBean.listGroups("*", 100);
            while (groupReaderMBean.haveCurrent(cursorName)) {
                String group = groupReaderMBean.getCurrentName(cursorName);
                System.out.println("\n Group: " + group);
                groupReaderMBean.advance(cursorName);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行程序,输出如下:

User: OracleSystemUser
User: weblogic
User: john
User: joe
User: ted
User: mary
User: albert
Group: AdminChannelUsers
Group: Administrators
Group: AppTesters
Group: CrossDomainConnectors
Group: Deployers
Group: Monitors
Group: Operators
Group: OracleSystemGroup
Group: employees
Group: managers

说明:MBeanHome已经不建议使用,官方文档的说明如下:
As of 9.0, the MBeanHome interface is deprecated.
Instead of using this API-like programming model, all JMX applications should use the standard JMX programming model, in which clients use the javax.management.MBeanServerConnection interface to discover MBeans, attributes, and attribute types at run time.
In this JMX model, clients interact indirectly with MBeans through the MBeanServerConnection interface.

参考文献:
1. http://weblogic-wonders.com/weblogic/2010/11/10/list-users-and-groups-in-weblogic-using-jmx/
2. http://dead-knight.iteye.com/blog/853008
3. http://www.360doc.com/content/10/1005/14/39755_58581727.shtml
4. http://www.360doc.com/content/10/1005/14/39755_58582655.shtml
5. http://www.ibm.com/developerworks/cn/java/j-lo-jse63/
6. http://java.e800.com.cn/articles/2007/11/1167663893665537991_1.html
7. http://docs.oracle.com/cd/E21764_01/web.1111/e13754/compat.htm

2009年3月25日星期三

WLS_063:Session Timeout的设置

运行开发环境:WebLogic Server 12c开发版(12.1.1.1.0) + OEPE 12c(12.1.1.1.0)

配置Session Timeout的地方有两个:web.xml和weblogic.xml。
为了更清楚地了解Sesstion何时Timeout,我使用了一个SessionListener监听Session的创建和失效时间。

1. 在web.xml中配置Session Timeout,单位是分钟。
<session-config>
    <session-timeout>1</session-timeout>
</session-config>

运行index.jsp,Console输出如下:
%%%%%%%%%% Session Created at Thu Oct 18 11:19:06 CST 2012
%%%%%%%%%% Session Destroyed at Thu Oct 18 11:19:07 CST 2012
%%%%%%%%%% Session Created at Thu Oct 18 11:19:13 CST 2012
%%%%%%%%%% Session Destroyed at Thu Oct 18 11:21:10 CST 2012
%%%%%%%%%% name = username,  value= ssouser

可以发现一个比较奇怪的事实:Session的生命周期比设置的约长了1分钟,大约2分钟。

2.  在weblogic.xml中配置Session Timeout,单位是秒。
去掉web.xml中Session Timeout的配置,只在weblogic.xml中配置如下:
<wls:session-descriptor>
    <wls:timeout-secs>180</wls:timeout-secs>
</wls:session-descriptor>

运行index.jsp,Console输出如下:
%%%%%%%%%% Session Created at Thu Oct 18 11:44:39 CST 2012
%%%%%%%%%% Session Created at Thu Oct 18 11:44:48 CST 2012
%%%%%%%%%% Session Destroyed at Thu Oct 18 11:44:48 CST 2012
%%%%%%%%%% Session Destroyed at Thu Oct 18 11:48:37 CST 2012
%%%%%%%%%% name = username,  value= ssouser

可以发现Session的生命周期比设置的还是约长了1分钟,大约4分钟。

3. 进一步说明
(1)如果在web.xml和weblogic.xml中都配置了Session Timeout,将以web.xml中配置为准。
(2)网上有人说,在web.xml中设置session-timeout为-2,表示将使用weblogic.xml中的设置;设置session-timeout为-1,表示Session将永不过期,忽略weblogic.xml中的设置。
但我的实验结果是:只要设置session-timeout为负值,Session似乎就会永不过期。

4. 结论
(1)不要在web.xml和weblogic.xml中同时配置Session Timeout,容易引起混淆。
(2)设置Session永不过期也不是一个好的方案,还是按实际需要设置。

Project 下载:SecurityWithSessionWeb.7z
参考文献:
1. http://blog.csdn.net/e_wsq/article/details/7555667

2009年3月24日星期二

WLS_062:虚拟目录的设置


运行开发环境:WebLogic Server 12c开发版(12.1.1.1.0) + OEPE 12c(12.1.1.1.0)

1. 修改weblogic.xml文件,增加virtual-directory-mapping
<wls:virtual-directory-mapping>
<wls:local-path>C:/Users/Public/Pictures/Sample Pictures</wls:local-path>
<wls:url-pattern>/images/*</wls:url-pattern>
<wls:url-pattern>*.jpg</wls:url-pattern>
</wls:virtual-directory-mapping>
含义如下:当访问 http://[app_name]/images/[image_name].jpg时,会到C:/Users/Public/Pictures/Sample Pictures/images目录下找[image_name].jpg文件。

2. 修改index.jsp,增加img
<img alt="" src="./images/57996_1600x1200-wallpaper-cb1344523063.jpg">

3. 运行index.jsp

Project 下载:FastSwap(Virtual_Directory).7z

2009年3月23日星期一

WLS_061:FastSwap特性

运行开发环境:WebLogic Server 12c开发版(12.1.1.1.0) + OEPE 12c(12.1.1.1.0)

FastSwap特性可以动态装载修改后的Java类而无需重新发布应用,显然这能够大大提升开发效率。

1. 创建EAR Application Project:FastSwapApp
(1)
 (2)

2. 创建Dynamic Web Project:FastSwapWeb
 (1)
 (2)选择Add Project to an EAR

(3)index.jsp代码
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<%@ page import="sessionbean.AccountBean" %>

<%
AccountBean bean;
if(session.getAttribute("bean") == null){
bean = new AccountBean();
session.setAttribute("bean",bean);
}else{
bean = (AccountBean)session.getAttribute("bean");
session.setAttribute("balance",bean.getBalance());
}
bean.deposit(100);
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
Current Balance: <%= bean.getBalance() %>
</body>
</html>

3. 创建EJB Project:EJBProject
 (1)选择Add Project to an EAR
 (2)选择Add Project to an EAR

(3)EJB代码
package sessionbean;

import static javax.ejb.TransactionAttributeType.*;

import javax.ejb.Stateful;
import javax.ejb.TransactionAttribute;
import javax.ejb.Remote;
import javax.ejb.EJB;

import javax.annotation.PreDestroy;

import javax.interceptor.Interceptors;
import javax.interceptor.ExcludeClassInterceptors;
import javax.interceptor.InvocationContext;

/**
 * Bean file that implements the Account business interface. Uses the following
 * EJB annotations: - @Stateful: specifies that this is a stateful session EJB - @TransactionAttribute
 * - specifies that this EJB never runs in a transaction - @Remote - specifies
 * the Remote interface for this EJB - @EJB - specifies a dependency on the
 * ServiceBean stateless session ejb - @Interceptors - Specifies that the bean
 * file is associated with an Interceptor class; by default all business methods
 * invoke the method in the interceptor class annotated with @AroundInvoke. - @ExcludeClassInterceptors
 * - Specifies that the interceptor methods defined for the bean class should
 * NOT fire for the annotated method. - @PreDestroy - Specifies lifecycle method
 * that is invoked when the bean is about to be destoryed by EJB container.
 * 
 */

@Stateful
@TransactionAttribute(NEVER)
@Remote({ sessionbean.Account.class })
@Interceptors({ sessionbean.AuditInterceptor.class })
public class AccountBean implements Account {

private int balance = 0;

public void deposit(int amount) {
balance += amount;
System.out.println("deposited: " + amount + " balance: " + balance);
}

public void withdraw(int amount) {
balance -= amount;
System.out.println("withdrew: " + amount + " balance: " + balance);
}

public int getBalance() {
int bogus;
return balance;
}

@ExcludeClassInterceptors
public void sayHelloFromAccountBean() {

}

@PreDestroy
public void preDestroy() {
System.out.println("Invoking method: preDestroy()");
}

}


4. 修改FastSwapWeb Project的Deployment Assembly
(1)右键FastSwap项目,选择属性,选择Deployment Assembly
(2)点击Add,选择 Project
(3)选择EJB Project和EJBProjectClient
(4)添加完成后,EJBProject.jar和EJBClient.jar添加到WEB-INF/lib目录下。

 5. 右键index.jsp,选择Run As,Run on Server
(1)显示Balance值。
(2)刷新页面,会以100递增Balance值。
(3)修改AccountBean.java,修改方法getBalance:return balance + 1;
(4)刷新页面,发现仍以100递增Balance值,也就是没有使用修改后的Java类。

(5)选择Server Tab,Publish应用。
(6)刷新页面,这次发现修改起作用了。

(7)在FastSwapApp Prjoect中,找到META-INF\weblogic-application.xml,选中FashSwap,勾上Enable class redefinition,然后重新发布应用。
(8) 再次修改AccountBean.java,修改方法getBalance:return balance + 2; 。
(9)无需重新发布应用,直接刷新页面,发现使用了修改后的Java类,说明FashSwap起作用了。

Project 下载:FastSwap.7z

2009年3月22日星期日

WLS_060:不同的Web应用如何共享Session?

WLS_060:不同的Web应用之间如何共享Session?

开发环境:WebLogic Server 12c开发版。
WebLogic Server支持同一个ear中不同的Web应用共享Session,详见《同一个ear中不同的Web应用如何共享Session?》,但是如果是不在一个ear中Web应用如何共享Session呢?
回答是把session对象放到ServletContext中。

值得注意的是这种用法不可移植,这是WebLogic自己的特性,不是规范中的。
因为根据规范,使用ServletContext.getContext("/appA")时,应用服务器可以出于安全的原因而返回null。

重点步骤说明:

1. shoppingcart1.war
(1)修改shoppingCart.java,在service方法后面增加

// put session into shoppingcart1's ServletContext      
        ServletContext context = getServletConfig().getServletContext();
        context.setAttribute("shoppingCartApp1", session);
说明:获取到ServletContext 对象后,把整个session对象放到ServletContext 中。

(2)修改viewShoppingCart.java,在service方法后面增加

// get session from shoppingcart2's ServletContext
ServletContext context =  getServletConfig().getServletContext();
ServletContext context2 = context.getContext("/shoppingcart2");
if (context2 != null) {
HttpSession session2 = (HttpSession)context2.getAttribute("shoppingCartApp2");
System.out.println("############### get session from shoppingcart2's ServletContext: " + session2);
     
if (session2 != null){
    Vector scitems1 = (Vector) session2.getAttribute("cart");
   if (scitems1 == null)
   {
    System.out.println("Nothing in shopping cart");
   } else {
     Enumeration enum = scitems1.elements();
     System.out.println("Your shopping cart includes: ");
       while (enum.hasMoreElements())
       {
             shoppingCartItem item = (shoppingCartItem) enum.nextElement();
             System.out.println("\tItem: " + item.getName() + " price: " + item.getPrice());
}
   }
}
}
}
说明:获取到/shoppingcart2的context后,再获取shoppingcart2应用的session对象,并循环遍历。

2. shoppingcart2.war
仿照shoppingcart1.war做相应修改。

3. shoppingcart.jar
把shoppingcart1.war 和shoppingcart2.war 中保存在session中的对象shoppingCartItem单独拿出来。
打成jar,放到[domain_name]\lib目录下。

4. 运行效果
(1)访问http://localhost:7001/shoppingcart1/,购买一些商品。
(2)访问http://localhost:7001/shoppingcart2/,查看购买的商品,发现看到了在shoppingcart1/购买的商品。说明shoppingcart2访问到了shoppingcart1的session对象。

Project 下载:shoppingcart(ServletContextSessionShare).7z

参考文献:
1. http://blog.csdn.net/yousuf007/article/details/5252604
2. http://johnchina.blog.51cto.com/4390273/922071
3. http://www.cnblogs.com/lcuzhanglei/archive/2012/06/11/2545240.html
4. http://energykey.iteye.com/blog/701721
5. http://panyongzheng.iteye.com/blog/1263411
6. http://java8988.iteye.com/blog/1174959

2009年3月21日星期六

WLS_059:JDBC Drivers 应该放在哪里?

开发环境:WebLogic Server 12c开发版。

WebLogic Server 12c默认安装了Oracle和MySQL数据库的JDBC Drivers。
如果要支持其它数据库,就要安装其它数据库的JDBC Drivers。
经过实验,把JDBC Drivers jar文件放到[domain_name]\lib下是不行的,会报告找不到JDBC Drivers Class。

经过查看文档,发现放置JDBC Drivers的步骤如下:

1. 把JDBC Drivers jar文件复制到[wls_home]\wlserver\server\ext\jdbc\[dbms_name]目录下。
存放在这里的JDBC Drivers jars只是作为一个备份的地方,你可以为所有可能用到的DBMS都建立一个目录。
其中,WebLogic默认已经有了oracle和mysql两个目录,其中包含各个版本的JDBC Drivers jars。

2. 把你要使用的JDBC Drivers jar文件从[wls_home]\wlserver\server\ext\jdbc\[dbms_name]目录下复制到[wls_home]\wlserver\server\lib\目录下。

3. 修改[wls_home]\wlserver\server\lib\weblogic.jar中的META-INF目录下的MANIFEST.MF内容。
weblogic.jar是WebLogic Server启动时会首先装载的jar,因此它用到的jar也会随之装载。
MANIFEST.MF定义了weblogic.jar要用到的jars,在其中增加你刚刚增加的JDBC Drivers jar。
下面是MANIFEST.MF默认的内容,可以看出它已经包含了很多JDBC Drivers jars:ojdbc6.jar、
wlsqlserver.jar、 wldb2.jar、 wlsybase.jar、 wlinformix.jar、 mysql-connector-java-commercial-5.1.17-bin.jar。

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.7.1
Created-By: R28.2.0-79-146777-1.6.0_29-20111005-1808-windows-ia32 (Ora
cle Corporation)
Main-Class: weblogic.Server
Implementation-Vendor: BEA Systems
Implementation-Title: WebLogic Server 12.1.1.0 Wed Dec 7 08:40:57 PST
2011 1445491
Implementation-Version: 12.1.1.0
Class-Path: ../../../modules/features/weblogic.server.modules_12.1.1.0.jar schema/weblogic-domain-binding.jar schema/weblogic-domain-binding-compatibility.jar schema/diagnostics-binding.jar schema/diagnostics -image-binding.jar wlcipher.jar webservices.jar xmlx.jar ojdbc6.jar ons.jar ucp.jar aqapi.jar EccpressoAsn1.jar EccpressoCore.jar EccpressoJcae.jar mysql-connector-java-commercial-5.1.17-bin.jar cryptoj.jar wlsqlserver.jar wldb2.jar wlsybase.jar wlinformix.jar fmwgenerictoken.jar wlw-langx.jar jcom.jar weblogic-L10N.jar

如果以上设置不管用,那就把JDBC Drivers jars直接放到CLASSPATH中。
比如直接修改脚本[wls_home]\wlserver\common\bin\commEnv.cmd或startweblogic.cmd中的CLASSPATH变量。
比如: set CLASSPATH=C:/mysql-connector-java-commercial-5.1.17-bin.jar ;%WEBLOGIC_CLASSPATH%;

参考文献:
1. http://docs.oracle.com/cd/E12839_01/web.1111/e13737/third_party_drivers.htm

2009年3月20日星期五

WLS_058:如何使用pack和unpack命令创建集群环境

单机版的WebLogic Server集群毕竟适用于实验环境,在测试和生产环境中一般还是会在多台机器上安装WebLogic Server,配置集群环境。
WebLogic Server提供了两个命令脚本可以帮助我们创建集群环境。
这里我使用A和B分别代表机器A和机器B,Admin Server创建在机器A上。

步骤说明:
1. 在机器A上安装WebLogic Server,并创建Domain。
就像配置单机版的WebLogic Server集群一样,创建和配置必要的集群对象,只不过IP地址要指向远程机器。
(1)创建集群中所有的Managed Server,使用真实有效的IP地址和端口。
(2)创建集群中所有的Machine,并把属于该Machine的Managed Server加。
(3)为集群中的每个Machine创建Node Manager。
(4)创建Cluster,加入所有Managed Server。
(5)创建其它需要Target到Cluster上的对象,比如Data Source、JMS。

2. 启动机器A上的Admin Server,确认一切正常。

3. 启动机器A上的Managed Server,确认一切正常。
(1)复制AdminServer目录下的security目录到ManagedServer目录下,这样启动ManagedServer时不用输入账户/口令。
(2)启动Managed Server,比如:
[domain_name]\bin\startManagedWebLogic [managed_server_name] t3://192.168.1.100:7001。

4. 在机器A执行pack命令把Domain打成一个jar包。
在[wls_home]\wlserver\common\bin目录下执行
pack -managed=true -domain=[在机器A上已创建的Domain目录] template=[要打成的jar包] -template_name=[domain_name]
例如:
pack -managed=true -domain=C:\Oracle\Middleware\user_projects\domains\dev_domain -template=C:\Temp\wls.jar -template_name=dev_domain

5. 在机器B上安装同一个版本的WebLogic Server
只完成安装即可,不用创建Domain。

6. 在机器B上执行unpack命令解压在机器A上打成的jar包
(1)把在机器A上打成的jar包复制到机器B的某个目录下,比如C:\Temp。
(2)在[wls_home]\wlserver\common\bin目录下执行
unpack -domain=[在机器B上要创建的Domain目录] -template=[在机器A上打成的jar包]
例如:
unpack -domain=C:\Oracle\Middleware\user_projects\domains\dev_domain -template==C:\Temp\wls.jar
这样,就会在机器B的C:\Oracle\Middleware\user_projects\domains\dev_domain目录下创建Domain。

7. 启动机器B上的Managed Server,确认一切正常。
(1)复制AdminServer目录下的security目录到ManagedServer目录下,这样启动ManagedServer时不用输入账户/口令。
(2)启动Managed Server,比如:
[domain_name]\bin\startManagedWebLogic [managed_server_name] t3://192.168.1.100:7001。

8. 查看WLS Console控制台,确认一切正常。

9. 使用控制台管理Managed Server。
(1)启动机器A上的Node Manager。
(2)启动机器B上的Node Manager。
在控制台中,启动/停止Managed Server,看看是否一切正常。

参考文献:
1. http://middlewaremagic.com/weblogic/?p=7795

2009年3月19日星期四

WLS_057:如何安装和使用Shared Library?

本文最后一次修改时间:2012-05-13。
开发环境:WebLogic Server 12c开发版。

如果多个Web应用使用一些共同的Jar包,应该把它们放到一个公共的地方,而不要把这些Jar打入每个Web应用。
其缺点如下:
(1)如果Jar包很多,那么每个Web应用的size会比较大。
(2)如果修改了Jar包,那么要重新为每个Web应用打包,并重新发布,不利于管理和维护。
(3)可能会引起CLassCastException异常,比如上一篇文章遇到的情况。
把这些Jar包放到[domain_name]\lib目录下,当然可以解决这个问题,但是这个解决方案还不够“优美”。
因为[domain_name]\lib是存放全局Library的地方,除非所有的应用都会使用到才会放在这里。
如果只是几个Web应用会使用到,一般不建议放在这里。
那么,该如何解决这个问题呢?
WebLogic Server提供了一个Shared Library功能,可以完美的解决这个问题。

我以上一篇文章的实验为例来说明如何安装和使用Shared Library。

1. 安装Shared Library:shoppingcart.jar
首先要修改一下shoppingcart.jar,把shoppingcart.jar解压缩后,创建目录META-INF和MANIFEST.MF文件。
其中MANIFEST.MF内容如下:
Extension-Name: shoppingcartSharedLib
Specification-Version: 1.0
Implementation-Version: 1.0

然后执行如下脚本,重新把java类和目录META-INF打成Jar包:shoppingcart.jar:
jar cvfm shoppingcart.jar META-INF/MANIFEST.MF *
其中目录的完整结构如下图所示。

然后运行如下脚本,把shoppingcart.jar作为Shared library发布到WebLogic Server上:
java weblogic.Deployer -adminurl t3://localhost:7001 -user weblogic -password welcome1 -deploy -library shoppingcart.jar
发布成功了,在WLS Console中可以看到该Library。


2. 修改shoppingcart.ear,并重新发布
因为在本例中是一个ear包中有多个war包,并且每个war包使用相同的Jar包。
因此修改Application级的META-INF目录下的weblogic-application.xml文件就可以了。
这里主要是引用Shared Library:
<?xml version='1.0' encoding='utf-8'?>
<weblogic-application xmlns="http://www.bea.com/ns/weblogic/90" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<session-descriptor>
<persistent-store-type>memory</persistent-store-type>
<sharing-enabled>true</sharing-enabled>
</session-descriptor>
<library-ref>
<library-name>shoppingcartSharedLib</library-name>
</library-ref>
</weblogic-application>
如果是每个War包引用的Jar包不同,那就要War包中的weblogic.xml(这是我推理得出的,有待验证)。
重新打成ear包后,重新发布。

3. 测试
如果[domain_name]\lib目录下有shoppingcart.jar,请删除掉,因为已经发布了Shared Library,所以不需要在这里再放了。
重新启动WLS后,重新访问应用shoppingcart1和shoppingcart2。
(1)访问http://localhost:7001/shoppingcart1/,添加一些商品。
(2)访问http://localhost:7001/shoppingcart2/,添加另一些商品。
(3)分别在shoppingcart1和shoppingcart2查看Session中的信息,发现显示了所有的商品信。
这说明实验结果和上一篇文章一样,Shared Library起作用了。

以后如果需要修改Jar包,只需要重新发布Jar包,无需重新发布应用。
这真是一个完美的解决方案!

Project 下载:shoppingcart(SharedLibrary).7z

参考文献:
1. https://forums.oracle.com/forums/thread.jspa?threadID=915004
2. http://java.chinaitlab.com/core/39096.html
3. http://geekerdever.wordpress.com/2010/09/07/将ADF应用部署为Shared-Library/

2009年3月18日星期三

WLS_056:同一个ear中不同的Web应用如何共享Session?

本文最后一次修改时间:2012-02-23。
开发环境:WebLogic Server 12c开发版。

Web规范中规定每个Web应用的Session信息彼此是不能共享的,但是在开发Web应用时,有时我们希望多个Web应用之间可以共享Session信息。
WebLogic 允许同一个ear中的多个Web应用共享Session信息。注意,这是WebLogic自己的特性,不是规范中的。

下面我们以shoppingcart.war为例,来说明如何实现。

1. 把shoppingcart.war复制两份,分别命名为shoppingcart1.war和shoppingcart2.war

2. 把shoppingcart1.war和shoppingcart2.war打到一个ear中:shoppingcart.ear
修改ear包中的META-INF下的application.xml内容如下:
<?xml version='1.0' encoding='utf-8'?>
<application xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.4">
  <display-name></display-name>
  <module>
    <web>
      <web-uri>shoppingcart1.war</web-uri>
      <context-root>shoppingcart1</context-root>
    </web>
  </module>
  <module>
    <web>
      <web-uri>shoppingcart2.war</web-uri>
      <context-root>shoppingcart2</context-root>
    </web>
  </module>
</application>

将ear包中的META-INF下的weblogic-application.xml内容置空:
因为我们首先要确定默认配置下,应用可以正常访问,然后再增加共享Session配置。
<?xml version='1.0' encoding='utf-8'?>
<weblogic-application xmlns="http://www.bea.com/ns/weblogic/90" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
</weblogic-application>


3. 部署并访问应用shoppingcart1和shoppingcart2
(1)http://localhost:7001/shoppingcart1/访问正常,购买12支黑色钢笔放入Session中。

(2)http://localhost:7001/shoppingcart2/访问正常,购买12支蓝色钢笔放入Session中。

可以看出,shoppingcart1和shoppingcart2各自访问正常,但Session信息没有共享。

4. 修改weblogic-application.xml,并重新部署
修改ear包中的META-INF下的weblogic-application.xml内容如下:
<?xml version='1.0' encoding='utf-8'?>
<weblogic-application xmlns="http://www.bea.com/ns/weblogic/90" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <session-descriptor> 
    <persistent-store-type>memory</persistent-store-type> 
    <sharing-enabled>true</sharing-enabled> 
  </session-descriptor> 
</weblogic-application>

访问http://localhost:7001/shoppingcart1/或http://localhost:7001/shoppingcart2/,
抛出如下异常:
java.lang.ClassCastException: com.servlets.shoppingCartItem cannot be cast to com.servlets.shoppingCartItem
这是为什么呢?
原来是因为一个ear中有两个war包,但是由不同的ClassLoader分别装载,war包中的类在各自Classloader中。
也就是说,虽然在本实验中两个war包中有一摸一样的类,但是JVM会认为它们是不一样的类,因此会报出上面的错误。

5. 把两个war包中相同的类拿出来,打成jar包,放到[domain_name]\lib目录下,再重新部署ear包。
(1)把shoppingcart1.war和shoppingcart2.war包中相同的类解压出来,打成jar包:shoppingcart.jar。
(2)删除shoppingcart1.war和shoppingcart2.war包中相同的类,重新打成ear包。
(3)把shoppingcart.jar复制到[domain_name]\lib目录下。
(4)重新启动WebLogic Server,重新部署shoppingcart.ear。

6. 重新访问应用shoppingcart1和shoppingcart2。
(1)访问http://localhost:7001/shoppingcart1/,添加一些商品。
(2)访问http://localhost:7001/shoppingcart2/,添加另一些商品。
(3)分别在shoppingcart1和shoppingcart2查看Session中的信息,发现显示了所有的商品信息。


这说明同一个ear中不同的Web应用共享Session信息配置成功。

Project 下载:shoppingcart(2WarSessionShare).7z

参考文献:
1. http://hi.baidu.com/listlofusage/blog/item/d7568f31ac7d00ac5edf0ef1.html
2. https://forums.oracle.com/forums/thread.jspa?threadID=1010819
3. http://objectmix.com/weblogic/528128-sharing-session-across-web-applications-wl8-1-a.html
4. http://alexsunny.iteye.com/blog/41836
5. http://blog.csdn.net/yousuf007/article/details/5252604
6. http://bbs.middleware123.com/thread-705-1-1.html
7. http://www.renren.it/a/fuwuqiruanjian/Jboss/20100926/24584.html
8. http://www.iteye.com/topic/1117133

2009年3月17日星期二

WLS_055:使用Sunone IPlanet LDAP作为Authentication Provider

本文最后一次修改时间:2012-02-08。
开发环境:WebLogic Server 12c + Sunone LDAP Server 5.2

1. 理解Control Flag含义
Authentication provider使用Control Flag 来决定是否调用其它Authentication provider。
  • REQUIRED(必需):认证必须成功,如果失败,则登录宣告失败。但无论成功与否,都会调用下一个认证提供者。

  • REQUISITE(必要):认证必须成功,如果失败,则登录宣告失败。只有成功才会调用下一个认证提供者。

  • SUFFICIENT(足够):认证不必成功,如果成功,立即返回;如果失败,调用下一个认证提供者。

  • OPTIONAL(可选):认证不必成功。但无论成功与否,都会调用下一个认证提供者。
说明:Authentication provider 验证的次序可以在Console中点击Reorder来调整。

2. 重点步骤说明骤
(1)

(2)增加一个新的Authentication Provider,Type选择IPlanetAuthenticator。

(3)设置新增的Sunone IPlanet Authentication provider的Control Flag为SUFFICIENT。

(4)IPlanet LDAP详细配置信息如下:
Host:192.1681.1.101
Port:389
Principal:cn=Directory Manager
Principal:welcome1
User Base DN:ou=people,dc=cn,dc=oracle,dc=com
User From Name Filter:(&(uid=*)(objectclass=person))
User Name Attribute = uid
Use Retrieved User name as Principal:选中
Group Base DN:ou=groups,dc=cn,dc=oracle,dc=com
Group From Name Filter:((&(cn=*)(objectclass=groupofUniqueNames))(&(cn=%g)(objectclass=groupOfURLs)))

(5)把Sunone IPlanet Authentication provider Recorder到最上面。

配置完毕后,保存并重新启动WebLogic Server。

3. 在LDAP中增加用户
在WLS Console中不能增加用户/组到LDAP中,所以只能在LDAP中手工增加。
在WLS Console中可以看到LDAP中的用户和组信息。
用户:

组:


参考文献:
1. http://www.oracle.com/technology/products/jdev/tips/fnimphius/oidconfig/index.html
2. http://blogs.oracle.com/jamesbayer/2007/08/using_openldap_with_weblogic_s.html
3. http://www.360doc.com/content/05/0913/15/677_12305.shtml

WLS_054:安装Sunone IPlanet LDAP 5.2 (Windows)

本文最后一次修改时间:2012-02-08。
为了不影响本机环境,我使用的是VMWare虚机:Windows XP。

1. 设置机器名后缀:domain.com
因为Sunone要求机器名必须有后缀,而虚机不在任何一个域中,所以我人为加了机器的后缀名为domain.com。
(1)找到系统属性页,点击“更改”

(2)找到系统属性页,点击“其它”

(3)设置DNS后缀为:domain.com


2. 安装Sunone
这里必须输入带DNS后缀的机器全名称,否则Sunone安装可能会有问题。

因为是新建的Sunone Instance,这里选择第一个选项。

因为是新建的Sunone Instance,这里选择第一个选项。

设置LDAP Server Port:389

设置管理员:admin/welcome1

机器所在的域:

LDAP目录管理员:cn=Directory Manager/welcome1

管理Console的Port:390

安装成功后的日志信息:


3. 使用Sunone Console查看LDAP信息




4. 使用LDAP Browser连接查看信息

2009年3月16日星期一

WLS_053:WebLogic Server 10g 问与答

1. 如何查看WebLogic Server 的版本号?
[server_name]->Monitoring->General
这里不仅仅可以看到WebLogic Server 的版本信息,还可以看到Java的版本信息,操作系统的版本信息等等。

2. 如何设置WebLogic Server可用内存?
启动WebLogic Server前,设置环境变量USER_MEM_ARGS,例如:
(1)Windows:set USER_MEM_ARGS=-Xms512m -Xmx512m
(2)Linux:export USER_MEM_ARGS="-Xms512m -Xmx512m"
也可根据情况,增加其它内存参数,如USER_MEM_ARGS="-Xms512m -Xmx1024m -XX:MaxPermSize=128m"。
然后再启动WebLogic Server实例:如 startManagedWebLogic.cmd mainserver。

3. WebLogic 9x 应用移植到10x上时,发布应用出现如下错误:
An error occurred during activation of changes, please see the log for details.
[Deployer:149189]Attempt to operate ''activate'' on null BasicDeploymentMBean for deployment MysqlDataSource. Operation can not be performed until server is restarted.

解决方案:把WebLogic10x的JDK版本从1.6改成1.5。

修改方法:找到Eclipse中配置服务器的位置(Windows--〉 reference-->MyEclipses-->Server或者Application Server-->WebLogic-->WebLogic10-->JDK),点击下拉列表右边的Add按钮,添加JDK5(可以使用WebLogic自带的JDK)即可。

本信息来源:《关于部署、配置基于WebLogic10应用时的一个错误

4. WebLogic 是否支持gzip请求和响应?

5. 如何把某个Web应用作为默认的应用,访问时不用输入Context root?修改weblogic.xml如下:
<weblogic-web-app><context-root>/</context-root></weblogic-web-app>

6. 如何修改WebLogic 使用的JVM?
修改setDomainEnv.cmd,设置环境变量JAVA_VENDOR。
比如 set JAVA_VENDOR=Sun 或 set JAVA_VENDOR=Oracle。

7. 能否定制化403和404错误页面?

8. Thread Dump时,经常发现weblogic.default.socketMuxer < size的异常,如何解决?

9. 系统增加了两台分发机器,同时增加了两台WebLogic Server,后台会不定期地报告找不数据库中的某张表或找不到某个表的某一列,每一次Reset 数据库连接后,错误消失,但下一次又会在某一台WebLogic Server上出现,如何解决?


10. 事务超时的设置能否按不同应用分别设置,而不是设置在Domain或Server上?

11. 日志的错误信息含有中文,如何让其显示英文(方便报告外国开发人员)?
在区域选项中,修改成:英文(美国),该修改无需重启。


或者修改\bin\startWeblogic.cmd启动脚本,在@REM Call setDomainEnv here行后增加一行:

set JAVA_OPTIONS=-Duser.language=en -Duser.country=US -Dsun.jnu.encoding=utf-8

要是Linux&Unix,在启动脚本命令行添加LANG变量或者LC_ALL变量(LC_ALL要比LANG作用域大):export LANG=en_US.utf-8

参考文献:
1. http://bbs.weblogicfans.net/viewthread.php?tid=2742