天天看点

ejb客户端的三种调用方法,以及InitialContext lookup后的jndi对象在服务重启后缓存失效的问题

1、第一种调用方法:

Properties p = new Properties();
		p.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
		p.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces");
		p.setProperty("java.naming.provider.url", "10.10.77.87:1199");
		
		InitialContext ctx = new InitialContext(p);
		CreditDirectPayRemote remote = (CreditDirectPayRemote) ctx.lookup("creditDirectPay/pay/remote");
           

在使用ejb客户端时,如果将jndi对象缓存起来,不用每次都lookup,这样可以节省一点效率,但是当ejb服务重启时,缓存的jndi对象将不能使用。

Properties p = new Properties();
		p.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
		p.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces");
		p.setProperty("java.naming.provider.url", "10.10.77.87:1199");
		
		InitialContext ctx = new InitialContext(p);
		CreditDirectPayRemote remote = (CreditDirectPayRemote) ctx.lookup("creditDirectPay/pay/remote");
				
		for(;;){
			try {
				System.out.println("第"+(++i)+"次:");
				rs= remote.directPay();
				Thread.sleep(1500);
			} catch (Exception e) {
				System.out.println("异常:"+e.getMessage());
				Thread.sleep(1000);
			}
		}	
           

将ejb服务重启后,报如下错误:

异常:Unreachable?: Service unavailable.

java.lang.RuntimeException: Unreachable?: Service unavailable.

at org.jboss.aspects.remoting.ClusterChooserInterceptor.invoke(ClusterChooserInterceptor.java:176)

at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)

at org.jboss.aspects.tx.ClientTxPropagationInterceptor.invoke(ClientTxPropagationInterceptor.java:67)

at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)

at org.jboss.aspects.security.SecurityClientInterceptor.invoke(SecurityClientInterceptor.java:53)

at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)

at org.jboss.ejb3.remoting.ClusteredIsLocalInterceptor.invoke(ClusteredIsLocalInterceptor.java:55)

at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)

at org.jboss.ejb3.stateless.StatelessClusteredProxy.invoke(StatelessClusteredProxy.java:112)

at com.sun.proxy.$Proxy0.directPay(Unknown Source)

at EjbTest.main(EjbTest.java:95)

所以,如果要支持ejb服务器的重启而不重启客户端应用,不能将客户端应用的jndi对象缓存。

2、第二种方法:在spring里,通过配置,可以支持缓存,也可以不支持缓存,配置如下:

<bean id="jndiTemplate"
		class="org.springframework.jndi.JndiTemplate">
		<property name="environment">
			<props>
				<prop key="java.naming.provider.url">  
                    ${java.naming.provider.url}   
                </prop>  
                <prop key="java.naming.factory.initial">  
                    ${java.naming.factory.initial}   
                </prop>  
                <prop key="java.naming.factory.url.pkgs">  
                    ${java.naming.factory.url.pkgs}   
                </prop>
			</props>
		</property>
	</bean>
<bean id="creditDirectPayRemote" class="org.springframework.jndi.JndiObjectFactoryBean">
		<property name="jndiTemplate" ref="jndiTemplate"/>
		<property name="cache" value="false"/>
		<property name="jndiName" value="creditDirectPay/pay/remote"/>
</bean>
<bean id="testBean" class="com.Test">
<property name="creditDirectPayRemote" ref="creditDirectPayRemote" />
</bean>
           

cache设为false就不缓存,每次用的时候重新lookup,否则,就会缓存。

分析spring源码如下:

JndiObjectFactoryBean

