天天看點

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對象,不會緩存。

繼續閱讀