Restore outdated local/remote-slsb attributes for declaration compatibility

Legacy EJB attributes are ignored since 6.0 due to being bound to a plain JndiObjectFactoryBean - but can still be declared now, e.g. when validating against the common versions of spring-jee.xsd out there.

Closes gh-31627
This commit is contained in:
Juergen Hoeller 2023-11-20 21:01:36 +01:00
parent 54f87f1ff7
commit 695559879e
5 changed files with 163 additions and 13 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,12 +18,13 @@ package org.springframework.ejb.config;
import org.w3c.dom.Element;
import org.springframework.beans.BeanUtils;
import org.springframework.jndi.JndiObjectFactoryBean;
/**
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser}
* implementation for parsing '{@code local-slsb}' tags and
* creating plain {@link JndiObjectFactoryBean} definitions.
* creating plain {@link JndiObjectFactoryBean} definitions on 6.0.
*
* @author Rob Harrop
* @author Juergen Hoeller
@ -36,4 +37,10 @@ class LocalStatelessSessionBeanDefinitionParser extends AbstractJndiLocatingBean
return JndiObjectFactoryBean.class;
}
@Override
protected boolean isEligibleAttribute(String attributeName) {
return (super.isEligibleAttribute(attributeName) &&
BeanUtils.getPropertyDescriptor(JndiObjectFactoryBean.class, extractPropertyName(attributeName)) != null);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,12 +18,13 @@ package org.springframework.ejb.config;
import org.w3c.dom.Element;
import org.springframework.beans.BeanUtils;
import org.springframework.jndi.JndiObjectFactoryBean;
/**
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser}
* implementation for parsing '{@code remote-slsb}' tags and
* creating plain {@link JndiObjectFactoryBean} definitions.
* creating plain {@link JndiObjectFactoryBean} definitions as of 6.0.
*
* @author Rob Harrop
* @author Juergen Hoeller
@ -36,4 +37,10 @@ class RemoteStatelessSessionBeanDefinitionParser extends AbstractJndiLocatingBea
return JndiObjectFactoryBean.class;
}
@Override
protected boolean isEligibleAttribute(String attributeName) {
return (super.isEligibleAttribute(attributeName) &&
BeanUtils.getPropertyDescriptor(JndiObjectFactoryBean.class, extractPropertyName(attributeName)) != null);
}
}

View File

@ -95,7 +95,7 @@
</xsd:complexType>
</xsd:element>
<xsd:element name="local-slsb" type="jndiLocatingType">
<xsd:element name="local-slsb" type="ejbType">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.jndi.JndiObjectFactoryBean"><![CDATA[
Exposes a reference to a local EJB Stateless SessionBean.
@ -103,15 +103,56 @@
</xsd:annotation>
</xsd:element>
<xsd:element name="remote-slsb" type="jndiLocatingType">
<xsd:element name="remote-slsb">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.jndi.JndiObjectFactoryBean"><![CDATA[
Exposes a reference to a remote EJB Stateless SessionBean.
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="ejbType">
<xsd:attribute name="home-interface" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:java.lang.Class"><![CDATA[
The home interface that will be narrowed to before performing the
parameterless SLSB create() call that returns the actual SLSB proxy.
NOTE: Effectively ignored as of 6.0 in favor of plain JNDI lookups.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="refresh-home-on-connect-failure" type="xsd:boolean" default="false">
<xsd:annotation>
<xsd:documentation><![CDATA[
Controls whether to refresh the EJB home on connect failure.
NOTE: Effectively ignored as of 6.0 in favor of plain JNDI lookups.
Can be turned on to allow for hot restart of the EJB server.
If a cached EJB home throws an RMI exception that indicates a
remote connect failure, a fresh home will be fetched and the
invocation will be retried.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cache-session-bean" type="xsd:boolean" default="false">
<xsd:annotation>
<xsd:documentation><![CDATA[
Controls whether to cache the actual session bean object.
NOTE: Effectively ignored as of 6.0 in favor of plain JNDI lookups.
Off by default for standard EJB compliance. Turn this flag
on to optimize session bean access for servers that are
known to allow for caching the actual session bean object.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="jndiLocatingType">
<!-- base types -->
<xsd:complexType name="jndiLocatingType" abstract="true">
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:sequence>
@ -183,6 +224,40 @@
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="ejbType">
<xsd:complexContent>
<xsd:extension base="jndiLocatingType">
<xsd:attribute name="lookup-home-on-startup" type="xsd:boolean" default="true">
<xsd:annotation>
<xsd:documentation><![CDATA[
Controls whether the lookup of the EJB home object is performed
immediately on startup (if true, the default), or on first access
(if false).
NOTE: Effectively ignored as of 6.0 in favor of plain JNDI lookups.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cache-home" type="xsd:boolean" default="true">
<xsd:annotation>
<xsd:documentation><![CDATA[
Controls whether the EJB home object is cached once it has been located.
On by default; turn this flag off to always reobtain fresh home objects.
NOTE: Effectively ignored as of 6.0 in favor of plain JNDI lookups.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="business-interface" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:java.lang.Class"><![CDATA[
The business interface of the EJB being proxied.
NOTE: Effectively ignored as of 6.0 in favor of plain JNDI lookups.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:simpleType name="environmentRefType">
<xsd:annotation>
<xsd:appinfo>

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,9 +16,12 @@
package org.springframework.ejb.config;
import javax.naming.NoInitialContextException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.RuntimeBeanReference;
@ -29,6 +32,7 @@ import org.springframework.core.io.ClassPathResource;
import org.springframework.jndi.JndiObjectFactoryBean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* @author Rob Harrop
@ -93,6 +97,10 @@ public class JeeNamespaceHandlerTests {
BeanDefinition beanDefinition = this.beanFactory.getMergedBeanDefinition("simpleLocalEjb");
assertThat(beanDefinition.getBeanClassName()).isEqualTo(JndiObjectFactoryBean.class.getName());
assertPropertyValue(beanDefinition, "jndiName", "ejb/MyLocalBean");
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() -> this.beanFactory.getBean("simpleLocalEjb"))
.withCauseInstanceOf(NoInitialContextException.class);
}
@Test
@ -100,6 +108,32 @@ public class JeeNamespaceHandlerTests {
BeanDefinition beanDefinition = this.beanFactory.getMergedBeanDefinition("simpleRemoteEjb");
assertThat(beanDefinition.getBeanClassName()).isEqualTo(JndiObjectFactoryBean.class.getName());
assertPropertyValue(beanDefinition, "jndiName", "ejb/MyRemoteBean");
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() -> this.beanFactory.getBean("simpleRemoteEjb"))
.withCauseInstanceOf(NoInitialContextException.class);
}
@Test
public void testComplexLocalSlsb() {
BeanDefinition beanDefinition = this.beanFactory.getMergedBeanDefinition("complexLocalEjb");
assertThat(beanDefinition.getBeanClassName()).isEqualTo(JndiObjectFactoryBean.class.getName());
assertPropertyValue(beanDefinition, "jndiName", "ejb/MyLocalBean");
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() -> this.beanFactory.getBean("complexLocalEjb"))
.withCauseInstanceOf(NoInitialContextException.class);
}
@Test
public void testComplexRemoteSlsb() {
BeanDefinition beanDefinition = this.beanFactory.getMergedBeanDefinition("complexRemoteEjb");
assertThat(beanDefinition.getBeanClassName()).isEqualTo(JndiObjectFactoryBean.class.getName());
assertPropertyValue(beanDefinition, "jndiName", "ejb/MyRemoteBean");
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() -> this.beanFactory.getBean("complexRemoteEjb"))
.withCauseInstanceOf(NoInitialContextException.class);
}
@Test

View File

@ -40,14 +40,41 @@
</util:properties>
<!-- Local EJB Tests -->
<jee:local-slsb id="simpleLocalEjb" jndi-name="ejb/MyLocalBean"/>
<jee:local-slsb id="simpleLocalEjb" jndi-name="ejb/MyLocalBean"
business-interface="org.springframework.beans.testfixture.beans.ITestBean"/>
<jee:local-slsb id="complexLocalEjb"
jndi-name="ejb/MyLocalBean"
business-interface="org.springframework.beans.testfixture.beans.ITestBean"
cache-home="true"
lookup-home-on-startup="true"
resource-ref="true">
<jee:environment>foo=bar</jee:environment>
</jee:local-slsb>
<!-- Remote EJB Tests -->
<jee:remote-slsb id="simpleRemoteEjb" jndi-name="ejb/MyRemoteBean"/>
<jee:remote-slsb id="simpleRemoteEjb" jndi-name="ejb/MyRemoteBean"
business-interface="org.springframework.beans.testfixture.beans.ITestBean"/>
<!-- Lazy beans Tests-->
<jee:remote-slsb id="complexRemoteEjb"
jndi-name="ejb/MyRemoteBean"
business-interface="org.springframework.beans.testfixture.beans.ITestBean"
cache-home="true"
lookup-home-on-startup="true"
resource-ref="true"
home-interface="org.springframework.beans.testfixture.beans.ITestBean"
refresh-home-on-connect-failure="true"
cache-session-bean="true">
<jee:environment>foo=bar</jee:environment>
</jee:remote-slsb>
<!-- Lazy Lookup Tests-->
<jee:jndi-lookup id="lazyDataSource" jndi-name="jdbc/MyDataSource" lazy-init="true"/>
<jee:local-slsb id="lazyLocalBean" jndi-name="ejb/MyLocalBean" lazy-init="true"/>
<jee:remote-slsb id="lazyRemoteBean" jndi-name="ejb/MyRemoteBean" lazy-init="true"/>
<jee:local-slsb id="lazyLocalBean" jndi-name="ejb/MyLocalBean"
business-interface="org.springframework.beans.testfixture.beans.ITestBean" lazy-init="true"/>
<jee:remote-slsb id="lazyRemoteBean" jndi-name="ejb/MyRemoteBean"
business-interface="org.springframework.beans.testfixture.beans.ITestBean" lazy-init="true"/>
</beans>