Pivotal GemFire: PDX serializer config in Spring Data GemFire - java

I have created a GemFire cluster with 2 Locators, 2 cache servers and a "Customer" REPLICATE Region. (Domain object class is placed in classpath during server startup).
I am able to run a Java program (Peer) to load the "Customer" Region in the cluster. Now we want to move to Spring Data GemFire where I am not sure how to configure PDX serialization and getting...
com.gemstone.gemfire.InternalGemFireException: java.io.NotSerializableException: com.gemfire.poc.DomainObjects.Customer
cache.xml in simple Java program...
<?xml version="1.0" encoding="UTF-8"?><cache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schema.pivotal.io/gemfire/cache" xsi:schemaLocation="http://schema.pivotal.io/gemfire/cache http://schema.pivotal.io/gemfire/cache/cache-8.1.xsd" version="8.1" lock-lease="120" lock-timeout="60" search-timeout="300" is-server="false" copy-on-read="false">
<pdx>
<pdx-serializer>
<class-name>
com.gemstone.gemfire.pdx.ReflectionBasedAutoSerializer
</class-name>
<parameter name="classes">
<string>com.gemfire.poc.DomainObjects.*</string>
</parameter>
</pdx-serializer>
</pdx>
<region name="Customer" refid="REPLICATE">
<region-attributes refid="REPLICATE" scope="distributed-no-ack">
<cache-loader>
<class-name>com.citigroup.pulse.pt.gemfire.poc.clientserver.SimpleCacheLoader</class-name>
</cache-loader>
</region-attributes>
</region>
</cache>
spring-context.xml in Spring Boot app...
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:gfe="http://www.springframework.org/schema/gemfire"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/gemfire http://www.springframework.org/schema/gemfire/spring-gemfire.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/data/gemfire
http://www.springframework.org/schema/data/gemfire/spring-data-gemfire.xsd">
<util:properties id="gemfireProperties">
<prop key="log-level">config</prop>
<prop key="locators">hostA[10334],hostB[10334]</prop>
</util:properties>
<bean id="mappingPdxSerializer" class="com.gemstone.gemfire.pdx.ReflectionBasedAutoSerializer"/>
<gfe:cache use-bean-factory-locator="false" properties-ref="gemfireProperties" use-cluster-configuration="true" pdx-serializer-ref="mappingPdxSerializer" />
<gfe:replicated-region id="Customer" ignore-if-exists="true">
</gfe:replicated-region>
</beans>
Can someone help me fix the serialization issue?
Caused by: com.gemstone.gemfire.InternalGemFireException: java.io.NotSerializableException: com.gemfire.poc.DomainObjects.Customer
at com.gemstone.gemfire.distributed.internal.DistributionManager.putOutgoing(DistributionManager.java:1954)
at com.gemstone.gemfire.internal.cache.DistributedCacheOperation.distribute(DistributedCacheOperation.java:476)
at com.gemstone.gemfire.internal.cache.AbstractUpdateOperation.distribute(AbstractUpdateOperation.java:65)
at com.gemstone.gemfire.internal.cache.DistributedRegion.distributeUpdate(DistributedRegion.java:519)
at com.gemstone.gemfire.internal.cache.DistributedRegion.basicPutPart3(DistributedRegion.java:500)
at com.gemstone.gemfire.internal.cache.AbstractRegionMap.basicPut(AbstractRegionMap.java:3052)
at com.gemstone.gemfire.internal.cache.LocalRegion.virtualPut(LocalRegion.java:5838)
Precisely, how to add "classes" parameter of ReflectionBasedAutoSerializer in spring-data-gemfire tags?
PDX deserialization exception while retrieving value from Region:
com.gemstone.gemfire.ToDataException: PdxSerializer failed when calling toData on class javax.management.Notification
at com.gemstone.gemfire.internal.InternalDataSerializer.writePdx(InternalDataSerializer.java:3130)
at com.gemstone.gemfire.internal.InternalDataSerializer.writeUserObject(InternalDataSerializer.java:1520)
at com.gemstone.gemfire.internal.InternalDataSerializer.writeWellKnownObject(InternalDataSerializer.java:1416)
at com.gemstone.gemfire.internal.InternalDataSerializer.basicWriteObject(InternalDataSerializer.java:2208)
at com.gemstone.gemfire.DataSerializer.writeObject(DataSerializer.java:3181)
at com.gemstone.gemfire.internal.util.BlobHelper.serializeToBlob(BlobHelper.java:50)
at com.gemstone.gemfire.internal.util.BlobHelper.serializeToBlob(BlobHelper.java:38)
at com.gemstone.gemfire.internal.cache.UpdateOperation$UpdateMessage.toData(UpdateOperation.java:492)
at com.gemstone.gemfire.internal.InternalDataSerializer.invokeToData(InternalDataSerializer.java:2407)
at com.gemstone.gemfire.internal.InternalDataSerializer.writeDSFID(InternalDataSerializer.java:1378)
at com.gemstone.gemfire.internal.tcp.MsgStreamer.writeMessage(MsgStreamer.java:239)
at com.gemstone.gemfire.distributed.internal.direct.DirectChannel.sendToMany(DirectChannel.java:458)
at com.gemstone.gemfire.distributed.internal.direct.DirectChannel.sendToOne(DirectChannel.java:310)
at com.gemstone.gemfire.distributed.internal.direct.DirectChannel.send(DirectChannel.java:696)
at com.gemstone.gemfire.distributed.internal.membership.jgroup.JGroupMembershipManager.directChannelSend(JGroupMembershipManager.java:2929)
at com.gemstone.gemfire.distributed.internal.membership.jgroup.JGroupMembershipManager.send(JGroupMembershipManager.java:3163)
at com.gemstone.gemfire.distributed.internal.DistributionChannel.send(DistributionChannel.java:79)
at com.gemstone.gemfire.distributed.internal.DistributionManager.sendOutgoing(DistributionManager.java:3907)
at com.gemstone.gemfire.distributed.internal.DistributionManager.sendMessage(DistributionManager.java:3948)
at com.gemstone.gemfire.distributed.internal.DistributionManager.putOutgoing(DistributionManager.java:1951)
at com.gemstone.gemfire.internal.cache.DistributedCacheOperation.distribute(DistributedCacheOperation.java:476)
at com.gemstone.gemfire.internal.cache.AbstractUpdateOperation.distribute(AbstractUpdateOperation.java:65)
at com.gemstone.gemfire.internal.cache.DistributedRegion.distributeUpdate(DistributedRegion.java:519)
at com.gemstone.gemfire.internal.cache.DistributedRegion.basicPutPart3(DistributedRegion.java:500)
at com.gemstone.gemfire.internal.cache.ProxyRegionMap.basicPut(ProxyRegionMap.java:242)
at com.gemstone.gemfire.internal.cache.LocalRegion.virtualPut(LocalRegion.java:5838)
at com.gemstone.gemfire.internal.cache.DistributedRegion.virtualPut(DistributedRegion.java:387)
at com.gemstone.gemfire.internal.cache.LocalRegionDataView.putEntry(LocalRegionDataView.java:118)
at com.gemstone.gemfire.internal.cache.LocalRegion.basicPut(LocalRegion.java:5228)
at com.gemstone.gemfire.internal.cache.LocalRegion.validatedPut(LocalRegion.java:1599)
at com.gemstone.gemfire.internal.cache.LocalRegion.put(LocalRegion.java:1582)
at com.gemstone.gemfire.internal.cache.AbstractRegion.put(AbstractRegion.java:327)
at com.gemstone.gemfire.management.internal.ManagementResourceRepo.putEntryInLocalNotificationRegion(ManagementResourceRepo.java:169)
at com.gemstone.gemfire.management.internal.NotificationHub$NotificationHubListener.handleNotification(NotificationHub.java:193)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor$ListenerWrapper.handleNotification(DefaultMBeanServerInterceptor.java:1754)
at javax.management.NotificationBroadcasterSupport.handleNotification(NotificationBroadcasterSupport.java:275)
at javax.management.NotificationBroadcasterSupport$SendNotifJob.run(NotificationBroadcasterSupport.java:352)
at javax.management.NotificationBroadcasterSupport$1.execute(NotificationBroadcasterSupport.java:337)
at javax.management.NotificationBroadcasterSupport.sendNotification(NotificationBroadcasterSupport.java:248)
at com.gemstone.gemfire.management.internal.beans.ManagementAdapter.handleRegionRemoval(ManagementAdapter.java:879)
at com.gemstone.gemfire.management.internal.beans.ManagementListener.handleEvent(ManagementListener.java:123)
at com.gemstone.gemfire.distributed.internal.InternalDistributedSystem.notifyResourceEventListeners(InternalDistributedSystem.java:2252)
at com.gemstone.gemfire.distributed.internal.InternalDistributedSystem.handleResourceEvent(InternalDistributedSystem.java:506)
at com.gemstone.gemfire.internal.cache.LocalRegion.basicDestroyRegion(LocalRegion.java:6642)
at com.gemstone.gemfire.internal.cache.DistributedRegion.basicDestroyRegion(DistributedRegion.java:1957)
at com.gemstone.gemfire.internal.cache.LocalRegion.close(LocalRegion.java:2219)
at org.springframework.data.gemfire.RegionFactoryBean.destroy(RegionFactoryBean.java:529)
at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:272)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:578)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:554)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:961)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:523)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.destroySingletons(FactoryBeanRegistrySupport.java:230)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingletons(DefaultListableBeanFactory.java:968)
at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1032)
at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1008)
at org.springframework.context.support.AbstractApplicationContext$2.run(AbstractApplicationContext.java:929)
Caused by: org.springframework.data.mapping.model.MappingException: Could not write value for property protected transient java.lang.Object java.util.EventObject.source
at org.springframework.data.gemfire.mapping.MappingPdxSerializer$2.doWithPersistentProperty(MappingPdxSerializer.java:188)
at org.springframework.data.gemfire.mapping.MappingPdxSerializer$2.doWithPersistentProperty(MappingPdxSerializer.java:173)
at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:309)
at org.springframework.data.gemfire.mapping.MappingPdxSerializer.toData(MappingPdxSerializer.java:173)
at com.gemstone.gemfire.internal.InternalDataSerializer.writePdx(InternalDataSerializer.java:3075)
... 56 more
Caused by: com.gemstone.gemfire.pdx.PdxFieldAlreadyExistsException: The field "source" already exists.
at com.gemstone.gemfire.pdx.internal.PdxType.addField(PdxType.java:262)
at com.gemstone.gemfire.pdx.internal.PdxWriterImpl.updateMetaData(PdxWriterImpl.java:858)
at com.gemstone.gemfire.pdx.internal.PdxWriterImpl.updateMetaData(PdxWriterImpl.java:851)
at com.gemstone.gemfire.pdx.internal.PdxWriterImpl.writeObject(PdxWriterImpl.java:303)
at com.gemstone.gemfire.pdx.internal.PdxWriterImpl.writeField(PdxWriterImpl.java:705)
at com.gemstone.gemfire.pdx.internal.PdxWriterImpl.writeField(PdxWriterImpl.java:625)
at org.springframework.data.gemfire.mapping.MappingPdxSerializer$2.doWithPersistentProperty(MappingPdxSerializer.java:184)
... 60 more

