Cannot retrieve Google Credentials outside of a Servlet - google-glass

We are looking to push periodic or event driven Glass Timeline updates and we're running into issues with the AuthUtil provided in the Java Glass starter project.
Normally, we would just need to call the following after a user has enabled our app with Google permissions:
Credential credential = AuthUtil.getCredential(googleUserToken);
This works perfectly when retrieving the Google UserToken from an HttpRequest during the authentication servlet or the notification servlet.
We are persisting the user's Google User Token at the App authentication phase. However, the same valid Google UserTokens returns a null Credential object when attempting to get the Google Credential from a Quartz Job or Message Queue.
We have debugged the code and noticed that the AuthUtil is using the ListableMemoryCredentialStore and this store is empty when trying to retrieve a Credential from a Quartz job.

Going to answer my own question here. The AuthUtil helper packaged with the starter project provides only an in-memory cache of Google Credentials. In the event of a server restart or new instance, the cache is gone and the user is then directed to the Google OAuth flow.
To prevent this and provide scalability to your Glass app, use the following CredentialStore implementation and replace the use of the ListableMemoryCredentialStore in AuthUtil. It's not the cleanest, but it's lightweight and preserves the singleton intent of the AuthUtil.
Replace the JDBC paths to your database, schema, username and password for each of the databse helper methods. The Google credential table is nothing more than a four column table storing the userId as the PK, accessToken, resfreshToken and expriationTimeInMillis. Obviously, feel free to adjust the data model as you see fit.
/**
* Created by Dayel Ostraco
* Date: 12/4/13
*
* This extends the Google OAuth2 CredentialStore interface and uses a Database backend to persist Google Credentials
* in a thread safe manner.
*
*/
public class DatabaseCredentialStore implements CredentialStore {
private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseCredentialStore.class);
/**
* Lock on access to the database Credential store.
*/
private final Lock lock = new ReentrantLock();
/**
* Stores a Google OAuth2 Credential in the database.
*
* #param userId String
* #param credential Google OAuth2 Credential
*/
public void store(String userId, Credential credential) {
lock.lock();
try {
DatabasePersistedCredential item = findByUserId(userId);
if (item == null) {
item = new DatabasePersistedCredential();
item.store(userId, credential);
create(item);
}
item.store(userId, credential);
} finally {
lock.unlock();
}
}
/**
* Removes a persisted Google Credential from the database.
*
* #param userId String
* #param credential Google OAuth2 Credential
*/
public void delete(String userId, Credential credential) {
lock.lock();
try {
DatabasePersistedCredential item = findByUserId(credential.getAccessToken());
delete(item);
} finally {
lock.unlock();
}
}
/**
* Loads a Google Credential with the contents of the persisted Google Credentials.
*
* #param userId String
* #param credential Google OAuth2 Credential
* #return boolean
*/
public boolean load(String userId, Credential credential) {
lock.lock();
try {
DatabasePersistedCredential item = findByUserId(userId);
if (item != null) {
item.load(userId, credential);
}
return item != null;
} finally {
lock.unlock();
}
}
/**
* Returns all users in the Google Credentials Table
* #return List of all persisted Google authenticated user IDs
*/
public List<String> listAllUsers() {
List<String> userIds = new ArrayList<>();
for(DatabasePersistedCredential credential : findAll()) {
userIds.add(credential.getUserId());
}
return userIds;
}
/****************
* JDBC Methods *
***************/
/**
* Persists a new Google Credential to the database.
*
* #param credential DatabasePersistedCredential
*/
private void create(DatabasePersistedCredential credential) {
Connection connect;
PreparedStatement preparedStatement;
try {
Class.forName("com.mysql.jdbc.Driver");
connect = DriverManager.getConnection("jdbc:mysql://yourdatabaseurl:3306/schema?user=un&password=password");
preparedStatement = connect.prepareStatement("INSERT INTO GoogleCredential values (?, ?, ?, ?)");
preparedStatement.setString(1, credential.getUserId());
preparedStatement.setString(2, credential.getAccessToken());
preparedStatement.setString(3, credential.getRefreshToken());
preparedStatement.setLong(4, credential.getExpirationTimeInMillis());
preparedStatement.executeUpdate();
} catch (ClassNotFoundException e) {
LOGGER.error("Could not load MySQL Driver.", e);
} catch (SQLException e) {
LOGGER.error("Could not persist DatabasePersistedCredential.", e);
}
}
/**
* Removes a Google credential from the database.
*
* #param credential DatabasePersistedCredential
*/
private void delete(DatabasePersistedCredential credential) {
Connection connect;
PreparedStatement preparedStatement;
ResultSet resultSet;
try {
Class.forName("com.mysql.jdbc.Driver");
connect = DriverManager.getConnection("jdbc:mysql://yourdatabaseurl:3306/schema?user=un&password=password");
preparedStatement = connect.prepareStatement("DELETE * FROM spgglassdb.GoogleCredential WHERE userId = ?;");
preparedStatement.setString(1, credential.getUserId());
preparedStatement.executeQuery();
} catch (ClassNotFoundException e) {
LOGGER.error("Could not load MySQL Driver.", e);
} catch (SQLException e) {
LOGGER.error("Could not persist DatabasePersistedCredential.", e);
}
}
/**
* Returns the Google credentials for a user with the passed in userId.
*
* #param userId String
* #return DatabasePersistedCredential
*/
private DatabasePersistedCredential findByUserId(String userId) {
Connection connect;
PreparedStatement preparedStatement;
ResultSet resultSet;
try {
Class.forName("com.mysql.jdbc.Driver");
connect = DriverManager.getConnection("jjdbc:mysql://yourdatabaseurl:3306/schema?user=un&password=password");
preparedStatement = connect.prepareStatement("SELECT * FROM spgglassdb.GoogleCredential WHERE userId = ?;");
preparedStatement.setString(1, userId);
resultSet = preparedStatement.executeQuery();
List<DatabasePersistedCredential> credentials = convertResultsSet(resultSet);
if(credentials.size()==1) {
return credentials.get(0);
} else {
return null;
}
} catch (ClassNotFoundException e) {
LOGGER.error("Could not load MySQL Driver.", e);
} catch (SQLException e) {
LOGGER.error("Could not persist DatabasePersistedCredential.", e);
}
return null;
}
/**
* Returns all DatabasePersistedCredentials located in the database.
*
* #return List<DatabasePersistedCredential>
*/
private List<DatabasePersistedCredential> findAll() {
Connection connect;
PreparedStatement preparedStatement;
ResultSet resultSet;
try {
Class.forName("com.mysql.jdbc.Driver");
connect = DriverManager.getConnection("jdbc:mysql://yourdatabaseurl:3306/schema?user=un&password=password");
preparedStatement = connect.prepareStatement("SELECT * FROM spgglassdb.GoogleCredential;");
resultSet = preparedStatement.executeQuery();
List<DatabasePersistedCredential> credentials = convertResultsSet(resultSet);
return credentials;
} catch (ClassNotFoundException e) {
LOGGER.error("Could not load MySQL Driver.", e);
} catch (SQLException e) {
LOGGER.error("Could not persist DatabasePersistedCredential.", e);
}
return null;
}
/**
* Converts a ResultSet to a collection of DatabasePersistedCredentials.
*
* #param resultSet JDBC ResultSet
* #return List<DatabasePersistedCredential>
* #throws SQLException
*/
private List<DatabasePersistedCredential> convertResultsSet(ResultSet resultSet) throws SQLException {
List<DatabasePersistedCredential> credentials = new ArrayList<>();
while (resultSet.next()) {
DatabasePersistedCredential credential = new DatabasePersistedCredential();
String accessToken = resultSet.getString("accessToken");
String refreshToken = resultSet.getString("refreshToken");
Long expirationTimeInMillis = resultSet.getLong("expirationTimeInMillis");
credential.setAccessToken(accessToken);
credential.setRefreshToken(refreshToken);
credential.setExpirationTimeInMillis(expirationTimeInMillis);
credentials.add(credential);
}
return credentials;
}
}

