How can I get all active sessions with Apache Shiro ehcache-terracotta? - ehcache

In my maven application, I want to provide that if a user log in a new account, another account that already open will be log out. I am using Apache shiro, ehcache and terracotta for this.
I want to get all active sessions to check whether that user is logined previously. I can take users and their session id from all servers using Hazelcast(this works), but i can not take session (because it is not serializable)
I use clustered servers in project. Servers have same ip address but their ports are different.
x.x.x.x:7002 -->server1,
x.x.x.x:7003 -->server2
how can i define that ports in ehcache configuration file?
how can i take all active sessions from that ports with terracotta?
Please help
ehcache.xml:
<ehcache>
<terracottaConfig url="x.x.x.x:9510"/>
<diskStore path="java.io.tmpdir/shiro-ehcache"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120">
<!-- <terracotta/>-->
</defaultCache>
<cache name="myactiveSessionCache"
maxElementsInMemory="10000"
eternal="true"
timeToLiveSeconds="0"
timeToIdleSeconds="0"
diskPersistent="false"
overflowToDisk="false"
diskExpiryThreadIntervalSeconds="600">
<!-- <terracotta/>-->
</cache>
<!-- Add more cache entries as desired, for example,
Realm authc/authz caching: -->
</ehcache>
shiro.ini:
sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
sessionDAO = org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO
sessionManager.sessionDAO = $sessionDAO
cacheManager = org.apache.shiro.cache.ehcache.EhCacheManager
cacheManager .cacheManagerConfigFile = classpath:ehcache.xml
securityManager.cacheManager = $cacheManager
securityManager.sessionManager = $sessionManager
Login.java:
User newuser = ..//get from db with username and password
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
Subject currentUser = new Subject.Builder().buildSubject();
token.setRememberMe(true);
currentUser = getSubject();
currentUser.login(token);
//at here, i take all active users and their's session id with Hazelcast.
//and check that is 'newuser' in all active users list?
if (!activeuserList.containsKey(newuser)) {
activeuserList.put(newuser, currentUser.getSession().getId());
} else {
Serializable s = activeuserList.get(newuser);
isSessionActive(s);
}
}
...
//check -- is session active?
public void isSessionActive(Serializable s) {
DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) SecurityUtils.getSecurityManager();
DefaultWebSessionManager sessionManager = (DefaultWebSessionManager) securityManager.getSessionManager();
EnterpriseCacheSessionDAO sessionDao = (EnterpriseCacheSessionDAO) sessionManager.getSessionDAO();
Cache<Serializable, Session> activeSessionsCache = sessionDao.getActiveSessionsCache();
if (activeSessionsCache.keys().contains(s)) {
//previous session is closed.
activeuserList.remove(newuser);
activeSessionsCache.get(s).stop();
//new session id is added to list
activeuserList.put(newuser, currentUser.getSession().getId());
}
}

Related

Ehcache jsr107:defaults not applying to programmatically created caches

