Spring Cache a List of object with Condition getting IllegalArgumentException - spring

I want to cache a List of Category when level == 0, but keeping getting IllegalArgumentException. What am I missing?
In Service class:
#Override
#Transactional(readOnly = true)
#Cacheable(value="categories", condition="#level == 0")
public List<Category> findCategoryByLevel(int level) throws DataAccessException {
return categoryRepository.findCategoryByLevel(level);
}
Error:
java.lang.IllegalArgumentException: Cannot find cache named 'categories' for CacheableOperation[public java.util.List com.mySite.service.DidicityServiceImpl.findCategoryByLevel(int) throws org.springframework.dao.DataAccessException] caches=[categories] | key='' | condition='#level == 0' | unless=''

What caching provider are you using in Spring's Cache Abstraction? (I.e. ehcache, Guava, Hazelcast, etc)
It appears you are missing an explicit "Cache" definition and instance in your actual caching provider. For example, when using Pivotal GemFire as a caching provider in Spring's Cache Abstraction, you need to define a Region (a.k.a. Cache in the Spring Cache Abstraction), using your example above, like so...
<gfe:cache ...>
<gfe:replicated-region id="categories" persistent="false"...>
...
</gfe:replicated-region>
Spring Data GemFire goes onto lookup the "Cache" when the cached application service|repository method is invoked, and so the actual backing "Cache" (i.e. the GemFire Region) must exist, otherwise the Spring Cache Abstraction throws an IllegalArgumentException.
So, by way of a more explicit example, I wrote the following test...
/*
* Copyright 2014-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.spring.cache;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.spring.cache.CachingWithConcurrentMapUsingExplicitlyNamedCachesTest.ApplicationConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* The CachingWithConcurrentMapUsingExplicitlyNamedCachesTest class is a test suite of test cases testing the contract
* and functionality of Spring Cache Abstraction using the ConcurrentMap-based Cache Management Strategy
* with explicitly named "Caches".
*
* NOTE: when the Cache(s) [is|are] explicitly named using the ConcurrentMapCacheManager, then "dynamic" is disabled
* and corresponding the named Cache in the #Cacheable annotation of the cached service method must exist
* (or be declared). If no explicitly named Caches are provided to the ConcurrentMapManager constructor, then dynamic
* is enabled and the Cache will be created at runtime, on the fly.
*
* #author John Blum
* #see org.junit.Test
* #see org.junit.runner.RunWith
* #see org.springframework.cache.Cache
* #see org.springframework.cache.CacheManager
* #see org.springframework.cache.annotation.Cacheable
* #see org.springframework.cache.annotation.EnableCaching
* #see org.springframework.cache.concurrent.ConcurrentMapCacheManager
* #see org.springframework.context.annotation.Bean
* #see org.springframework.context.annotation.Configuration
* #see org.springframework.test.context.ContextConfiguration
* #see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
* #since 1.0.0
*/
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = ApplicationConfiguration.class)
#SuppressWarnings("unused")
public class CachingWithConcurrentMapUsingExplicitlyNamedCachesTest {
#Autowired
private NumberCategoryService numberCategoryService;
#Test
public void numberCategoryCaching() {
assertThat(numberCategoryService.isCacheMiss(), is(false));
List<NumberCategory> twoCategories = numberCategoryService.classify(2.0);
assertThat(twoCategories, is(notNullValue()));
assertThat(twoCategories.size(), is(equalTo(3)));
assertThat(twoCategories.containsAll(Arrays.asList(
NumberCategory.EVEN, NumberCategory.POSITIVE, NumberCategory.WHOLE)), is(true));
assertThat(numberCategoryService.isCacheMiss(), is(true));
List<NumberCategory> twoCategoriesAgain = numberCategoryService.classify(2.0);
assertThat(twoCategoriesAgain, is(sameInstance(twoCategories)));
assertThat(numberCategoryService.isCacheMiss(), is(false));
List<NumberCategory> negativeThreePointFiveCategories = numberCategoryService.classify(-3.5);
assertThat(negativeThreePointFiveCategories, is(notNullValue()));
assertThat(negativeThreePointFiveCategories.size(), is(equalTo(3)));
assertThat(negativeThreePointFiveCategories.containsAll(Arrays.asList(
NumberCategory.ODD, NumberCategory.NEGATIVE, NumberCategory.FLOATING)), is(true));
assertThat(numberCategoryService.isCacheMiss(), is(true));
}
#Configuration
#EnableCaching
public static class ApplicationConfiguration {
#Bean
public CacheManager cacheManager() {
//return new ConcurrentMapCacheManager("Categories");
return new ConcurrentMapCacheManager("Temporary");
}
#Bean
public NumberCategoryService numberCategoryService() {
return new NumberCategoryService();
}
}
#Service
public static class NumberCategoryService {
private volatile boolean cacheMiss;
public boolean isCacheMiss() {
boolean localCacheMiss = this.cacheMiss;
this.cacheMiss = false;
return localCacheMiss;
}
protected void setCacheMiss() {
this.cacheMiss = true;
}
#Cacheable("Categories")
public List<NumberCategory> classify(double number) {
setCacheMiss();
List<NumberCategory> categories = new ArrayList<>(3);
categories.add(isEven(number) ? NumberCategory.EVEN : NumberCategory.ODD);
categories.add(isPositive(number) ? NumberCategory.POSITIVE : NumberCategory.NEGATIVE);
categories.add(isWhole(number) ? NumberCategory.WHOLE : NumberCategory.FLOATING);
return categories;
}
protected boolean isEven(double number) {
return (isWhole(number) && Math.abs(number) % 2 == 0);
}
protected boolean isFloating(double number) {
return !isWhole(number);
}
protected boolean isNegative(double number) {
return (number < 0);
}
protected boolean isOdd(double number) {
return !isEven(number);
}
protected boolean isPositive(double number) {
return (number > 0);
}
protected boolean isWhole(double number) {
return (number == Math.floor(number));
}
}
public enum NumberCategory {
EVEN,
FLOATING,
NEGATIVE,
ODD,
POSITIVE,
WHOLE
}
}
This test example is currently setup to throw the IllegalArgumentException. If you change this...
return new ConcurrentMapCacheManager("Temporary");
To this...
return new ConcurrentMapCacheManager("Categories");
Then all is well.
Hopefully this adequately illustrates the problem you are having and how to fix it.
Cheers,
John