You have a couple of options here, along with a few suggested recommendations.
1) First, I would not use Pivotal GemFire's o.a.g.pdx.ReflectionBasedAutoSerializer. Rather SDG has a much more robust PdxSerializer implementation based on Spring Data's Mapping Infrastructure (i.e. the o.s.d.g.mapping.MappingPdxSerializer).
In addition, SDG's MappingPdxSerializer allows you to register custom PdxSerializer's on an entity field/property case-by-case basis. Imagine if your Customer class has a reference to a complex Address class and that class has special serialization needs.
Furthermore, SDG's MappingPdxSerializer can handle transient and read-only properties.
Finally, you don't have to mess with any fussy/complex Regex to properly identify the application domain model types that need to be serialized.
2) Second, you can leverage Spring's JavaConfig along with SDG's new Annotation-based configuration model to configure Pivotal GemFire PDX Serialization as simply as this...
#SpringBootApplication
#PeerCacheApplication
#EnablePdx(..)
class MySpringBootApacheGeodeApplication {
...
}
That is, using the SDG #EnablePdx annotation.
More details on #1 and #2 above are available here and here.
Of course, the later is more applicable when using Pivotal GemFire 9.x+ with Spring Data GemFire Kay (2.0+). Judging by the package in your configuration of the com.gemstone.gemfire.pdx.ReflectionBasedAutoSerializer from your XML config (i.e. the com.gemstone.gemfire package) it would appear you are using Pivotal GemFire 8.2.x with Spring Data GemFire Ingalls (or 1.9.x.RELEASE), perhaps?
However, if you insist on, or are required to use XML for your configuration, then you can do the following...
<beans ...>
<bean id="mappingPdxSerializer" class="org.springframework.data.gemfire.mapping.MappingPdxSerializer"/>
<gfe:cache pdx-serializer-ref="mappingPdxSerializer" .../>
</beans>
And, if you really want to use Pivotal GemFire's ReflectionBasedAutoSerializer, then you can find examples of it's use in the SDG test suite. For instance.
I also have a few more examples in my spring-gemfire-test project/repo (which is quite a mess and I don't maintain this repo much anymore, as a warning). Examples here, using Java configuration with GemFire's API here, here, here which also shows the use of SDG's MappingPdxSerializer as well (by comparison), and so on and so forth. Many examples riddle throughout my repos.
Hope this helps!
Cheers,
-John

Related

How to properly locate Spring Context property-placeholder?

I am attempting to implement an outdated open-source Properties File Manager (https://github.com/Confluex/Zuul/wiki) with a Mule Test Application in MuleSoft's Anypoint Studio platform. This appears to require the Spring Context Schema, but the project cannot locate it. It continually states there is no 'context:property-placeholder'. I feel a versioning error may be the problem. Here is my attempt:
<mule
xmlns="http://www.mulesoft.org/schema/mule/core"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:spring="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:zuul="zuul-spring-client-1.5.1"
xsi:schemaLocation="
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
zuul-spring-client-1.5.1 zuul-spring-client-1.5.1.xsd">
<context:property-placeholder properties-ref="MuleMeetZuul" />
</mule>
Here is the error thrown:
Caused by: org.mule.runtime.api.exception.MuleRuntimeException: There was '1' error while parsing the given file 'zuultest.xml'.
Full list:
org.xml.sax.SAXParseException; lineNumber: 17; columnNumber: 65; cvc-complex-type.2.4.a: Invalid content was found starting with element 'context:property-placeholder'. One of '{"http://www.mulesoft.org/schema/mule/core":annotations, "http://www.mulesoft.org/schema/mule/core":description, "http://www.mulesoft.org/schema/mule/core":global-property, "http://www.mulesoft.org/schema/mule/core":configuration, "http://www.mulesoft.org/schema/mule/core":notifications, "http://www.mulesoft.org/schema/mule/core":abstract-extension, "http://www.mulesoft.org/schema/mule/core":abstract-shared-extension, "http://www.mulesoft.org/schema/mule/core":abstract-mixed-content-extension, "http://www.mulesoft.org/schema/mule/core":abstract-security-manager, "http://www.mulesoft.org/schema/mule/core":abstract-transaction-manager, "http://www.mulesoft.org/schema/mule/core":abstract-shared-transaction-manager, "http://www.mulesoft.org/schema/mule/core":abstract-connector, "http://www.mulesoft.org/schema/mule/core":abstract-shared-connector, "http://www.mulesoft.org/schema/mule/core":abstract-global-endpoint, "http://www.mulesoft.org/schema/mule/core":abstract-exception-strategy, "http://www.mulesoft.org/schema/mule/core":abstract-on-error, "http://www.mulesoft.org/schema/mule/core":abstract-flow-construct, "http://www.mulesoft.org/schema/mule/core":flow, "http://www.mulesoft.org/schema/mule/core":sub-flow, "http://www.mulesoft.org/schema/mule/core":top-level-processor, "http://www.mulesoft.org/schema/mule/core":abstract-global-intercepting-message-processor, "http://www.mulesoft.org/schema/mule/core":abstract-object-store}' is expected.
Any advice would be much appreciated. Thanks.
If you are trying to use the Mule 3 implementation in a Mule 4 it will fail, and that should be expected. Mule 3 property placeholder providers where directly Spring property placeholder providers. Mule 4 uses a different way to implement them. <context:property-placeholder> doesn't exist in Mule 4. Its replacement <configuration-properties> will not work for this usage.
Instead you will have to use Mule SDK for Java to develop a custom configuration property provider by implementing the ConfigurationPropertiesProviderFactory interface in a factory class and extending the DefaultConfigurationPropertiesProvider class to implement the provider. In the provider you will need to use the Zuul library to implement the operations to get the keys and values.
Instructions are available in the documentation: https://docs.mulesoft.com/mule-runtime/4.3/custom-configuration-properties-provider

How do I "inject" structured configuration information with Spring?

I have the following scenario:
There will be a Java language testbed system consisting of a number of "services" that collaborate by passing messages to one another. The service implementation is intended to be generic, i.e. there is no specific "business logic" contained within. For the purposes of the testbed it is important to be able to create various collections of services, configured externally (if possible).
The services themselves are unaware of the existence of any other service. Each service simply subscribes to the topics where it expects to receive information and publishes on topics where it sends information out to any waiting subscribers. With careful configuration it then would be possible to simulate a data flow graph.
The plan is to configure a given service instance by providing configuration information that describes the information needed to set up subscribers (readers) and publishers (writers). The configuration information may include other properties not related to publish/subscribe.
Below is a possible example:
Note: XML was chosen for the example simply because it's easy enough to read and allows for structured data.
<service>
<name>Service A</name>
<service-id>service ID</service-id>
<publish>
<per-second>5</per-second>
<topic>
<name>Topic 1</name>
<class>org.xyz.Topic1</class>
<!-- override 5/sec rate -->
<per-second>10</per-second>
</topic>
<topic>
<name>Topic 2</name>
<class>org.xyz.Topic2</class>
</topic>
</publish>
<subscribe>
<topic>
<name>Topic 3</name>
<class>org.xyz.Topic3</class>
</topic>
</subscribe>
</service>
<service>
<name>Service B</name>
<service-id>service ID</service-id>
<publish>
<per-second>30</per-second>
<topic>
<name>Topic 3</name>
<class>org.xyz.Topic3</class>
</topic>
</publish>
<subscribe>
<topic>
<name>Topic 2</name>
<class>org.xyz.Topic2</class>
</topic>
</subscribe>
</service>
...
I would like to use the Spring framework to help with the configuration of these services. Note: I am very new to Spring and am currently reading Spring in Action (and other sources) to educate myself.
What I would like to know is: How could I "inject" the configuration information to some sort of controller or factory that would then use it to create the collection of services and provide them with the necessary information to create the readers and writers they will use to receive and send messages?
From what I've read so far, Spring appears to be pretty powerful WRT to dependency injection and "bean wiring", but I don't know enough about what can (and cannot) be done, nor how to do it.
I'm not partial to whether Spring is configured by Java or XML. I just used XML because it easy to put together, allows for data structuring and seems to be used everywhere. If it makes more sense to specify the configuration a different way, just let me know.
How a given service would handle an event (i.e. receive a specific message) to possibly send out a message "response", or take some other action, is a topic outside the scope of this question. I am researching how that could be done - mainly at rules based processing. If anyone has suggestions, I will gladly take a look at them.
Make a config file like this:
some.paremeter=cool
some.other.parameter=awesome
named myconfig.properties. Make sure the file is in your classpath, then include -Dspring.config.name=myconfig in your vm args; then in the xml you can use ${some.parameter} etc. In particular, putting the config file in <project-root>/config/ will work.
For example, here is a simple MongoClient from one of my projects that uses a spring config:
<!--Mongo-->
<bean id="mongoClient" class="com.mongodb.MongoClient">
<constructor-arg>
<bean class="com.mongodb.MongoClientURI">
<constructor-arg type="java.lang.String"
value="mongodb://${mongo.db.user}:${mongo.db.password}#${mongo.db.host}:${mongo.db.port}/${mongo.db.database}"/>
</bean>
</constructor-arg>
</bean>

Wiring Database Object with Spring Bean

From Spring in Action book, there is an example of Spring Bean. It uses Compact Disc analogy. When an application needs a "The Beatles" album, it creates "The Beatles" album bean, and as well as other albums.
If there are n albums in database, so should I create n album beans?
If it is not, how the n albums represented in application? Is it just a POJO domain model (not a bean)?
What is the real use case using Spring Bean?
If I were you, I would depart from the analogy of the compact disc as a Spring bean, especially with respect to your later questions. Quite plainly, any Java object can be declared as a bean in Spring, whether you're using XML configuration or Java configuration.
Let's suppose I have these 2 classes:
public class Foo {
private String s;
private Bar bar;
// getters & setters
}
public class Bar {
private int i;
// getter & setter
}
I can make the former a Spring Bean by declaring it in an XML configuration file:
<bean id="foo" class="demo.Foo">
<property name="s" value="Hello, World!" />
<property name="bar">
<bean class="demo.Bar">
<property name="i" value="10" />
</bean>
</property>
</bean>
Now, with these 2 lines of code:
ApplicationContext ctx = new ClassPathXmlApplicationContext("app.xml");
Foo foo = ctx.getBean(Foo.class);
The foo object that was configured can be retrieved, and all its properties including bar will be set. This is the core use case of Spring, i.e. letting you configure how the building blocks of your application resolve their dependencies at runtime. Initially Spring was all about configuration outside of code, but the focus now has slightly changed, with things like component scans and Java configuration...
Anyway, to conclude with this brief example, the following line of code will print 10:
System.out.println(foo.getBar().getI());
In this example, I took Foo and Bar, but it could as well be a Web Service, a service implementing some business logic, a database, an ORM facade, a template engine, a thread pool, anything... But mostly components dealing with data objects, not data objects themselves, though this is entirely possible.
Now to return with your use case, in a Spring app, I would generally have these components if I'm coding a Web app with a database: a controller (the Web boundary), a service (for business logic), a repository (for querying) and of course a data source. I won't delve into too much details here (no declarative transactions for example). Notice that with this configuration, no specific data provider is compiled into my Java code, it remains in the configuration:
<bean id="cdController" class="demo.compactdisc.CdController">
<property name="cdService" ref="cdService" />
</bean>
<bean id="cdService" class="demo.compactdisc.CdServiceImpl">
<property name="cdRepository" ref="cdRepository" />
</bean>
<bean id="cdRepository" class="demo.compactdisc.CdRepositoryImpl">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="test"/>
<property name="password" value="s3cr3t"/>
</bean>
With your domain, the repository would return the compact discs from the database to the service, the service to the controller and the controller to the user. Compact discs would not be described as Spring beans but would certainly be parameters and return values from the actual Spring beans.
You just need to have one Album class and annotate it as a #Component or do it via xml.
The terms bean and POJO are interchangeable. As per Spring in action 4rd edition, Spring tries hard to be a non-invasive framework.
(...)the classes in a Spring-based application often have no
indication that they’re being used by Spring. At worst, a class may be annotated with one of Spring’s annotations, but it’s otherwise a POJO
and
Although Spring uses the words bean and JavaBean liberally
when referring to application components, this doesn’t mean a Spring component
must follow the JavaBeans specification to the letter. A Spring component can be
any type of POJO.
The use case is that you can use the Spring Dependency Injection to wire your beans on run-time, your application can benefit from Spring in terms of simplicity, testability, and loose coupling.
In short, a Spring bean as you refer is just a POJO used in the context of an Spring Application. If you use the xml mapping instead of the annotation, your class will be just another regular Java class, a Plain Old Java Object.
If there are n albums in database, so should I create n album beans?
I would think not. If there are n albums it would be very cumbersome to include them all explicitly in your App.config file if that's what you're referring to; but you could. You would probably add an AlbumService (#Service) #Bean and associated #Repository to handle writing and retrieving them from the DB.
If it is not, how the n albums represented in application? Is it just
a POJO domain model (not a bean)?
You could have an Album #Entity bean with the attributes of an album. When you save an album you'd set the attributes as opposed to having individual components implementing a common interface. Your DB would have n albums in it. If you needed to retrieve just one Beatles album you could query based on the album title, for example. If you wanted them all you could do albumService.findAll(); and get a container of them.
What is the real use case using Spring Bean?
Spring is the real use case of a Spring Bean. According to the Spring IoC Container Reference:
In Spring, the objects that form the backbone of your application and
that are managed by the Spring IoC container are called beans. A bean
is an object that is instantiated, assembled, and otherwise managed by
a Spring IoC container. Otherwise, a bean is simply one of many
objects in your application. Beans, and the dependencies among them,
are reflected in the configuration metadata used by a container.
I can't provide a better answer than what's contained in the documentation or given in this answer.

Spring cache support requires ApplicationContext?

I'm running into an issue trying to use Spring caching with ehcache in my application. For reasons that I can't elaborate on, my application uses a graph of BeanFactories instead of ApplicationContexts. This approach has worked well as long as we manually register our BeanPostProcessors, as is called out in the Spring documentation.
We are now adding caching to the app. When we used the simplest annotation configuration, it works.
// This works
package com.x.y.z;
public class RoleManager {
private String user;
public RoleManager( String user ) {
this.user = user;
}
public String getName() {
return user;
}
#Cacheable("user")
public boolean isAllowed(String permissionId, Map<String,?> params)
{
... lengthy and expensive operation to determine if user is permitted to do something
}
}
We configure this to using spring xml for this bean factory:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
xmlns:cache="http://www.springframework.org/schema/cache" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<cache:annotation-driven/>
<bean id="roleManager" class="com.x.y.z.RoleManager" scope="prototype"/>
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="ehcacheManager"/>
</bean>
<bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="file:${conf.dir}/ehcache.xml"/>
<property name="shared" value="true"/>
</bean>
</beans>
... unrelated business beans elided ...
We are using Spring 4.1.9 and ehcache 2.10.2
The above code works quite well. Our ehcache instance for "user" begins to fill as we get cache misses, and returns cached values for hits.
Once this was running correctly, we found that it isn't possible to evict all the entries for a particular user because the cache key is a concatenation of the permissionid and the Map::toString result. We decided to create a cache per user so we would have more control over eviction. To use Spring, we need to use a CacheResolver to accomplish this.
package com.x.y.z;
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.AbstractCacheResolver;
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
import java.util.Collection;
import java.util.Collections;
public class MyCacheResolver extends AbstractCacheResolver {
public MyCacheResolver() {
}
public MyCacheResolver(CacheManager cacheManager) {
super(cacheManager);
}
#Override
protected Collection<String> getCacheNames(CacheOperationInvocationContext<?> cacheOperationInvocationContext) {
if(cacheOperationInvocationContext.getTarget() instanceof RoleManager) {
return Collections.singleton(((RoleManager) cacheOperationInvocationContext.getTarget()).getName());
}
return Collections.singleton("user");
}
}
We wire this up by adding a new bean definition
<bean id="myCacheResolver" class="com.x.y.z.MyCacheResolver">
<constructor-arg index="0" ref="cacheManager"/>
</bean>
And change the annotation in RoleManager to
#Cacheable(cacheResolver="myCacheResolver")
Once we do this, however, we get the following exception when the isAllowed method is invoked:
java.lang.NullPointerException
at org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils.qualifiedBeanOfType(BeanFactoryAnnotationUtils.java:57)
at org.springframework.cache.interceptor.CacheAspectSupport.getBean(CacheAspectSupport.java:282)
at org.springframework.cache.interceptor.CacheAspectSupport.getCacheOperationMetadata(CacheAspectSupport.java:254)
at org.springframework.cache.interceptor.CacheAspectSupport.getOperationContext(CacheAspectSupport.java:226)
at org.springframework.cache.interceptor.CacheAspectSupport$CacheOperationContexts.<init>(CacheAspectSupport.java:500)
at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:299)
at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy61.isAllowed(Unknown Source)
at com.x.y.z.RoleManager.isAllowed(CompositeRoleManager.java:61)
When I look at the CacheAspectSupport class from the stack trace, I see that it has a member, applicationContext, which is null.
protected <T> T getBean(String beanName, Class<T> expectedType) {
return BeanFactoryAnnotationUtils.qualifiedBeanOfType(this.applicationContext, expectedType, beanName);
}
This seems like a bug in Spring to me since we do not use ApplicationContexts, and yet caching works until we need to use a CacheResolver. I've looked over the documentation and I see no mention that one must use ApplicationContexts in order to use the Spring caching abstraction.
I guess my question is, has anyone experienced this problem, and if so, what did you do to resolve it? We absolutely cannot use ApplicationContexts in our application, and I'd rather not throw out a perfectly usable abstraction and code directly to the ehcache (or JSR-107) APIs.
Thanks in advance!
Spring 4.3 has fixed the problem by adding a setBeanFactory() method and using the BeanFactory thus set to call the CacheResolvers. Unfortunately I am unable to update our Spring library code to 4.3 at this time, but it will work when we are able to upgrade in the future.

Property file in spring mvc

I have a property file with key value pairs:
key1=value1
key2=value2
Now, in my controller, I want to directly print the value of a property file (of course after loading the property file using web.xml / app-servlet.xml), like:
System.out.printl(${key1});
Is it possible to do that?
If not, I want to create an interface with all constant variable to read values from property file. How do I do it??
public interface MyConstants
{
#Value("${key1}")
public static final KEY_1="";
}
But as expected only empty string is assigned.
How do I solve this issue? Or, what is the best way to using property files to retrieve values? Thanks in advance...
There are two reasons why having an interface for 'MyConstants' instead of a class is incorrect :
1) Spring cannot inject values to an interface which has no implementation. Simply because you wont be able instantiate the interface. Remember, Spring is just a factory and it can play only with 'things' which can be instantiated.
2) Another reason is that having an interface for storing your constants is an anti-pattern in itself. That is not what interfaces are designed for. You might want to refer to the Constant interface anti-pattern.
http://en.wikipedia.org/wiki/Constant_interface
It's possible! You need to use the util namespace in your app-servlet.xml as below:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd">
<util:properties id="props" location="classpath:yourfile.properties" />
<!-- other -->
</beans>
And your controller is something like
#org.springframework.beans.factory.annotation.Value("#{props.key1}")
public void setFoo(String foo) {
System.out.println("props.key1: " + foo);
}
update for another way:
You also can use namespace context
<context:property-placeholder location="classpath:yourfile.properties" />
In controller, declare a property as below
#Value("${pros.key1}")
private String foo;
Creating a ''Constants'' class / interface is a widely used approach, but I think its a flawed approach. It creates a weird coupling where classes from different layers in your system suddenly start depending on one Constants class. It also becomes difficult to understand by looking at the constants class, as to which constant is being used by who? Not to mention the fact that it completely mocks abstraction. You suddenly have a constants class which contains information about the error message to show on the jsp, username and password of a third party api, thread pool size etc.. all in one "I know everything" class
So avoid a constant class / interface as far as possible. Look at your controllers / services, if a particular service class needs a particular configuration value that you want exposed in a property file, inject it into the class and store it as an instance level constant. This design is much cleaner from an abstraction point of view, it also helps to unit test this class easily.
In Spring, you can create a handle to a property file as follows:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:my-application.properties" />
</bean>
As the code suggests, you can mention multiple property files here. After you do this, you can reference a key from the mentioned property file, elsewhere in the context like so:
<bean id="xx" class="com.xx.SomeClass" p:imageUrl="${categories.images}"/>
The SomeClass instance here has a property called imageUrl which is now injected with the value mentioned against the categories.images key from the property file called my-application.properties
Hope this helps.

Categories