I have this in src/main/groovy/...
package com.mycompany.web;
// imports....
#Controller
class GroovyController {
#RequestMapping("/status_groovy")
public #ResponseBody String getStatus() {
return "Hello World from groovy!";
}
}
Using maven 3 and spring 3.1 (Milestone). Spring MVC works perfectly well for java controllers and everything is set up fine. The groovy class compiles fine and can be found in the classes directory along with the java controller classes.
I have similar controller written in java (JavaController) in same package but under src/main/java and its getting picked up properly by spring and mapped and I can see the response on screen when I hit the url.
package com.mycompany.web;
// imports....
#Controller
class JavaController {
#RequestMapping("/status")
public #ResponseBody String getStatus() {
return "Hello World!";
}
}
Jetty starts normally with no error in log but in I dont see groovy url getting mapped whereas i can see the java one.
2011-09-23 16:05:50,412 [main] INFO org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped "{[/status],methods=[],params=[],headers=[],consumes=[],produces=[]}" onto public java.lang.String com.mycompany.web.JavaController.getStatus()
All the setting are fine as other parts of app are working just fine with annotations (component-scan etc.), Just that I can not get the url mapped in GroovyController
Can anyone explain what needs to be done in order to get Controllers written in groovy working?
PS: I am avoiding GroovyServlet to run the scripts because it has major downside when it comes to bean injection and url path mappings.
With all due respect to Ben (whom I work with), the problem here isn't that Spring is creating a cglib proxy. Rather, it's creating a dynamic JDK (or interface-based) proxy. This method of creating proxies can only implement methods declared in the target's implemented interfaces. You actually want Spring to create a cglib proxy, which creates a proxy that is a subclass of the target object and can therefore recreate all of its public methods. Unless you specify otherwise, Spring will create a cglib proxy if the target object doesn't implement any interfaces, and an interface-based proxy otherwise. Since all Groovy objects implement GroovyObject, you're getting an interface-based proxy, even though you didn't explicitly implement any interfaces in your Groovy controller. Ben's solution is correct in that if you create an interface with all your controller methods, you'll get the expected behavior. An alternative is to create a BeanFactoryPostProcessor which instructs Spring to create cglib proxies for classes that implement GroovyObject and only GroovyObject. Here's the code:
/**
* Finds all objects in the bean factory that implement GroovyObject and only GroovyObject, and sets the
* AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE value to true. This will, in the case when a proxy
* is necessary, force the creation of a CGLIB subclass proxy, rather than a dynamic JDK proxy, which
* would create a useless proxy that only implements the methods of GroovyObject.
*
* #author caleb
*/
public class GroovyObjectTargetClassPreservingBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
private static final Logger logger = LoggerFactory.getLogger(GroovyObjectTargetClassPreservingBeanFactoryPostProcessor.class);
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
for (String beanDefName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition bd = beanFactory.getBeanDefinition(beanDefName);
//ignore abstract definitions (parent beans)
if (bd.isAbstract())
continue;
String className = bd.getBeanClassName();
//ignore definitions with null class names
if (className == null)
continue;
Class<?> beanClass;
try {
beanClass = ClassUtils.forName(className, beanFactory.getBeanClassLoader());
}
catch (ClassNotFoundException e) {
throw new CannotLoadBeanClassException(bd.getResourceDescription(), beanDefName, bd.getBeanClassName(), e);
}
catch (LinkageError e) {
throw new CannotLoadBeanClassException(bd.getResourceDescription(), beanDefName, bd.getBeanClassName(), e);
}
Class<?>[] interfaces = beanClass.getInterfaces();
if (interfaces.length == 1 && interfaces[0] == GroovyObject.class) {
logger.debug("Setting attribute {} to true for bean {}", AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, beanDefName);
bd.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, true);
}
}
}
}
Just include a bean of this type in your context, and voila! You can have Groovy controllers without needing to define interfaces.
I beg to differ. There is no need to implement an interface. The problem here is that the default AnnotationMethodHandlerAdapter does not read annotations from proxies. Hence we would have to create this proxy aware AnnotationMethodHandlerAdapter which extends the default AnnotationMethodHandlerAdapter of spring. We also need to instantiate a bean for this ProxyAwareAnnotationMethodHandlerAdapter in the Spring Configuration xml file.
Note: This feature is not available in Spring 3.x but since spring 4.0 would support groovy beans, this feature should be covered.
//ProxyAwareAnnotationMethodHandlerAdapter.java
package name.assafberg.spring;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.Advised;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;
/**
* Add proxy awareness to <code>AnnotationMethodHandlerAdapter</code>.
*
* #author assaf
*/
public class ProxyAwareAnnotationMethodHandlerAdapter extends AnnotationMethodHandlerAdapter {
/**
* #param request
* #param response
* #param handler
* #return
* #throws Exception
* #see org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object)
*/
#Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
handler = unwrapHandler(handler);
return super.handle(request, response, handler);
}
/**
* #param handler
* #return
* #see org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter#supports(java.lang.Object)
*/
#Override
public boolean supports(Object handler) {
handler = unwrapHandler(handler);
return super.supports(handler);
}
/**
* Attempt to unwrap the given handler in case it is an AOP proxy
*
* #param handler
* #return Object
*/
private Object unwrapHandler(Object handler) {
if (handler instanceof Advised) {
try {
TargetSource targetSource = ((Advised) handler).getTargetSource();
return targetSource.getTarget();
} catch (Exception x) {
throw new RuntimeException(x);
}
} else {
return handler;
}
}
}
The spring configuration XML file must have the following. Instead of creating a bean of AnnotationMethodHandlerAdapter we must create a ProxyAwareAnnotationMethodHandlerAdapter bean.
<beans .........
...
...
<bean class="full.qualified.name.of.ProxyAwareAnnotationMethodHandlerAdapter" />
...
...
<lang:groovy script-source="classpath:com/example/mysample.groovy refresh-check-delay="1000" />
</beans>
Also Spring parses the configuration XML file using a SAX parser (based on event occurence). So, in order for spring to understand the annotations within the groovy scripts, the groovy beans (using tag) must be created after the ProxyAwareAnnotationMethodHandlerAdapter.
Hope than helps
Reference: http://forum.springsource.org/showthread.php?47271-Groovy-Controller
Unfortunately, if you want to get this running in Groovy you'll have to create an interface for your Controller class and annotate the method definitions as well. Spring creates a proxy for your class using Cglib. However, without creating a custom interface for your controller Spring is proxying on groovy.lang.GroovyObject because all Groovy objects implement that interface by default.
interface GroovyControllerInterface {
#RequestMapping("/status_groovy")
#ResponseBody String getStatus()
}
#Controller
class GroovyController implements GroovyControllerInterface {
#RequestMapping("/status_groovy")
public #ResponseBody String getStatus() {
return "Hello World from groovy!";
}
}
Related
I am new to Java. I have below java class:-
#WebService
#SOAPBinding(style = SOAPBinding.Style.RPC, use = SOAPBinding.Use.LITERAL)
public class ABC extends AbstractWebService {
protected ClassNameForService classNameForService;
public void init() {
}
#WebMethod
#Path("/test")
#Produces("text/plain")
#GET
/**
*
* #return String
*/
public String test(
#WebParam(partName = "sessionID")
#QueryParam("sessionID") String sessionID
) throws UserNotDefinedException
{
// does something here
}
}
and below is my spring configuration file
<bean id=”org.testproject.webservices.ABC class=”org.testproject.webservices.ABC init-method=”init” parent=”org.testproject.webservices.AbstractWebService”>
<property name=classNameForService ref=”org.testproject.service.gb.ClassNameForService/>
</bean>
So do we need init-method=”init” here? Can I remove init-method=”init” from configuration as well as init method from class, Is it a standard practice?
You do NOT have to specify an init method (or destroy method for that matter). I think it's worth noting that you ask if it's standard practice, I believe the standard practice has moved away from the XML configuration to an annotation-driven approach but for the purpose of your question, it is standard practice to not include an init method when it is not needed.
The init-method is akin to adding a #PostContstruct annotated method on annotation declared beans.
In a Spring Boot Web MVC REST service I want to use the operation ID and path values from SpringDoc generated OpenAPI from within the service where its generated. How can I get the OpenAPI JSON doc without going through the web endpoint?
If I understand you correctly:
You want to get OpenAPI documentation in JSON format inside code your Spring application.
I do it this way:
1.) Create a component that extends from the OpenApiResource class.
And create a getOpenApiJson method that calls getOpenApi() (creating or receiving an OpenApi model) and writeJsonValue() (serialization of OpenAPI).
#Component
public class CustomOpenApiResource extends OpenApiResource {
public CustomOpenApiResource(ObjectFactory<OpenAPIService> openAPIBuilderObjectFactory,
AbstractRequestService requestBuilder,
GenericResponseService responseBuilder,
OperationService operationParser,
Optional<List<OperationCustomizer>> operationCustomizers,
Optional<List<OpenApiCustomiser>> openApiCustomisers,
Optional<List<OpenApiMethodFilter>> methodFilters,
SpringDocConfigProperties springDocConfigProperties,
SpringDocProviders springDocProviders) {
super(openAPIBuilderObjectFactory,
requestBuilder,
responseBuilder,
operationParser,
operationCustomizers,
openApiCustomisers,
methodFilters,
springDocConfigProperties,
springDocProviders);
}
#Override
protected String getServerUrl(HttpServletRequest request, String apiDocsUrl) {
/**
* How to implement this method you can find out for example from OpenApiWebMvcResource
*/
return "";
}
public String getOpenApiJson() throws JsonProcessingException {
return writeJsonValue(getOpenApi(Locale.getDefault()));
}
}
2.) Inject CustomOpenApiResource component
#Autowired
private CustomOpenApiResource resource;
And use getOpenApiJson()
String openApiJson = resource.getOpenApiJson();
Spring cache is not working when calling cached method from another method of the same bean.
Here is an example to explain my problem in clear way.
Configuration:
<cache:annotation-driven cache-manager="myCacheManager" />
<bean id="myCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="myCache" />
</bean>
<!-- Ehcache library setup -->
<bean id="myCache"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:shared="true">
<property name="configLocation" value="classpath:ehcache.xml"></property>
</bean>
<cache name="employeeData" maxElementsInMemory="100"/>
Cached service :
#Named("aService")
public class AService {
#Cacheable("employeeData")
public List<EmployeeData> getEmployeeData(Date date){
..println("Cache is not being used");
...
}
public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
List<EmployeeData> employeeData = getEmployeeData(date);
...
}
}
Result :
aService.getEmployeeData(someDate);
output: Cache is not being used
aService.getEmployeeData(someDate);
output:
aService.getEmployeeEnrichedData(someDate);
output: Cache is not being used
The getEmployeeData method call uses cache employeeData in the second call as expected. But when the getEmployeeData method is called within the AService class (in getEmployeeEnrichedData), Cache is not being used.
Is this how spring cache works or am i missing something ?
I believe this is how it works. From what I remember reading, there is a proxy class generated that intercepts all requests and responds with the cached value, but 'internal' calls within the same class will not get the cached value.
From https://code.google.com/p/ehcache-spring-annotations/wiki/UsingCacheable
Only external method calls coming in through the proxy are
intercepted. This means that self-invocation, in effect, a method
within the target object calling another method of the target object,
will not lead to an actual cache interception at runtime even if the
invoked method is marked with #Cacheable.
Since Spring 4.3 the problem could be solved using self-autowiring over #Resource annotation:
#Component
#CacheConfig(cacheNames = "SphereClientFactoryCache")
public class CacheableSphereClientFactoryImpl implements SphereClientFactory {
/**
* 1. Self-autowired reference to proxified bean of this class.
*/
#Resource
private SphereClientFactory self;
#Override
#Cacheable(sync = true)
public SphereClient createSphereClient(#Nonnull TenantConfig tenantConfig) {
// 2. call cached method using self-bean
return self.createSphereClient(tenantConfig.getSphereClientConfig());
}
#Override
#Cacheable(sync = true)
public SphereClient createSphereClient(#Nonnull SphereClientConfig clientConfig) {
return CtpClientConfigurationUtils.createSphereClient(clientConfig);
}
}
The example below is what I use to hit the proxy from within the same bean, it is similar to #mario-eis' solution, but I find it a bit more readable (maybe it's not:-). Anyway, I like to keep the #Cacheable annotations at the service level:
#Service
#Transactional(readOnly=true)
public class SettingServiceImpl implements SettingService {
#Inject
private SettingRepository settingRepository;
#Inject
private ApplicationContext applicationContext;
#Override
#Cacheable("settingsCache")
public String findValue(String name) {
Setting setting = settingRepository.findOne(name);
if(setting == null){
return null;
}
return setting.getValue();
}
#Override
public Boolean findBoolean(String name) {
String value = getSpringProxy().findValue(name);
if (value == null) {
return null;
}
return Boolean.valueOf(value);
}
/**
* Use proxy to hit cache
*/
private SettingService getSpringProxy() {
return applicationContext.getBean(SettingService.class);
}
...
See also Starting new transaction in Spring bean
Here is what I do for small projects with only marginal usage of method calls within the same class. In-code documentation is strongly advidsed, as it may look strage to colleagues. But its easy to test, simple, quick to achieve and spares me the full blown AspectJ instrumentation. However, for more heavy usage I'd advice the AspectJ solution.
#Service
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class AService {
private final AService _aService;
#Autowired
public AService(AService aService) {
_aService = aService;
}
#Cacheable("employeeData")
public List<EmployeeData> getEmployeeData(Date date){
..println("Cache is not being used");
...
}
public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
List<EmployeeData> employeeData = _aService.getEmployeeData(date);
...
}
}
If you call a cached method from same bean it will be treated as a private method and annotations will be ignored
Yes, the caching will not happen because of the reasons that were already mentioned in the other posts. However I would solve the problem by putting that method to its own class (service in this case). With that your code will be easier to maintain/test and understand.
#Service // or #Named("aService")
public class AService {
#Autowired //or how you inject your dependencies
private EmployeeService employeeService;
public List<EmployeeData> getEmployeeData(Date date){
employeeService.getEmployeeData(date);
}
public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
List<EmployeeData> employeeData = getEmployeeData(date);
...
}
}
#Service // or #Named("employeeService")
public class EmployeeService {
#Cacheable("employeeData")
public List<EmployeeData> getEmployeeData(Date date){
println("This will be called only once for same date");
...
}
}
In my Case I add variable :
#Autowired
private AService aService;
So I call the getEmployeeData method by using the aService
#Named("aService")
public class AService {
#Cacheable("employeeData")
public List<EmployeeData> getEmployeeData(Date date){
..println("Cache is not being used");
...
}
public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
List<EmployeeData> employeeData = aService.getEmployeeData(date);
...
}
}
It will use the cache in this case.
Better approach should be creating another service like ACachingService and call ACachingService.cachingMethod() instead of self Autowiring ( or any other approach trying to self inject). This way you do not fall into Circular dependency, which may be resulted in warning/error when upgrade to newer Spring ( Spring 2.6.6 in my case ) :
ERROR o.s.boot.SpringApplication - Application run failed
org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'webSecurityConfig':
Requested bean is currently in creation: Is there an unresolvable circular reference?
We looked at all the solutions here and decided to use a separate class for the cached methods because Spring 5 doesn't like circular dependencies.
Use static weaving to create proxy around your bean. In this case even 'internal' methods would work correctly
I use internal inner bean (FactoryInternalCache) with real cache for this purpose:
#Component
public class CacheableClientFactoryImpl implements ClientFactory {
private final FactoryInternalCache factoryInternalCache;
#Autowired
public CacheableClientFactoryImpl(#Nonnull FactoryInternalCache factoryInternalCache) {
this.factoryInternalCache = factoryInternalCache;
}
/**
* Returns cached client instance from cache.
*/
#Override
public Client createClient(#Nonnull AggregatedConfig aggregateConfig) {
return factoryInternalCache.createClient(aggregateConfig.getClientConfig());
}
/**
* Returns cached client instance from cache.
*/
#Override
public Client createClient(#Nonnull ClientConfig clientConfig) {
return factoryInternalCache.createClient(clientConfig);
}
/**
* Spring caching feature works over AOP proxies, thus internal calls to cached methods don't work. That's why
* this internal bean is created: it "proxifies" overloaded {#code #createClient(...)} methods
* to real AOP proxified cacheable bean method {#link #createClient}.
*
* #see Spring Cache #Cacheable - not working while calling from another method of the same bean
* #see Spring cache #Cacheable method ignored when called from within the same class
*/
#EnableCaching
#CacheConfig(cacheNames = "ClientFactoryCache")
static class FactoryInternalCache {
#Cacheable(sync = true)
public Client createClient(#Nonnull ClientConfig clientConfig) {
return ClientCreationUtils.createClient(clientConfig);
}
}
}
I would like to share what I think is the easiest approach:
Autowire the controller and use to call the method it instead of using the class context this.
The updated code would look like:
#Controller
public class TestController {
#Autowired TestController self;
#RequestMapping("/test")
public String testView(){
self.expensiveMethod();
return "test";
}
#Cacheable("ones")
public void expensiveMethod(){
System.out.println("Cache is not being used");
}
}
The default advice mode for processing caching annotation is “proxy”. At the startup of an application, all the caching annotations like #Caching, #Cacheable, #CacheEvict etc. are scanned and a target proxy class is generated for all of these classes. The proxy allows for intercepting the calls to these cacheable methods, which adds the caching advice/behavior.
So when we invoke the cacheable methods from the same class, as shown below, calls from the clients don’t get intercepted in a way that allows for caching advice to be added to them. Hence, every single time there is an unexpected cache miss.
Solution: Invoke the Cacheable methods from a different bean to use proxy class with caching advice.
I'm getting problems trying to call a remote service deployed in an EAR from another EAR.
I do not specify any names for my EJB, no matter they are #Local or #Remote and so just use annotation and inject it via #EJB.
This is what i have:
EAR A/
lib/any lib jar (including API jar for remote service B)
war
ejb module(s) with service A calling remote service B
EAR B/
lib/any API lib jar
ejb module with service B
Additional information: Service B implement both #Local and #Remote interfaces, and Service A inject Service B with Remote interface via:
#EJB private MyRemoteInterface remoteService;
This structure works perfectly fine with jboss server, but with websphere (8.5.5.1) one i must bind names onto my remote EJB. If do not add bindings on both EARs (i did it though admin console directly not to have to edit ejb-jar.xml) then my remote bean is not resolved at runtime.
Of course, i have to make it work with WAS, else i won't not post :)
My question: is that normal to be forced to name remote EJB with WebSphere or is it a regression (from any previous version) ? I'm expecting the injection #EJB on remote beans to works with automatic resolution on types, but maybe i'm wrong somewhere ?
Solution:
Because lookup must be done to make the resolution work, i decided to add the lookup configuration part onto the client ejb-jar.xml file(s). This is done automatically by maven plugin execution, with lookup name based on remote interface full name (package included) as this is the default binding WebSphere use if nothing is specified in EJB implementation.
I've chosen this solution for two reasons:
i don't want to do the lookup in my code (duplicate code with no interest)
i need to make it automatic and transparent for other developers
Thanks bkail for the answer.
Finally, for business delays reason i've written a CDI extension to make the job.
The extension scan all injection point with Remote contract and proxify them. Proxies are #ApplicationScoped managed beans created on demand, and their job only consists in:
lookup the target bean related to the scanned remote contract
delegate the execution of the called remote method
This solution also offers me the possibility to handle lookup operation on different machine(s) by configuration though ENV variables so that deployement by container (i.e Docker) would easily works (which is one of our target in a comming future)
EDIT: CDI extension code below
RemoteEjbExtension.java:
public class RemoteEjbExtension implements Extension {
/**
* This method is fired by the container for every Java EE component class
* supporting injection that may be instantiated by the container at runtime,
* including every managed bean declared using javax.annotation.ManagedBean,
* EJB session or message-driven-bean, enabled bean, enabled interceptor or
* enabled decorator.
*
* #param pit the event that has been fired
*/
<T> void processInjectionTarget(#Observes final ProcessInjectionTarget<T> pit) {
for (AnnotatedField<? super T> field : pit.getAnnotatedType().getFields()) {
if (field.getJavaMember().getType().isAnnotationPresent(Remote.class)) {
RemoteProxyFactory.putIfAbsent(field.getJavaMember().getType());
}
}
}
/**
* This method is fired by the container when it has fully completed the
* bean discovery process, validated that there are no definition errors
* relating to the discovered beans, and registered Bean and ObserverMethod
* objects for the discovered beans, but before detecting deployment problems.
*
* #param abd AfterBeanDiscovery fired events
* #param bm Allows a portable extension to interact directly with the container.
* Provides operations for obtaining contextual references for beans,
* along with many other operations of use to portable extensions.
*/
#SuppressWarnings("unchecked")
void afterBeanDiscovery(#Observes final AfterBeanDiscovery abd, final BeanManager bm) {
// Roll over discovered remote interfaces
for (final Entry<String, Class<?>> remoteClassEntry : RemoteProxyFactory.getProxyClassEntries()) {
// Proxy that points to the remote EJB
final Object remoteProxy;
final Class<?> remoteClass = remoteClassEntry.getValue();
try {
// Build a proxy that fetches the remote EJB using JNDI
// and delegate the call.
remoteProxy = RemoteProxyFactory.Builder.createEJBRemoteProxy(remoteClass);
} catch (Exception e) {
throw new IllegalStateException("Proxy creation for " + remoteClass.getCanonicalName() + " failed.", e);
}
final InjectionTarget<Object> it;
try {
AnnotatedType<Object> at = ((AnnotatedType<Object>) bm.createAnnotatedType(remoteProxy.getClass()));
it = bm.createInjectionTarget(at);
} catch (Exception e) {
throw new IllegalStateException("Injection target for " + remoteClass.getCanonicalName() + " is invalid.", e);
}
final Bean<?> beanRemoteProxy = RemoteProxyFactory.Builder.createBeanForProxy(remoteProxy, it, remoteClass, ApplicationScoped.class);
abd.addBean(beanRemoteProxy);
}
}
}
RemoteProxyFactory.java:
public final class RemoteProxyFactory {
/** The JNDI initial context */
private static InitialContext CTX;
static {
try {
RemoteProxyFactory.CTX = new InitialContext();
} catch (NamingException e) {
throw new IllegalStateException("Unable to get initial context.", e);
}
}
private static final Map<String, Class<?>> REMOTE_EJB_CLASS_MAP = new ConcurrentHashMap<String, Class<?>>();
/**
* Register given class into proxy map
* #param remoteEJBContractClass the remote contract's class to register
*/
public static void putIfAbsent(final Class<?> remoteEJBContractClass) {
// Works only for same class-loader. You would change this code
// and transform the map to handle multiple class-loader for same contract.
// In our current configuration there is no need as APIs / IMPL libraries share the same CL.
if (!REMOTE_EJB_CLASS_MAP.containsKey(remoteEJBContractClass.getSimpleName())) {
REMOTE_EJB_CLASS_MAP.put(remoteEJBContractClass.getSimpleName(), remoteEJBContractClass);
}
}
public static Set<Entry<String, Class<?>>> getProxyClassEntries() {
return REMOTE_EJB_CLASS_MAP.entrySet();
}
public static InitialContext getContext() {
return RemoteProxyFactory.CTX;
}
public static final class Builder {
private static final Logger LOGGER = Logger.getLogger(Builder.class.getName());
/**
* Create a new proxy that lookup the remote EJB
* though JNDI.
* #param remoteEJBClazz the remote class contract
* #return a new remote EJB proxy
*/
public static Object createEJBRemoteProxy(final Class<?> remoteEJBClazz) {
return Proxy.newProxyInstance(remoteEJBClazz.getClassLoader(), new Class[] {
remoteEJBClazz
}, new InvocationHandler() {
#Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
Object ejbInstance = null;
try {
// Pull the remote EJB from the JNDI
ejbInstance = RemoteProxyFactory.getContext().lookup(remoteEJBClazz.getName());
} catch (Exception e) {
throw new IllegalStateException("Remote EJB not found : " + remoteEJBClazz.getSimpleName(), e);
}
// Delegates the call to the remote EJB
return method.invoke(ejbInstance, args);
}
});
}
/**
* Create a bean for given proxy / injection target / type / scope
* #param proxy the proxy object
* #param it the injection target
* #param clazz the proxy type
* #param targetScope the returned managed bean' scope
* #return the managed bean handling given proxy
*/
public static <T extends Object> Bean<T> createBeanForProxy(final T proxy, final InjectionTarget<T> it, final Class<?> clazz, final Class<? extends Annotation> targetScope) {
return new Bean<T>() {
#Override
public T create(final CreationalContext<T> ctx) {
return proxy;
}
#Override
public void destroy(final T instance, final CreationalContext<T> ctx) {
it.preDestroy(instance);
it.dispose(instance);
ctx.release();
}
#Override
public Class<?> getBeanClass() {
return clazz;
}
#Override
public Set<InjectionPoint> getInjectionPoints() {
return it.getInjectionPoints();
}
#Override
public String getName() {
return clazz.toString();
}
#Override
public Set<Annotation> getQualifiers() {
Set<Annotation> qualifiers = new HashSet<Annotation>();
qualifiers.add(new AnnotationLiteral<Default>() {
/** Default serial-id. */
private static final long serialVersionUID = 1L;
});
qualifiers.add(new AnnotationLiteral<Any>() {
/** Default serial-id. */
private static final long serialVersionUID = 1L;
});
return qualifiers;
}
#Override
public Class<? extends Annotation> getScope() {
return targetScope;
}
#Override
public Set<Class<? extends Annotation>> getStereotypes() {
return Collections.emptySet();
}
#Override
public Set<Type> getTypes() {
Set<Type> types = new HashSet<Type>();
types.add(clazz);
return types;
}
#Override
public boolean isAlternative() {
return false;
}
#Override
public boolean isNullable() {
return false;
}
};
}
}
}
This is working as expected for WebSphere Application Server, and it is not a regression. The javadoc only requires automatic binding for #EJB when the type is within the same application:
If no explicit linking information is provided and there is only one
session bean within the same application that exposes the matching
client view type, by default the EJB dependency resolves to that
session bean.
I am trying to add a RepositoryEventHandler as described on Spring Data REST documentation to the REST repository shown below:
#RepositoryRestResource(collectionResourceRel = "agents", path = "/agents")
public interface AgentRepository extends CrudRepository<Agent, Long> {
// no implementation required; Spring Data will create a concrete Repository
}
I created an AgentEventHandler:
#Component
#RepositoryEventHandler(Agent.class)
public class AgentEventHandler {
/**
* Called before {#link Agent} is persisted
*
* #param agent
*/
#HandleBeforeSave
public void handleBeforeSave(Agent agent) {
System.out.println("Saving Agent " + agent.toString());
}
}
and declared it in a #Configuration component:
#Configuration
public class RepositoryConfiguration {
/**
* Declare an instance of the {#link AgentEventHandler}
*
* #return
*/
#Bean
AgentEventHandler agentEvenHandler() {
return new AgentEventHandler();
}
}
When I am POSTing to the REST resource, the Entity gets persisted but the method handleBeforeSave never gets invoked. What am I missing?
I'm using: Spring Boot 1.1.5.RELEASE
Sometimes obvious mistakes go unnoticed.
POST-ing a Spring Data REST resource, emits a BeforeCreateEvent. To catch this event, the method handleBeforeSave must be annotated with #HandleBeforeCreate instead of #HandleBeforeSave (the latter gets invoked on PUT and PATCH HTTP calls).
Tests pass successfully on my (cleaned up) demo app now.
How does your main Application class look like? Does it import the RepositoryRestMvcConfiguration as described in https://spring.io/guides/gs/accessing-data-rest/?