Looks like you want to use the key as a static hardcoded string.
Try the following "'categories'"
#Override
#Transactional(readOnly = true)
#Cacheable(value="'categories'", condition="#level == 0")
public List<Category> findCategoryByLevel(int level) throws DataAccessException {
return categoryRepository.findCategoryByLevel(level);
}

Related

Android Architecture Components(MVVM) - Ideal way to deal with remote and local data using repository pattern

I have browsed through a lot of sample code available for the new architecture components but I am still facing some issues in setting up my project.
I need to fetch data from my remote service and save it to the room database. I want my view to observe just a single live-data list. My AppRepository handles the RemoteRepository and LocalRepository. Remote Repository has a method fetchMovies() which receives a list of movies from the web-service. I want to save this list in the room database, at present my RemoteRepository class does this.
public void fetchMovieFromRemote(int page){
movieService.fetchPopularMovies(ApiConstants.BASE_URL, page).enqueue(new Callback<List<Movie>>() {
#Override
public void onResponse(Call<List<Movie>> call, Response<List<Movie>> response) {
int statusCode = response.code();
if (statusCode == 200){
mLocalRepository.insert(response.body());
}
}
#Override
public void onFailure(Call<List<Movie>> call, Throwable t) {
}
});
}
According to my understanding, ideally the Remote and Local repositories should be independent and this work should be done by the AppRepository class.
One way to do this is to use callbacks but I want to use live-data to do this. Should the fetchMovieFromRemote(int page) method return a live-data for that, but in that case how to handle it in my viewmodel which at present has a live-data of the list of movies which is returned by room.
#Query("SELECT * from movie ORDER BY id ASC")
LiveData<List<Movie>> getAllWords();
I am new to MVVM, kindly guide me on what's the ideal approach for this architecture.
I have adopted the pattern that Google uses in their example for a repository which gives you a single source of truth (your Room database).
It is discussed here: https://developer.android.com/jetpack/docs/guide
The key part to take notice of is the NetworkBoundResource class (Google sample: https://github.com/googlesamples/android-architecture-components/blob/master/GithubBrowserSample/app/src/main/java/com/android/example/github/repository/NetworkBoundResource.kt). This example from Google is in Kotlin I did find a Java example.
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package iammert.com.androidarchitecture.data;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MediatorLiveData;
import android.os.AsyncTask;
import android.support.annotation.MainThread;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public abstract class NetworkBoundResource<ResultType, RequestType> {
private final MediatorLiveData<Resource<ResultType>> result = new MediatorLiveData<>();
#MainThread
NetworkBoundResource() {
result.setValue(Resource.loading(null));
LiveData<ResultType> dbSource = loadFromDb();
result.addSource(dbSource, data -> {
result.removeSource(dbSource);
if (shouldFetch(data)) {
fetchFromNetwork(dbSource);
} else {
result.addSource(dbSource, newData -> result.setValue(Resource.success(newData)));
}
});
}
private void fetchFromNetwork(final LiveData<ResultType> dbSource) {
result.addSource(dbSource, newData -> result.setValue(Resource.loading(newData)));
createCall().enqueue(new Callback<RequestType>() {
#Override
public void onResponse(Call<RequestType> call, Response<RequestType> response) {
result.removeSource(dbSource);
saveResultAndReInit(response.body());
}
#Override
public void onFailure(Call<RequestType> call, Throwable t) {
onFetchFailed();
result.removeSource(dbSource);
result.addSource(dbSource, newData -> result.setValue(Resource.error(t.getMessage(), newData)));
}
});
}
#MainThread
private void saveResultAndReInit(RequestType response) {
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... voids) {
saveCallResult(response);
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
result.addSource(loadFromDb(), newData -> result.setValue(Resource.success(newData)));
}
}.execute();
}
#WorkerThread
protected abstract void saveCallResult(#NonNull RequestType item);
#MainThread
protected boolean shouldFetch(#Nullable ResultType data) {
return true;
}
#NonNull
#MainThread
protected abstract LiveData<ResultType> loadFromDb();
#NonNull
#MainThread
protected abstract Call<RequestType> createCall();
#MainThread
protected void onFetchFailed() {
}
public final LiveData<Resource<ResultType>> getAsLiveData() {
return result;
}
}
This is a repo using that class:
package iammert.com.androidarchitecture.data;
import android.arch.lifecycle.LiveData;
import android.support.annotation.NonNull;
import java.util.List;
import javax.inject.Inject;
import iammert.com.androidarchitecture.data.local.dao.MovieDao;
import iammert.com.androidarchitecture.data.local.entity.MovieEntity;
import iammert.com.androidarchitecture.data.remote.MovieDBService;
import iammert.com.androidarchitecture.data.remote.model.MoviesResponse;
import retrofit2.Call;
/**
* Created by mertsimsek on 19/05/2017.
*/
public class MovieRepository {
private final MovieDao movieDao;
private final MovieDBService movieDBService;
#Inject
public MovieRepository(MovieDao movieDao, MovieDBService movieDBService) {
this.movieDao = movieDao;
this.movieDBService = movieDBService;
}
public LiveData<Resource<List<MovieEntity>>> loadPopularMovies() {
return new NetworkBoundResource<List<MovieEntity>, MoviesResponse>() {
#Override
protected void saveCallResult(#NonNull MoviesResponse item) {
movieDao.saveMovies(item.getResults());
}
#NonNull
#Override
protected LiveData<List<MovieEntity>> loadFromDb() {
return movieDao.loadMovies();
}
#NonNull
#Override
protected Call<MoviesResponse> createCall() {
return movieDBService.loadMovies();
}
}.getAsLiveData();
}
public LiveData<MovieEntity> getMovie(int id){
return movieDao.getMovie(id);
}
}
I will try to explain it briefly, you have a method in your Repo, say, loadMovies() that returns a LiveData list of movies from your repository. With NetworkBoundResource, the Room database is checked first, then the API is queried and the results are then loaded into the database. Once the database is updated, the LiveData that you are observing is updated with the new results.
This diagram shows the logical flow behind this:
As you can see above, you are observing the disk for changes, and receive an update when it does change.
I recommend reading through that Jetpack guide I linked previously as they will explain it in more detail. I believe that is what you were looking for.
Two way to communication between viewmodel and repository is
Using callback interface
Using Transformations.
Transformations has map and switchMap operators similar to RxJava
which can converts on livedata to different one
You can also use Mediator livedata to create your own custom
operators. MediatorLiveData is used for observing multiple livedata
sources and perform on there changes.
For more information about MVVVM and live data
Youtube
Medium post