Related

Spring Boot Lambda Startup multiple times at the cold start

I'm writing small Data downloading service function using Spring Boot 2.7. I deployed it in the AWS lambda function. At the cold starting each time it initializes twice as shown in the image below.
Here is the lambderHandler.java
public class LambdaHandler implements RequestHandler<SQSEvent, String> {
private static final ApplicationContext applicationContext = SpringApplication.run(DataDownloaderService.class);
#Override
public String handleRequest(SQSEvent event, Context context) {
context.getLogger().log("Input: " + event);
if (!event.getRecords().isEmpty()) {
SQSEvent.SQSMessage firstRecord = event.getRecords().get(0);
String eventBody = firstRecord.getBody();
if (eventBody != null) {
ProductDownloadService productDownloadService = applicationContext.getBean(ProductDownloadServiceImpl.class);
try {
productDownloadService.downloadProductData(JsonUtil.getUserInfo(eventBody));
return "Successfully processed";
} catch (IOException e) {
return "Error in data downloading: " + e.getMessage();
}
}
}
return "Data download not success";
}
}
Here is the DataDownloaderService.java
#SpringBootApplication
#ComponentScan(basePackages = {"com.test.*"})
#EntityScan(basePackages = {"com.test.*"})
public class DataDownloaderService {
}
Why spring boot lambda function initialize multiple times at the cold start? Did I have some misconfiguration here or is it a bug?
I found a workaround to fix this issue from here.
Here is the workaround for the above code.
public class LambdaHandler implements RequestHandler<SQSEvent, String> {
private ApplicationContext applicationContext;
/**
* Entry point in the lambda function
* #param event
* #param context
* #return
*/
#Override
public String handleRequest(SQSEvent event, Context context) {
return initDataDownloaderService(event, context);
}
/**
* initialize application context and Data downloader service
*
* #param event
* #param context
* #return
*/
private String initDataDownloaderService(SQSEvent event, Context context) {
if (applicationContext == null) {
applicationContext = SpringApplication.run(DataDownloaderService.class);
}
context.getLogger().log("Input: " + event);
if (!event.getRecords().isEmpty()) {
SQSEvent.SQSMessage firstRecord = event.getRecords().get(0);
String eventBody = firstRecord.getBody();
if (eventBody != null) {
ProductDownloadService productDownloadService = applicationContext.getBean(ProductDownloadServiceImpl.class);
try {
productDownloadService.downloadProductData(JsonUtil.getUserInfo(eventBody));
return "Successfully processed";
} catch (IOException e) {
return "Error in data downloading: " + e.getMessage();
}
}
}
return "Data download not success";
}
}