if (this.proxyInterfaces != null || !this.lookupOnStartup || !this.cache || this.exposeAccessContext) {
// We need to create a proxy for this...
if (this.defaultObject != null) {
throw new IllegalArgumentException(
"'defaultObject' is not supported in combination with 'proxyInterface'");
}
// We need a proxy and a JndiObjectTargetSource.
this.jndiObject = JndiObjectProxyFactory.createJndiObjectProxy(this);
}
else {
if (this.defaultObject != null && getExpectedType() != null &&
!getExpectedType().isInstance(this.defaultObject)) {
throw new IllegalArgumentException("Default object [" + this.defaultObject +
"] of type [" + this.defaultObject.getClass().getName() +
"] is not of expected type [" + getExpectedType().getName() + "]");
}
// Locate specified JNDI object.
this.jndiObject = lookupWithFallback();
}
}
		private static Object createJndiObjectProxy(JndiObjectFactoryBean jof) throws NamingException {
			// Create a JndiObjectTargetSource that mirrors the JndiObjectFactoryBean's configuration.
			JndiObjectTargetSource targetSource = new JndiObjectTargetSource();
			targetSource.setJndiTemplate(jof.getJndiTemplate());
			targetSource.setJndiName(jof.getJndiName());
			targetSource.setExpectedType(jof.getExpectedType());
			targetSource.setResourceRef(jof.isResourceRef());
			targetSource.setLookupOnStartup(jof.lookupOnStartup);
			targetSource.setCache(jof.cache);
			targetSource.afterPropertiesSet();

			// Create a proxy with JndiObjectFactoryBean's proxy interface and the JndiObjectTargetSource.
			ProxyFactory proxyFactory = new ProxyFactory();
			if (jof.proxyInterfaces != null) {
				proxyFactory.setInterfaces(jof.proxyInterfaces);
			}
			else {
				Class targetClass = targetSource.getTargetClass();
				if (targetClass == null) {
					throw new IllegalStateException(
							"Cannot deactivate 'lookupOnStartup' without specifying a 'proxyInterface' or 'expectedType'");
				}
				proxyFactory.setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, jof.beanClassLoader));
			}
			if (jof.exposeAccessContext) {
				proxyFactory.addAdvice(new JndiContextExposingInterceptor(jof.getJndiTemplate()));
			}
			proxyFactory.setTargetSource(targetSource);
			return proxyFactory.getProxy(jof.beanClassLoader);
		}
           

JndiObjectTargetSource类部分代码:如果cache为false cachedObject 就会一直是null,每次调用都会重新lookup

public Object getTarget() {
		try {
			if (this.lookupOnStartup || !this.cache) {
				return (this.cachedObject != null ? this.cachedObject : lookup());
			}
			else {
				synchronized (this) {
					if (this.cachedObject == null) {
						this.cachedObject = lookup();
					}
					return this.cachedObject;
				}
			}
		}
		catch (NamingException ex) {
			throw new JndiLookupFailureException("JndiObjectTargetSource failed to obtain new target object", ex);
		}
	}
           
lookup的调用

protected <T> T lookup(String jndiName, Class<T> requiredType) throws NamingException {
		Assert.notNull(jndiName, "'jndiName' must not be null");
		String convertedName = convertJndiName(jndiName);
		T jndiObject = null;
		try {
			jndiObject = getJndiTemplate().lookup(convertedName, requiredType);
		}
		catch (NamingException ex) {
			if (!convertedName.equals(jndiName)) {
				// Try fallback to originally specified name...
				if (logger.isDebugEnabled()) {
					logger.debug("Converted JNDI name [" + convertedName +
							"] not found - trying original name [" + jndiName + "]. " + ex);
				}
				jndiObject = getJndiTemplate().lookup(jndiName, requiredType);
			}
			else {
				throw ex;
			}
		}

public Object lookup(final String name) throws NamingException {
		if (logger.isDebugEnabled()) {
			logger.debug("Looking up JNDI object with name [" + name + "]");
		}
		return execute(new JndiCallback<Object>() {
			public Object doInContext(Context ctx) throws NamingException {
				Object located = ctx.lookup(name);
				if (located == null) {
					throw new NameNotFoundException(
							"JNDI object with [" + name + "] not found: JNDI implementation returned null");
				}
				return located;
			}
		});
	}

public <T> T execute(JndiCallback<T> contextCallback) throws NamingException {//每次都会创建一个新的InitialContext对象,然后lookup,最终关闭。
		Context ctx = getContext();
		try {
			return contextCallback.doInContext(ctx);
		}
		finally {
			releaseContext(ctx);
		}
	}
           

3、第三种方法

如果不使用spring注入的方式编写ejb的调用,可以直接引用spring的org.springframework.jndi.JndiTemplate类,如下:

Properties p = new Properties();
		p.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
		p.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces");
		p.setProperty("java.naming.provider.url", "10.10.77.87:1199");
					
JndiTemplate jndiTemplate = new JndiTemplate(p);
		Object remot = null;
		try {
			remot = jndiTemplate.lookup(jndi);
		} catch (NamingException e) {
			 
		}				
				
           

这也是每次都重新lookup jndi对象,不会缓存。