AEM 6.3 : Creating Scheduler using OSGi R6 annotations

I have written a scheduler using OSGi R6 annotations but it doesn't seem to run :
package com.aem.sites.interfaces;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.AttributeType;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
#ObjectClassDefinition(name = "Scheduler Configuration for Weather", description = "Configuration file for Scheduler")
public #interface SchedulerConfiguration {
#AttributeDefinition(
name = "sample parameter",
description="Sample String parameter",
type = AttributeType.STRING
)
public String parameter() default "scheduler";
#AttributeDefinition(
name = "Concurrent",
description = "Schedule task concurrently",
type = AttributeType.BOOLEAN
)
boolean scheduler_concurrent() default true;
#AttributeDefinition(
name = "Expression",
description = "Cron-job expression. Default: run every minute.",
type = AttributeType.STRING
)
String scheduler_expression() default "0 * * * * ?";
}
and
package com.aem.sites.schedulers;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.metatype.annotations.Designate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.aem.sites.interfaces.SchedulerConfiguration;
#Component(immediate = true,
configurationPid = "com.aem.sites.schedulers.WeatherServiceScheduler")
#Designate(ocd=SchedulerConfiguration.class)
public class WeatherServiceScheduler implements Runnable {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private String myParameter;
#Override
public void run() {
logger.info("*******************************************Sample OSGi Scheduler is now running", myParameter);
}
#Activate
public void activate(SchedulerConfiguration config) {
logger.info("*******************************************weather service scheduler"+ myParameter);
myParameter = config.parameter();
}
}
I am following this https://github.com/nateyolles/aem-osgi-annotation-demo/blob/master/core/src/main/java/com/nateyolles/aem/osgiannotationdemo/core/schedulers/SampleOsgiScheduledTask.java but looks like I am doing something wrong here. Not sure what though.
Thanks in advance
In your WeatherSchedulerService class, you are not registering it as a service. Instead of configurationPid, you can do like this service = Runnable.class.
The correct way to create a SlingScheduler using OSGi R6 annotations is as follows -
Create your OSGi configuration class
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.AttributeType;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
#ObjectClassDefinition(name = "Sling Scheduler Configuration", description = "This configuration is used to demonstrates a sling scheduler in action")
public #interface SchedulerConfiguration {
#AttributeDefinition(
name = "Scheduler name",
description = "Name of the scheduler",
type = AttributeType.STRING)
public String name() default "Custom Sling Scheduler";
#AttributeDefinition(
name = "Enabled",
description = "Flag to enable/disable a scheduler",
type = AttributeType.STRING)
public boolean enabled() default false;
#AttributeDefinition(
name = "Cron expression",
description = "Cron expression used by the scheduler",
type = AttributeType.STRING)
public String cronExpression() default "0 * * * * ?";
#AttributeDefinition(
name = "Custom parameter",
description = "Custom parameter to showcase the usage of a sling scheduler",
type = AttributeType.STRING)
public String customParameter();
}
Create your Scheduler class as a service. For creating an OSGi service using R6 annotations we use #Component(service=<your-interface>.class,...).
Thus, create a service as follows
import org.apache.sling.commons.scheduler.ScheduleOptions;
import org.apache.sling.commons.scheduler.Scheduler;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.metatype.annotations.Designate;
import org.redquark.aem.learning.core.configurations.SchedulerConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#Component(immediate = true, service = Runnable.class)
#Designate(ocd = SchedulerConfiguration.class)
public class CustomScheduler implements Runnable {
// Logger
private final Logger log = LoggerFactory.getLogger(this.getClass());
// Custom parameter that is to be read from the configuration
private String customParameter;
// Id of the scheduler based on its name
private int schedulerId;
// Scheduler instance injected
#Reference
private Scheduler scheduler;
/**
* Activate method to initialize stuff
*
* #param schedulerConfiguration
*/
#Activate
protected void activate(SchedulerConfiguration schedulerConfiguration) {
schedulerId = schedulerConfiguration.name().hashCode();
customParameter = schedulerConfiguration.customParameter();
}
/**
* Modifies the scheduler id on modification
*
* #param schedulerConfiguration
*/
#Modified
protected void modified(SchedulerConfiguration schedulerConfiguration) {
// Removing scheduler
removeScheduler();
// Updating the scheduler id
schedulerId = schedulerConfiguration.name().hashCode();
// Again adding the scheduler
addScheduler(schedulerConfiguration);
}
/**
* This method deactivates the scheduler and removes it
*
* #param schedulerConfiguration
*/
#Deactivate
protected void deactivate(SchedulerConfiguration schedulerConfiguration) {
// Removing the scheduler
removeScheduler();
}
/**
* This method removes the scheduler
*/
private void removeScheduler() {
log.info("Removing scheduler: {}", schedulerId);
// Unscheduling/removing the scheduler
scheduler.unschedule(String.valueOf(schedulerId));
}
/**
* This method adds the scheduler
*
* #param schedulerConfiguration
*/
private void addScheduler(SchedulerConfiguration schedulerConfiguration) {
// Check if the scheduler is enabled
if (schedulerConfiguration.enabled()) {
// Scheduler option takes the cron expression as a parameter and run accordingly
ScheduleOptions scheduleOptions = scheduler.EXPR(schedulerConfiguration.cronExpression());
// Adding some parameters
scheduleOptions.name(schedulerConfiguration.name());
scheduleOptions.canRunConcurrently(false);
// Scheduling the job
scheduler.schedule(this, scheduleOptions);
log.info("Scheduler added");
} else {
log.info("Scheduler is disabled");
}
}
/**
* Overridden run method to execute Job
*/
#Override
public void run() {
log.info("Custom Scheduler is now running using the passed custom paratmeter, customParameter {}",
customParameter);
}
In the activate() method, we are reading the required values. Then we are getting the schedulerId from the scheduler name.
The modified() method recalculates the schedulerId in case the OSGi configuration is modified.
In the addScheduler() method, we are registering the scheduler using the Scheduler API.
For more information and step by step execution, you can see my blog post as well - Day 13: Schedulers in AEM
I hope this helps. Happy coding!
There is no need for configurationPid in the class annotation, and also you are missing service=Runnable.class which should follow immediate=true, i.e. the class declaration should look like:
#Component(immediate = true, service=Runnable.class)
#Designate(ocd=SchedulerConfiguration.class)
public class WeatherServiceScheduler implements Runnable {

spring-security session is not invalidating properly on timeout

I'm using spring-security-core 2.0 in my grails 2.4.4 application. I limited my concurrent session access of a user to 1.
So the problem is after session timeout of 30 minutes I couldn't login with that username and password, it will throw the concurrent session maximum exceeded exception.
I'm doubtful that, on the session timeout my logout is not working properly so that session can still be active.
I'm a newbi to spring-security so can anyone tell me what to do?
Here I'm giving What are changes I have made in my code to limit the concurrent access.
resources.groovy
// Place your Spring DSL code here
/*beans = {
messageSource(org.springframework.context.support.ReloadableResourceBundleMessageSource) { basename = "classpath:grails-app/src/resource_bundle" }
}*/
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy;
import com.custom.sessiontime.CustomSessionLogoutHandler
beans = {
sessionRegistry(SessionRegistryImpl)
customSessionLogoutHandler(CustomSessionLogoutHandler,ref('sessionRegistry'))
concurrentSessionControlAuthenticationStrategy(ConcurrentSessionControlAuthenticationStrategy,ref('sessionRegistry')){
exceptionIfMaximumExceeded = true
maximumSessions = 1
}
sessionFixationProtectionStrategy(SessionFixationProtectionStrategy){
migrateSessionAttributes = true
alwaysCreateSession = true
}
registerSessionAuthenticationStrategy(RegisterSessionAuthenticationStrategy,ref('sessionRegistry'))
sessionAuthenticationStrategy(CompositeSessionAuthenticationStrategy,[ref('concurrentSessionControlAuthenticationStrategy'),ref('sessionFixationProtectionStrategy'),ref('registerSessionAuthenticationStrategy')])
}
CustomSessionLogoutHandler
package com.custom.sessiontime
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.util.Assert;
import org.springframework.security.core.session.SessionRegistry;
/**
* {#link CustomSessionLogoutHandler} is in charge of removing the {#link SessionRegistry} upon logout. A
* new {#link SessionRegistry} will then be generated by the framework upon the next request.
*
* #author Mohd Qusyairi
* #since 0.1
*/
public final class CustomSessionLogoutHandler implements LogoutHandler {
private final SessionRegistry sessionRegistry;
/**
* Creates a new instance
* #param sessionRegistry the {#link SessionRegistry} to use
*/
public CustomSessionLogoutHandler(SessionRegistry sessionRegistry) {
Assert.notNull(sessionRegistry, "sessionRegistry cannot be null");
this.sessionRegistry = sessionRegistry;
}
/**
* Clears the {#link SessionRegistry}
*
* #see org.springframework.security.web.authentication.logout.LogoutHandler#logout(javax.servlet.http.HttpServletRequest,
* javax.servlet.http.HttpServletResponse,
* org.springframework.security.core.Authentication)
*/
public void logout(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) {
this.sessionRegistry.removeSessionInformation(request.getSession().getId());
}
}
Finally added below line to Config.groovy
grails.plugin.springsecurity.logout.handlerNames = ['customSessionLogoutHandler','securityContextLogoutHandler','rememberMeServices']

How to use a custom ssh key location with Spring Cloud Config

I am trying to setup a Spring Cloud Config server that uses a custom location for the ssh private key.
The reason i need to specify a custom location for the key is because the user running the application has no home directory ..so there is not way for me to use the default ~/.ssh directory for my key.
I know that there is the option of creating a read-only account and provide the user/password in the configuration but the ssh way seams more clean.Is there a way I can setup this?
After reading a lot more code... I found a relatively simple work around to allow you to set whatever SSH keys you want.
First: Create a class as follows:
/**
* #file FixedSshSessionFactory.java
*
* #date Aug 23, 2016 2:16:11 PM
* #author jzampieron
*/
import org.eclipse.jgit.transport.JschConfigSessionFactory;
import org.eclipse.jgit.transport.OpenSshConfig.Host;
import org.eclipse.jgit.util.FS;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
/**
* Short Desc Here.
*
* #author jzampieron
*
*/
public class FixedSshSessionFactory extends JschConfigSessionFactory
{
protected String[] identityKeyPaths;
/**
* #param string
*/
public FixedSshSessionFactory( String... identityKeyPaths )
{
this.identityKeyPaths = identityKeyPaths;
}
/* (non-Javadoc)
* #see org.eclipse.jgit.transport.JschConfigSessionFactory#configure(org.eclipse.jgit.transport.OpenSshConfig.Host, com.jcraft.jsch.Session)
*/
#Override
protected void configure( Host hc, Session session )
{
// nothing special needed here.
}
/* (non-Javadoc)
* #see org.eclipse.jgit.transport.JschConfigSessionFactory#getJSch(org.eclipse.jgit.transport.OpenSshConfig.Host, org.eclipse.jgit.util.FS)
*/
#Override
protected JSch getJSch( Host hc, FS fs ) throws JSchException
{
JSch jsch = super.getJSch( hc, fs );
// Clean out anything 'default' - any encrypted keys
// that are loaded by default before this will break.
jsch.removeAllIdentity();
for( final String identKeyPath : identityKeyPaths )
{
jsch.addIdentity( identKeyPath );
}
return jsch;
}
}
Then register it with jgit:
...
import org.eclipse.jgit.transport.SshSessionFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
#SpringBootApplication
#EnableConfigServer
public class ConfigserverApplication
{
public static void main(String[] args) {
URL res = ConfigserverApplication.class.getClassLoader().getResource( "keys/id_rsa" );
String path = res.getPath();
SshSessionFactory.setInstance( new FixedSshSessionFactory( path ) );
SpringApplication.run(ConfigserverApplication.class, args);
}
}
For this example I'm storing the keys in the src/main/resources/keys folder and
I'm using the class loader to get at them.
The removeAllIdentities is important b/c JSch was loading my default ssh key before the one I specified and then Spring Cloud was crashing out b/c its encrypted.
This allowed me to successfully authenticate with bitbucket.
The FixedSshSessionFactory solution of #Jeffrey Zampieron is good. However it won't work if packaging the spring boot app as a fat jar.
Polish it a bit for working with fat jar,
/**
* #file FixedSshSessionFactory.java
* #date Aug 23, 2016 2:16:11 PM
* #author jzampieron
*/
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jgit.transport.JschConfigSessionFactory;
import org.eclipse.jgit.transport.OpenSshConfig.Host;
import org.eclipse.jgit.util.FS;
import org.springframework.util.StreamUtils;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
/**
* Short Desc Here.
*
* #author jzampieron
*/
#Slf4j
public class FixedSshSessionFactory extends JschConfigSessionFactory {
protected URL[] identityKeyURLs;
/**
* #param url
*/
public FixedSshSessionFactory(URL... identityKeyURLs) {
this.identityKeyURLs = identityKeyURLs;
}
/* (non-Javadoc)
* #see org.eclipse.jgit.transport.JschConfigSessionFactory#configure(org.eclipse.jgit.transport.OpenSshConfig.Host, com.jcraft.jsch.Session)
*/
#Override
protected void configure(Host hc, Session session) {
// nothing special needed here.
}
/* (non-Javadoc)
* #see org.eclipse.jgit.transport.JschConfigSessionFactory#getJSch(org.eclipse.jgit.transport.OpenSshConfig.Host, org.eclipse.jgit.util.FS)
*/
#Override
protected JSch getJSch(Host hc, FS fs) throws JSchException {
JSch jsch = super.getJSch(hc, fs);
// Clean out anything 'default' - any encrypted keys
// that are loaded by default before this will break.
jsch.removeAllIdentity();
int count = 0;
for (final URL identityKey : identityKeyURLs) {
try (InputStream stream = identityKey.openStream()) {
jsch.addIdentity("key" + ++count, StreamUtils.copyToByteArray(stream), null, null);
} catch (IOException e) {
logger.error("Failed to load identity " + identityKey.getPath());
}
}
return jsch;
}
}
I am having a similar problem because my default SSH key is encrypted with a password and therefore doesn't "just work", which makes sense because this is a head-less setup.
I went source-diving into Spring Cloud Config, org.eclipse.jgit and eventually ended up in com.jcraft.jsch. The short answer is that neither JGit nor Spring Cloud expose an obvious way to do this.
JSch clearly supports this feature within a JSch() instance, but you can't get at it from the Spring Cloud level. At least not that I could find in a hour or so of looking.

Use of resource-manager specific transaction demarcation API in EJB 3.x

Accordinong to EJB 3.0 specification: While an instance is in a transaction, the instance must not attempt to use the resource-manager specific transaction demarcation API (e.g. it must not invoke the
commit or rollback method on the java.sql.Connection interface or on the
javax.jms.Session interface) In 13.3.3 of Specification.
I tried one example - where in BEAN managed transaction I included java.sql.Connection.commit() - created Stateless bean in NetBeans as EE5, deployed on Glassfish 3.1 and container did not complain? Bean method updates the database without any errors in Glassfish log. Is this expected behavior?
Also, there is no such restriction on using java.sql.Connection.commit() for beans with container transaction managed transactions mentioned in specification.
Thanks
Branislav
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package ejb;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.ejb.*;
import javax.persistence.Transient;
import javax.sql.DataSource;
import javax.transaction.*;
/**
*
* #author bane
*/
#Stateless
#TransactionManagement(TransactionManagementType.BEAN)
public class MySession implements MySessionRemote {
#Resource(name = "SAMPLE")
private DataSource SAMPLE;
//
#Resource UserTransaction utx;
//gore je novi kod
#Override
public String getResult() {
return "This is my Session Bean";
}
public void doSomething() {
try {
Connection conn = SAMPLE.getConnection();
Statement stmt = conn.createStatement();
String q = "select * from BOOK";
String up = "update BOOK set PRICE = PRICE + 1";
utx.begin();
int num = stmt.executeUpdate(up);
System.out.println("num: "+num);
ResultSet rs = stmt.executeQuery(q);
//is conn.commit() legal?
conn.commit();
String name = null;
int price = 0;
while (rs.next()) {
name = rs.getString(2);
price = rs.getInt(3);
System.err.println(name+" , "+price);
}
utx.commit();
} catch (SQLException ex) {
Logger.getLogger(MySession.class.getName()).log(Level.SEVERE, null, ex);
} catch (Exception ex) {
Logger.getLogger(MySession.class.getName()).log(Level.SEVERE, null, ex);
}
}
// Add business logic below. (Right-click in editor and choose
// "Insert Code > Add Business Method")
}

Resources