AWS Redshift serverless - how to get the cluster id value

I'm following the AWS documentation about how to connect to redshift [generating user credentials][1]
But the get-cluster-credentials API requires a cluster id parameter, which i don't have for a serverless endpoint. What id should I use?
EDIT:
[![enter image description here][2]][2]
This is the screen of a serverless endpoint dashboard. There is no cluster ID.
[1]: https://docs.aws.amazon.com/redshift/latest/mgmt/generating-user-credentials.html
[2]: https://i.stack.imgur.com/VzvIs.png
Look at this Guide (a newer one) that talks about Connecting to Amazon Redshift Serverless. https://docs.aws.amazon.com/redshift/latest/mgmt/serverless-connecting.html
See this information that answers your question:
Connecting to the serverless endpoint with the Data API
You can also use the Amazon Redshift Data API to connect to serverless endpoint. Leave off the cluster-identifier parameter in your AWS CLI calls to route your query to serverless endpoint.
UPDATE
I wanted to test this to make sure that a successful connection can be made. I followed this doc to setup a Serverless instance.
Get started with Amazon Redshift Serverless
I loaded sample data and now have this.
Now I attemped to connect to it using software.amazon.awssdk.services.redshiftdata.RedshiftDataClient.
The Java V2 code:
try {
ExecuteStatementRequest statementRequest = ExecuteStatementRequest.builder()
.database(database)
.sql(sqlStatement)
.build();
ExecuteStatementResponse response = redshiftDataClient.executeStatement(statementRequest);
return response.id();
} catch (RedshiftDataException e) {
System.err.println(e.getMessage());
System.exit(1);
}
return "";
}
Notice there is no cluster id or user. Only a database name (sample_data_dev). The call worked perfectly.
HEre is the full code example that successfully queries data from a serverless instance using the AWS SDK for Java V2.
package com.example.redshiftdata;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.redshiftdata.model.*;
import software.amazon.awssdk.services.redshiftdata.RedshiftDataClient;
import software.amazon.awssdk.services.redshiftdata.model.DescribeStatementRequest;
import java.util.List;
/**
* To run this Java V2 code example, ensure that you have setup your development environment, including your credentials.
*
* For information, see this documentation topic:
*
* https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
*/
public class RetrieveDataServerless {
public static void main(String[] args) {
final String USAGE = "\n" +
"Usage:\n" +
" RetrieveData <database> <sqlStatement> \n\n" +
"Where:\n" +
" database - the name of the database (for example, sample_data_dev). \n" +
" sqlStatement - the sql statement to use. \n" ;
String database = "sample_data_dev" ;
String sqlStatement = "Select * from tickit.sales" ;
Region region = Region.US_WEST_2;
RedshiftDataClient redshiftDataClient = RedshiftDataClient.builder()
.region(region)
.build();
String id = performSQLStatement(redshiftDataClient, database, sqlStatement);
System.out.println("The identifier of the statement is "+id);
checkStatement(redshiftDataClient,id );
getResults(redshiftDataClient, id);
redshiftDataClient.close();
}
public static void checkStatement(RedshiftDataClient redshiftDataClient,String sqlId ) {
try {
DescribeStatementRequest statementRequest = DescribeStatementRequest.builder()
.id(sqlId)
.build() ;
// Wait until the sql statement processing is finished.
boolean finished = false;
String status = "";
while (!finished) {
DescribeStatementResponse response = redshiftDataClient.describeStatement(statementRequest);
status = response.statusAsString();
System.out.println("..."+status);
if (status.compareTo("FINISHED") == 0) {
break;
}
Thread.sleep(1000);
}
System.out.println("The statement is finished!");
} catch (RedshiftDataException | InterruptedException e) {
System.err.println(e.getMessage());
System.exit(1);
}
}
public static String performSQLStatement(RedshiftDataClient redshiftDataClient,
String database,
String sqlStatement) {
try {
ExecuteStatementRequest statementRequest = ExecuteStatementRequest.builder()
.database(database)
.sql(sqlStatement)
.build();
ExecuteStatementResponse response = redshiftDataClient.executeStatement(statementRequest);
return response.id();
} catch (RedshiftDataException e) {
System.err.println(e.getMessage());
System.exit(1);
}
return "";
}
public static void getResults(RedshiftDataClient redshiftDataClient, String statementId) {
try {
GetStatementResultRequest resultRequest = GetStatementResultRequest.builder()
.id(statementId)
.build();
GetStatementResultResponse response = redshiftDataClient.getStatementResult(resultRequest);
// Iterate through the List element where each element is a List object.
List<List<Field>> dataList = response.records();
// Print out the records.
for (List list: dataList) {
for (Object myField:list) {
Field field = (Field) myField;
String value = field.stringValue();
if (value != null)
System.out.println("The value of the field is " + value);
}
}
} catch (RedshiftDataException e) {
System.err.println(e.getMessage());
System.exit(1);
}
}
}