Based on my findings in my previous SO question, I'm trying to setup JCaches in a mix of declarative and imperative configuration, to limit the max size of caches declaratively.
I keep a list of the caches and the duration (TTL) for their entries in my application.yaml, which I get with a property loader. I then create my caches with the code below:
#Bean
public List<javax.cache.Cache<Object, Object>> getCaches() {
javax.cache.CacheManager cacheManager = this.getCacheManager();
List<Cache<Object, Object>> caches = new ArrayList();
Map<String, String> cacheconfigs = //I populate this with a list of cache names and durations;
Set<String> keySet = cacheconfigs.keySet();
Iterator i$ = keySet.iterator();
while(i$.hasNext()) {
String key = (String)i$.next();
String durationMinutes = (String)cacheconfigs.get(key);
caches.add((new GenericDefaultCacheConfigurator.GenericDefaultCacheConfig(key, new Duration(TimeUnit.MINUTES, Long.valueOf(durationMinutes)))).getCache(cacheManager));
}
return caches;
}
#Bean
public CacheManager getCacheManager() {
return Caching.getCachingProvider().getCacheManager();
}
private class GenericDefaultCacheConfig {
public GenericDefaultCacheConfig(String cacheName, Duration duration) {
public GenericDefaultCacheConfig(String id, Duration duration, Factory expiryPolicyFactory) {
CACHE_ID = id;
DURATION = duration;
EXPIRY_POLICY = expiryPolicyFactory;
}
private MutableConfiguration<Object, Object> getCacheConfiguration() {
return new MutableConfiguration<Object, Object>()
.setTypes(Object.class, Object.class)
.setStoreByValue(true)
.setExpiryPolicyFactory(EXPIRY_POLICY);
}
public Cache<Object, Object> getCache(CacheManager cacheManager) {
CacheManager cm = cacheManager;
Cache<K, V> cache = cm.getCache(CACHE_ID, Object.class, Object.class);
if (cache == null)
cache = cm.createCache(CACHE_ID, getCacheConfiguration());
return cache;
}
}
I try limiting the cache size with the following ehcache.xml:
<config
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns='http://www.ehcache.org/v3'
xmlns:jsr107='http://www.ehcache.org/v3/jsr107'
xsi:schemaLocation="
http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd
http://www.ehcache.org/v3/jsr107 http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd">
<service>
<jsr107:defaults default-template="heap-cache" enable-management="true" enable-statistics="true">
</jsr107:defaults>
</service>
<cache-template name="heap-cache">
<resources>
<heap unit="entries">20</heap>
</resources>
</cache-template> </config>
I set the following declaration in my application.yaml:
spring:
cache:
jcache:
config: classpath:ehcache.xml
However, my caches don't honor the imposed limit. I validate with the following test:
#Test
public void testGetCacheMaxSize() {
Cache<Object, Object> cache = getCache(MY_CACHE); //I get a cache of type Eh107Cache[myCache]
CacheRuntimeConfiguration<Object, Object> ehcacheConfig = (CacheRuntimeConfiguration<Object, Object>)cache.getConfiguration(
Eh107Configuration.class).unwrap(CacheRuntimeConfiguration.class);
long size = ehcacheConfig.getResourcePools().getPoolForResource(ResourceType.Core.HEAP).getSize(); //Returns 9223372036854775807 instead of the expected 20
for(int i=0; i<30; i++)
commonDataService.getAllStates("ENTRY_"+i);
Map<Object, Object> cachedElements = cacheManagerService.getCachedElements(MY_CACHE);
assertTrue(cachedElements.size().equals(20)); //size() returns 30
}
Can somebody point out what I am doing wrong? Thanks in advance.
The issue comes from getting the cache manager as:
Caching.getCachingProvider().getCacheManager();
By setting the config file URI on cache manager's initialization I got it to work:
cachingProvider = Caching.getCachingProvider();
configFileURI = resourceLoader.getResource(configFilePath).getURI();
cacheManager = cachingProvider.getCacheManager(configFileURI, cachingProvider.getDefaultClassLoader());
I was under the expectation that Spring Boot would automatically create the cache manager based on the configuration file included given in property spring.cache.jcache.config,
but that was not the case because I get the cache manager as described above instead of simply auto-wiring it and letting Spring create it.

Gradual performance degradation of data import using Spring batch, EhCache, and Hibernate

I am using the Spring MVC 4, Spring Batch 3, Hibernate 5, and Ehcache 2.8. Using Spring Batch, I am importing a lot of data from CSV to the DB using Hibernate at chunks of 1000. For additional information, data from the CSV refers to other tables that is why i have a lot of DB queries before the actual insert.
At first, the interval between chunks are small, between 10 seconds. Gradually, after around 100,000 items, the interval between the items are more than 1 minute. I suspect that it is some caching issue as this is gradual degradation. My current hibernate stats are
2nd Level Cache Hit %: 97.8839177750907
Query Cache Hit %: 54.206282344445775
Query Max Time: 4.08s at a query used in the import
Here is my ehcache.xml configuration
<defaultCache
eternal="false"
timeToIdleSeconds="180"
timeToLiveSeconds="240"
maxEntriesLocalHeap="10000"
maxEntriesLocalDisk="50000"
>
<persistence strategy="localTempSwap" />
</defaultCache>
<cache name="org.hibernate.cache.internal.StandardQueryCache"
maxEntriesLocalHeap="10000"
maxEntriesLocalDisk="50000"
eternal="false"
timeToIdleSeconds="180"
timeToLiveSeconds="240"
>
<persistence strategy="localTempSwap" />
</cache>
<cache name="org.hibernate.cache.spi.UpdateTimestampsCache"
eternal="false"
maxEntriesLocalHeap="0"
/>
[EDIT] and here is the source code for the batch job
#Bean
#StepScope
public static FlatFileItemReader<CsvPayment> paymentReader( #Value( "#{jobParameters[fullPathFileName]}" ) String pathToFile,
#Value( "#{jobParameters[delimeter]}" ) String delimeter,
#Value( "#{jobParameters[skipItems]}" ) Long skipItems,
#Value( "#{jobParameters[limitItems]}" ) Long limitItems )
{
FlatFileItemReader<CsvPayment> reader = new FlatFileItemReader<>();
reader.setResource( new FileSystemResource( pathToFile ) );
reader.setEncoding( GlobalConstants.UTF8 );
reader.setMaxItemCount( limitItems.intValue() );
reader.setLinesToSkip( skipItems.intValue() );
reader.setLineMapper( new CsvPaymentLineMapper( delimeter ) );
return reader;
}
#Bean( "importPayment" )
public Job importPayment( ItemReader<CsvPayment> paymentReader )
{
return jobBuilderFactory.get( "paymentReader" ).incrementer( new RunIdIncrementer() ).flow(
paymentStep1( paymentReader ) ).end().build();
}
#Bean
public Step paymentStep1( ItemReader<CsvPayment> paymentReader )
{
return stepBuilderFactory.get( "paymentStep1" ).<CsvPayment, OccupancyPayment> chunk( CHUNK_SIZE ).reader(
paymentReader ).processor( itemProcessor ).faultTolerant().listener(
new ChunkListenerImpl( logger ) ).writer( itemWriter() ).build();
}
#Bean
public HibernateItemWriter<OccupancyPayment> itemWriter()
{
HibernateItemWriter<OccupancyPayment> itemWriter = new HibernateItemWriter<>();
itemWriter.setSessionFactory( sessionFactory );
itemWriter.setClearSession( true );
return itemWriter;
}
Hopefully, someone could point me to the right direction. Thanks.
The session cache is growing and making your app slower and consuming more memory.
Try this:
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
Customer customer = new Customer(.....);
session.save(customer);
if ( i % 20 == 0 ) { //20, same as the JDBC batch size
//flush a batch of inserts and release memory:
session.flush();
session.clear();
}
}
tx.commit();
session.close();
Example taken from here and I confirm to you we use the same solution and it works good.
The only downside of the solution is that sometimes you may get NonUniqueObjectException from Hibernate. It comes as a consequence of the session.clear() which makes all Hibernate instances detached. In such a case, you definitely need to read this and probably call session.refresh(instance).

Spring 3 processing event for all active

I am trying to dinamically update the users credentials in next way: When de admin pushes a button a new role is added to every standard user in database. The disconnected users have not problem, because when they login the custom authentication provider loads all his roles from db, but the logued users cant access to the new available sections, because the authentication object has not the new role. In order to solve this i tried many mechanisms, but the rightfull and less intrusive i saw was using listeners. Here is the idea: when the admin pushes the button, the db is updated and a new custom event is triggered and processed. This event produces, in theory, a reauthentication for each active user AND IT DOES, just that only for the user who triggered the event(the admin).
Now, i want to know why does it, why the event doesnt apply to every SecurityContextHolder and just to the one who triggered it. I though the problem was in the scope of the bean, so i gave it a session scope, but throws an error. Please, if anyone can help me.
Here is the properly code
My dispatcher servlet
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<context:component-scan base-package="printer">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/jsp/"
p:suffix=".jsp" />
//nothing that matters here
<bean id="reauthenticating" class="printer.Security.Events.ReauthenticatingEventsPostProcessor" scope="prototype"/>
The event
public class ReauthenticatingUseronRoleChangeEvent extends ApplicationEvent {
private static final long serialVersionUID = 1L;
private String roleType;
private String actionType;
public ReauthenticatingUseronRoleChangeEvent(Object source,String roleType, String actionType) {
super(source);
this.roleType = roleType;
this.actionType = actionType;
}
public String getRoleType() {
return roleType;
}
public String getActionType() {
return actionType;
}
The event trigger which is my UserService
public class UserService_Impl implements UserService,ApplicationEventPublisherAware{
#Override
public void publishAccessToDownloadEvent() {
.....
enter code here
#Override
public void publishAccessToDownloadEvent() {
publisher.publishEvent(new ReauthenticatingUseronRoleChangeEvent(this, "ROLE_DOWNLOAD", "add"));
}
Here is the event listener. This is where i get lost, doesnt suppose it is executed for every user????
public class ReauthenticatingEventsPostProcessor implements ApplicationListener<ReauthenticatingUseronRoleChangeEvent> {
#Autowired
#Qualifier("userDao")
UserDAO userDao;
#Override
public void onApplicationEvent(ReauthenticatingUseronRoleChangeEvent e) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority> (auth.getAuthorities());
Role r=new Role();
r.setRole(e.getRoleType());
authorities.add(r);
Authentication newAuth = new UsernamePasswordAuthenticationToken(auth.getPrincipal(),auth.getCredentials(),authorities);
SecurityContextHolder.getContext().setAuthentication(newAuth);
}
}
#Override
public void setApplicationEventPublisher(ApplicationEventPublisher aep) {
this.publisher=aep;}
As i said above, the listener works fine, just that not as suppossed. Instead execute him for each user, does it for the user who triggered the event only.

EhCache Replication with Websphere MQ

Is it possible to use EHCache replication over JMS with IBM's Websphere MQ 7.0? I have a working sample using ActiveMQ (out of the box) but am not sure how to configure EHCache to work with Websphere MQ.
Below is a sample of my working ehcache.xml and ActiveMQInitialContextFactory configuration for ActiveMQ.
_______________________________ActiveMQ Sample_____________________________
ehcache.xml
<!-- ActiveMQ configuration out of the box -->
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.jms.JMSCacheManagerPeerProviderFactory"
properties="initialContextFactoryName=com.hcsc.contextfactory.ActiveMQContextFactory,
providerURL=tcp://LJGO4RAJ:61616,
replicationTopicConnectionFactoryBindingName=topicConnectionFactory,
getQueueConnectionFactoryBindingName=queueConnectionFactory,
getQueueBindingName=ehcache,
listenToTopic=true,
replicationTopicBindingName=ehcache"
propertySeparator="," />
<cache name="distributedCache" maxEntriesLocalHeap="1000"
eternal="true" overflowToDisk="false">
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.jms.JMSCacheReplicatorFactory"
properties="replicateAsynchronously=true,
replicatePuts=true,
replicateUpdates=true,
replicateUpdatesViaCopy=true,
replicateRemovals=true, asynchronousReplicationIntervalMillis=500"
propertySeparator="-" />
</cache>
ActiveMQInitialContextFactory implementation (from ehcache.xml)
public class ActiveMQContextFactory extends ActiveMQInitialContextFactory {
#Override
public Context getInitialContext(Hashtable environment)
throws NamingException {
Map<String, Object> data = new ConcurrentHashMap<String, Object>();
// Configure the Topic connection factory binding name
String factoryBindingName = (String) environment
.get(JMSUtil.TOPIC_CONNECTION_FACTORY_BINDING_NAME);
try {
data.put(factoryBindingName, createConnectionFactory(environment));
} catch (URISyntaxException e) {
throw new NamingException("Error initialisating ConnectionFactory"
+ " with message " + e.getMessage());
}
String topicBindingName = (String) environment
.get(JMSUtil.REPLICATION_TOPIC_BINDING_NAME);
data.put(topicBindingName, createTopic(topicBindingName));
// Configure queue connection factory binding name
String getQueueConnectionfactoryBindingName = (String) environment
.get(JMSUtil.GET_QUEUE_CONNECTION_FACTORY_BINDING_NAME);
if (getQueueConnectionfactoryBindingName != null) {
try {
data.put(getQueueConnectionfactoryBindingName,
createConnectionFactory(environment));
} catch (URISyntaxException e) {
throw new NamingException(
"Error initialisating TopicConnectionFactory with message "
+ e.getMessage());
}
}
String getQueueBindingName = (String) environment
.get(JMSUtil.GET_QUEUE_BINDING_NAME);
if (getQueueBindingName != null) {
data.put(getQueueBindingName, createQueue(getQueueBindingName));
}
return createContext(environment, data);
}
}
The above configuration works perfectly, but I need to convert it to use Websphere MQ. Below is what I have so far...
_______________________________WebsphereMQ Sample__________________________
ehcache.xml
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.jms.JMSCacheManagerPeerProviderFactory"
properties="initialContextFactoryName=com.hcsc.contextfactory.WebSphereMQContextFactory,
providerURL=tcp://LJGO4RAJ:1415,
replicationTopicConnectionFactoryBindingName=topicConnectionFactory,
getQueueConnectionFactoryBindingName=queueConnectionFactory,
getQueueBindingName=myqueue,
listenToTopic=true,
replicationTopicBindingName=ehcache"
propertySeparator="," />
<cache name="distributedCache" maxEntriesLocalHeap="1000"
eternal="true" overflowToDisk="false">
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.jms.JMSCacheReplicatorFactory"
properties="replicateAsynchronously=true,
replicatePuts=true,
replicateUpdates=true,
replicateUpdatesViaCopy=true,
replicateRemovals=true,
asynchronousReplicationIntervalMillis=500"
propertySeparator="-" />
</cache>
MQQueueConnectionFactory (I assume I need this similar to ActiveMQContextFactory)
public class WebSphereMQContextFactory extends MQQueueConnectionFactory {
// TODO - NOT SURE HOW TO CONFIGURE THIS FOR EHCACHE?
}
I believe I will need a MQQueueConnectionFactory similar to how I created one for ActiveMQ, am I going in the right direction? I cannot find any sample code to achieve this.

ASP.NET Membership for one website but multiple and potentially unknown sql server instance

This my problem. I have 1 and only 1 website for multiple customer. To access to their data, customers use urls like this :
http://customer1.mysite.com
http://customer2.mysite.com
etc
Each customer have a SqlServer instance with their data.
Depends to the URL the website connect to the right sql server instance. This is good.
My issue is about Membership, each instance have is own "membership database". In my webconfig I configure a dummy section like this :
<membership defaultProvider="MyMembershipProvider">
<providers>
<clear />
<add name="MyMembershipProvider"
type="MapApp.MyMembershipProvider, MyApp"
enablePasswordRetrieval="true"
enablePasswordReset="true"
requiresQuestionAndAnswer="false"
applicationName="/"
requiresUniqueEmail="false"
passwordFormat="Encrypted"
minRequiredPasswordLength="5"
minRequiredNonalphanumericCharacters="0"
passwordAttemptWindow="10"
passwordStrengthRegularExpression=""
connectionStringName="A_DATABASE" />
</providers>
</membership>
Also I have a custom Membershipprovider with code like this :
public class MyMembershipProvider : SqlMembershipProvider
{
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
base.Initialize(name, config);
// Update the private connection string field in the base class.
string connectionString = "my new connection string depdend of the customer"
// Set private property of Membership provider.
FieldInfo connectionStringField = GetType().BaseType.GetField("_sqlConnectionString", BindingFlags.Instance | BindingFlags.NonPublic);
connectionStringField.SetValue(this, connectionString);
}
}
but is not enough a have some issues like "already instanciate" when I call my custom membership provider.
Someone can help me ? Please...
Sorry for my bad english in this long post
This is an interesting problem. Seems like you are attempting to use a different connection string for the database, based on the URL. This might be a bit less elegant, but how about getting the code for the sql membership provider and modifying it so that the connection string which it uses is based on the users URL. I think this could be a good work around if you are truly only using one asp.net application.
I think I have the solution but it's a bit strange.
In my customMemberShipProvider class I must have an empty constructor even if I never call it. And I need another constructor (with args even if I never use it).
So this is my code :
public class myMembershipProvider : SqlMembershipProvider
{
public myMembershipProvider()
{
//never use
}
public myMembershipProvider(string dummyArg)
{
string configPath = "~/web.config";
Configuration config = WebConfigurationManager.OpenWebConfiguration(configPath);
MembershipSection section = (MembershipSection)config.GetSection("system.web/membership");
ProviderSettingsCollection settings = section.Providers;
NameValueCollection membershipParams = settings[section.DefaultProvider].Parameters;
if ((HttpContext.Current.Session != null) && (HttpContext.Current.Session["SubDomainInstanceName"] != null) && (!string.IsNullOrEmpty(HttpContext.Current.Session["SubDomainInstanceName"].ToString())))
{
membershipParams.Add("Instance", HttpContext.Current.Session["SubDomainInstanceName"].ToString());
}
else
{
membershipParams.Add("Instance", "default");
}
Initialize(section.DefaultProvider, membershipParams);
}
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
//need to remove the key before call Initialize method
string instance = config["Instance"];
config.Remove("Instance");
base.Initialize(name, config);
// Update the private connection string field in the base class.
string connectionString = //my specific connectionstring;
// Set private property of Membership provider.
FieldInfo connectionStringField = GetType().BaseType.GetField("_sqlConnectionString", BindingFlags.Instance | BindingFlags.NonPublic);
connectionStringField.SetValue(this, connectionString);
}
Regards

Resources