How to manage secret rotation used by spring boot app running on ECS in AWS cloud

My organization is running spring boot app on AWS ECS docker container which reads the credentials for Postgres sql from secrets manager in AWS during boot up. AS part of security complaince, we are rotating the secrets every 3 months. The spring boot app is loosing connection with the database and going down when the RDS credentials are rotated.we have to restart it in order to pick the new credentials to work properly. Is there any way I can read the credentials automatically once the credentials are rotated to avoid restarting the application manually?
After some research I found that the postgres database in AWS supports passwordless authentication using IAM roles. We can generate a token which is valid for 15 mins and can connect to database using that token. I prefer this way of connecting to database rather than using password for my database. More details about setting up password less authentication can be found here
Code example as below
import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
import com.amazonaws.services.rds.auth.GetIamAuthTokenRequest;
import com.amazonaws.services.rds.auth.RdsIamAuthTokenGenerator;
import org.apache.commons.lang3.StringUtils;
import org.apache.tomcat.jdbc.pool.ConnectionPool;
import org.apache.tomcat.jdbc.pool.PoolConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.net.URISyntaxException;
import java.sql.SQLException;
import java.util.Properties;
public class RdsIamAuthDataSource extends org.apache.tomcat.jdbc.pool.DataSource {
private static final Logger LOGGER = LoggerFactory.getLogger(RdsIamAuthDataSource.class);
private static final int DEFAULT_PORT = 5432;
private static final String USESSL = "useSSL";
private static final String REQUIRE_SSL = "requireSSL";
private static final String BOOLEAN_TRUE = "true";
private static final String VERIFY_SERVER_CERTIFICATE = "verifyServerCertificate";
private static final String THREAD_NAME = "RdsIamAuthDataSourceTokenThread";
/**
* Constructor for RdsIamAuthDataSource.
* #param props {#link PoolConfiguration}
*/
public RdsIamAuthDataSource(PoolConfiguration props) {
this.poolProperties = props;
}
#Override
public ConnectionPool createPool() throws SQLException {
if (pool == null) {
return createPoolImpl();
} else {
return pool;
}
}
protected ConnectionPool createPoolImpl() throws SQLException {
synchronized (this) {
return pool = new RdsIamAuthConnectionPool(poolProperties);
}
}
private class RdsIamAuthConnectionPool extends ConnectionPool implements Runnable {
private RdsIamAuthTokenGenerator rdsIamAuthTokenGenerator;
private String host;
private String region;
private int port;
private String username;
private Thread tokenThread;
/**
* Constructor for RdsIamAuthConnectionPool.
* #param prop {#link PoolConfiguration}
* #throws SQLException {#link SQLException}
*/
public RdsIamAuthConnectionPool(PoolConfiguration prop) throws SQLException {
super(prop);
}
#Override
protected void init(PoolConfiguration prop) throws SQLException {
try {
final URI uri = new URI(prop.getUrl().substring(5));
this.host = uri.getHost();
this.port = uri.getPort();
if (this.port < 0) {
this.port = DEFAULT_PORT;
}
this.region = StringUtils.split(this.host,'.')[2];
this.username = prop.getUsername();
this.rdsIamAuthTokenGenerator = RdsIamAuthTokenGenerator.builder()
.credentials(new DefaultAWSCredentialsProviderChain())
.region(this.region)
.build();
updatePassword(prop);
final Properties props = prop.getDbProperties();
props.setProperty(USESSL, BOOLEAN_TRUE);
props.setProperty(REQUIRE_SSL, BOOLEAN_TRUE);
props.setProperty(VERIFY_SERVER_CERTIFICATE, BOOLEAN_TRUE);
super.init(prop);
this.tokenThread = new Thread(this, THREAD_NAME);
this.tokenThread.setDaemon(true);
this.tokenThread.start();
} catch (URISyntaxException e) {
LOGGER.error("Database URL is not correct. Please verify", e);
throw new RuntimeException(e.getMessage());
}
}
/**
* Refresh the token every 12 minutes.
*/
#Override
public void run() {
try {
while (this.tokenThread != null) {
Thread.sleep(12 * 60 * 1000);
updatePassword(getPoolProperties());
}
} catch (InterruptedException e) {
LOGGER.error("Background token thread interrupted", e);
}
}
#Override
protected void close(boolean force) {
super.close(force);
final Thread thread = tokenThread;
if (thread != null) {
thread.interrupt();
}
}
private void updatePassword(PoolConfiguration props) {
final String token = rdsIamAuthTokenGenerator.getAuthToken(GetIamAuthTokenRequest.builder()
.hostname(host)
.port(port)
.userName(this.username)
.build());
LOGGER.info("Updated IAM token for connection pool");
props.setPassword(token);
}
}
}
Supply the following DataSource as a spring bean. That's it. Now your application will automatically refresh credentials every 12 minutes
#Bean
public DataSource dataSource() {
final PoolConfiguration props = new PoolProperties();
props.setUrl("jdbc:postgresql://myapp.us-east-2.rds.amazonaws.com/myschema?ssl=true");
props.setUsername("rdsadminuser");
props.setDriverClassName("org.somedatabase.Driver");
return new RdsIamAuthDataSource(props);
}

How do I fix request timeout when trying to sing up a user in cognito via .net

Does somebody know how to fix request timeout by gateway (504) when trying to register a user into cognito? I use it with AWS Api gateway. locally it runs with no problem, but when I upload it into AWS lambda it gets a request timeout.
When I have te code below with the await it timesout. When I leave the await away it will not timeout but it also will not register the user in cognito.
private static readonly string _clientId = DotNetEnv.Env.GetString("AWS_CLIENT_ID", null);
private static readonly string _poolId = DotNetEnv.Env.GetString("AWS_USER_POOL", null);
private static AmazonCognitoIdentityProviderClient CreateCognitoClient()
{
return new AmazonCognitoIdentityProviderClient(new AnonymousAWSCredentials());
}
/*
* Method name: regsiterAsync
* #param1: user (User), contains the basic userdata for registering a user (email, username, password, name)
* returns: Response (object)
**/
public static async Task<Response> registerAsync(User user)
{
Response response = new Response();
/*
* Check if user already exists in our database.
**/
if (DbHandler.getUserByEmail(user.email) == null)
{
/*
* Create a signup request to be sent to amazon cognito.
**/
SignUpRequest signUpRequest = new SignUpRequest()
{
ClientId = _clientId,
Username = user.email,
Password = user.password,
UserAttributes = new List<Amazon.CognitoIdentityProvider.Model.AttributeType>
{
new AttributeType
{
Name = "email",
Value = user.email
}
}
};
try
{
/*
* Send singup request to amazon cognito
* Returns a SingUpRequest(object)
**/
using(var cognitoClient = CreateCognitoClient())
{
var signUpResult = await cognitoClient.SignUpAsync(signUpRequest);
}
}
catch (Exception e)
{
/*
* Incase that a error accoured by sending the SingUpRequest to cognito. Return a error response.
**/
response.status = "failed";
response.message = e.Message;
return response;
}
/*
* Add user to our database
**/
User storedUser = DbHandler.addUser(user);
/*
* Return response with success data
**/
response.message = "User successful registered.";
response.data = storedUser;
return response;
}
/*
* In case the user already exists in our database. Return a non successful response
**/
response.status = "failed";
response.message = "User already exists.";
return response;
You need to increase the timeout value of the lambda function under Basic settings section. Default value for timeout is 3 seconds and your function takes more than that.

jetty 9.4 share sessions among different contexts

I recently upgraded from jetty 9.3.11 to 9.4.6 . Since 9.4.x does not support HashSessionManager, I created my own custom SessionHandler. But when i attach this SessionHandler to the WebAppContext then the context becomes null when trying to access from servlets. there are no errors thrown in the logs.
Relevant section of code:
MyCustomSessionHandler sessionHandler = new MyCustomSessionHandler();
HandlerCollection handlers_ = new HandlerCollection(true);
COntextHandlerCollection chc_ = new ContextHandlerCollection();
for(WebAppConfig wap: webAppConfigs) //webappconfig a POJO from where I am getting webapp configs
{
String path = wap.getPath();
String warFile = wap.getWarFile();
WebAppContext context =
new WebAppContext(chc_, warFile, path);
// context.setSessionHandler(new SessionHandler()); // this one works.
context.setSessionHandler(sessionHandler); // this one doesnt work.
for (ServletConfig servletConfig: wap.getServletConfigs()) //ServletConfig is another POJO to get servlet configs
{
String servletName = servletConfig.getName();
String servletPath = servletConfig.getPath();
Servlet servlet = servletConfig.getServlet();
ServletHolder servletHolder = new ServletHolder(servlet);
context.addServlet(servletHolder, servletPath);
}
}
handlers_.setHandlers(new Handler[] { chc_, new DefaultHandler()});
server_.setHandler(handlers_);
Sample of my custom Session handler
public class MyCUstomSessionHandler extends SessionHandler
{
public MyCustomSessionHandler()
{
super();
}
public void setSecureCookies(boolean secureCookies)
{
getSessionCookieConfig().setSecure(secureCookies);
}
public void setHttpOnly(boolean httpOnly)
{
getSessionCookieConfig().setHttpOnly(httpOnly);
}
public void setMaxCookieAge(int age)
{
getSessionCookieConfig().setMaxAge(age);
}
}
Further clarification: It happens because I create a singleton sessionhandler and share it across different WepAppContext as a way of sharing sessions among them. This method seemed work fine without issues in 9.3 but doesn't work with new session management in 9.4.
Any help to solve this problem is appreciated.
I solved it by
setting cookie path to root ("/")
extending the getSession() function of SessionHandler to loop through all the contexts to check if session is created for the cookie in any other context.
/* check all contexts for sessions*/
public Session getSession(String id)
{
Session session = getLocalSession(id);
if (session == null)
{
for (SessionHandler manager: getSessionIdManager().getSessionHandlers())
{
if (manager.equals(this) ||
!(manager instanceof CustomSessionHandler))
{
continue;
}
session = ((CustomSessionHandler)manager).getLocalSession(id);
if (session != null)
{
break;
}
}
// should we duplicate sessions in each context?
// will we end up with inconsistent sessions?
/*
if (externalSession != null)
{
try
{
getSessionCache().put(id, externalSession);
}
catch (Exception e)
{
LOG.warn("Unable to save session to local cache.");
}
}
*/
}
return session;
}
/* ------------------------------------------------------------ */
/**
* Get a known existing session
* #param id The session ID stripped of any worker name.
* #return A Session or null if none exists.
*/
public Session getLocalSession(String id)
{
return super.getSession(id);
}