diff --git a/pom.xml b/pom.xml
index 2678a55..46a03f1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -73,8 +73,8 @@
org.slf4j
slf4j-log4j12
1.7.7
- test
+
diff --git a/src/main/java/com/microsoft/aad/msal4jextensions/CrossProcessCacheFileLock.java b/src/main/java/com/microsoft/aad/msal4jextensions/CrossProcessCacheFileLock.java
index 34636c7..52e3541 100644
--- a/src/main/java/com/microsoft/aad/msal4jextensions/CrossProcessCacheFileLock.java
+++ b/src/main/java/com/microsoft/aad/msal4jextensions/CrossProcessCacheFileLock.java
@@ -5,35 +5,34 @@
import java.io.File;
import java.io.IOException;
-import java.io.RandomAccessFile;
import java.lang.management.ManagementFactory;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.StandardOpenOption;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * Cross process lock based on OS level file lock.
+ * Cross process lock based on lock file creation/deletion (optional) + OS level file lock.
*/
class CrossProcessCacheFileLock {
-
private final static Logger LOG = LoggerFactory.getLogger(CrossProcessCacheFileLock.class);
- public static final String READ_MODE = "r";
- public static final String WRITE_MODE = "rw";
private int retryDelayMilliseconds;
private int retryNumber;
private File lockFile;
- private RandomAccessFile randomAccessFile;
-
- private String mode;
private FileLock lock;
+ private FileChannel fileChannel;
+
+ private boolean locked;
+
/**
* Constructor
*
@@ -43,113 +42,134 @@ class CrossProcessCacheFileLock {
*/
CrossProcessCacheFileLock(String lockfileName, int retryDelayMilliseconds, int retryNumber) {
lockFile = new File(lockfileName);
- lockFile.deleteOnExit();
this.retryDelayMilliseconds = retryDelayMilliseconds;
this.retryNumber = retryNumber;
}
- /**
- * Acquire read lock - can be shared by multiple readers
- *
- * @throws CacheFileLockAcquisitionException
- */
- void readLock() throws CacheFileLockAcquisitionException {
- lock(READ_MODE);
- }
-
- /**
- * Acquire write lock - exclusive access
- *
- * @throws CacheFileLockAcquisitionException
- */
- void writeLock() throws CacheFileLockAcquisitionException {
- lock(WRITE_MODE);
- }
-
- private String getProcessId(){
+ private String getProcessId() {
String vmName = ManagementFactory.getRuntimeMXBean().getName();
- String pid = vmName.substring(0, vmName.indexOf("@"));
-
- return pid;
+ return vmName.substring(0, vmName.indexOf("@"));
}
private String getLockProcessThreadId() {
return "pid:" + getProcessId() + " thread:" + Thread.currentThread().getId();
}
+ private boolean tryToCreateLockFile() {
+ for (int tryCount = 0; tryCount < retryNumber; tryCount++) {
+ boolean fileCreated = false;
+ try {
+ fileCreated = lockFile.createNewFile();
+ } catch (IOException ex) {
+ LOG.error(ex.getMessage());
+ }
+ if (fileCreated) {
+ return true;
+ } else {
+ waitBeforeRetry();
+ }
+ }
+ return false;
+ }
+
+ private void waitBeforeRetry(){
+ try {
+ Thread.sleep(retryDelayMilliseconds);
+ } catch (InterruptedException e) {
+ LOG.error(e.getMessage());
+ }
+ }
+
/**
- * Tries to acquire OS lock for lock file
+ * Tries to acquire lock by creating lockFile (optional),
+ * and acquiring OS lock for lockFile (mandatory)
* Retries {@link #retryNumber} times with {@link #retryDelayMilliseconds} delay
*
* @throws CacheFileLockAcquisitionException if the lock was not obtained.
*/
- private void lock(String mode) throws CacheFileLockAcquisitionException {
+ void lock() throws CacheFileLockAcquisitionException {
+ if (!tryToCreateLockFile()) {
+ LOG.debug(getLockProcessThreadId() + " Failed to create lock file!");
+ }
+
for (int tryCount = 0; tryCount < retryNumber; tryCount++) {
try {
lockFile.createNewFile();
- LOG.debug(getLockProcessThreadId() + " acquiring " + mode + " file lock");
-
- randomAccessFile = new RandomAccessFile(lockFile, mode);
- FileChannel channel = randomAccessFile.getChannel();
+ LOG.debug(getLockProcessThreadId() + " acquiring file lock");
+ fileChannel = FileChannel.open(lockFile.toPath(),
+ StandardOpenOption.READ,
+ StandardOpenOption.SYNC,
+ StandardOpenOption.WRITE);
- boolean isShared = READ_MODE.equals(mode);
- lock = channel.lock(0L, Long.MAX_VALUE, isShared);
-
- this.mode = mode;
+ // try to get file lock
+ lock = fileChannel.tryLock();
+ if (lock == null) {
+ throw new IllegalStateException("Lock is not available");
+ }
- if (!lock.isShared()) {
- String jvmName = java.lang.management.ManagementFactory.getRuntimeMXBean().getName();
+ // for debugging purpose write jvm name to lock file
+ writeJvmName(fileChannel);
- // for debugging purpose
- // if exclusive lock acquired, write jvm name to lock file
- ByteBuffer buff = ByteBuffer.wrap(jvmName.replace("@", " ").
- getBytes(StandardCharsets.UTF_8));
- channel.write(buff);
- }
- LOG.debug(getLockProcessThreadId() + " acquired file lock, isShared - " + lock.isShared());
+ locked = true;
+ LOG.debug(getLockProcessThreadId() + " acquired OK file lock");
return;
} catch (Exception ex) {
- LOG.debug(getLockProcessThreadId() + " failed to acquire " + mode + " lock," +
+ LOG.debug(getLockProcessThreadId() + " failed to acquire lock," +
" exception msg - " + ex.getMessage());
- try {
- releaseResources();
- } catch (IOException e) {
- LOG.error(e.getMessage());
- }
-
- try {
- Thread.sleep(retryDelayMilliseconds);
- } catch (InterruptedException e) {
- LOG.error(e.getMessage());
- }
+ releaseResources();
+ waitBeforeRetry();
}
}
- LOG.error(getLockProcessThreadId() + " failed to acquire " + mode + " lock");
+ LOG.error(getLockProcessThreadId() + " failed to acquire lock");
throw new CacheFileLockAcquisitionException(
- getLockProcessThreadId() + " failed to acquire " + mode + " lock");
+ getLockProcessThreadId() + " failed to acquire lock");
+ }
+
+ private void writeJvmName(FileChannel fileChannel) throws IOException {
+ String jvmName = ManagementFactory.getRuntimeMXBean().getName();
+
+ ByteBuffer buff = ByteBuffer.wrap(jvmName.replace("@", " ").
+ getBytes(StandardCharsets.UTF_8));
+ fileChannel.write(buff);
}
/**
- * Release OS lock for lockFile
+ * Release OS lock for lockFile,
+ * delete lockFile if it was created by lock() method
*
* @throws IOException
*/
void unlock() throws IOException {
- LOG.debug(getLockProcessThreadId() + " releasing " + mode + " lock");
+ LOG.debug(getLockProcessThreadId() + " releasing lock");
releaseResources();
+
+ if (locked) {
+ deleteLockFile();
+ }
}
- private void releaseResources() throws IOException {
- if (lock != null) {
- lock.release();
+ private void deleteLockFile() throws IOException {
+ if (!Files.deleteIfExists(lockFile.toPath())) {
+ LOG.debug(getLockProcessThreadId() + " FAILED to delete lock file");
+ }
+ }
+
+ private void releaseResources() {
+ try {
+ if (lock != null) {
+ lock.release();
+ }
+ if (fileChannel != null) {
+ fileChannel.close();
+ }
}
- if (randomAccessFile != null) {
- randomAccessFile.close();
+ catch (IOException ex){
+ LOG.error(ex.getMessage());
}
}
}
\ No newline at end of file
diff --git a/src/main/java/com/microsoft/aad/msal4jextensions/PersistenceSettings.java b/src/main/java/com/microsoft/aad/msal4jextensions/PersistenceSettings.java
index fd64949..8d518a4 100644
--- a/src/main/java/com/microsoft/aad/msal4jextensions/PersistenceSettings.java
+++ b/src/main/java/com/microsoft/aad/msal4jextensions/PersistenceSettings.java
@@ -312,7 +312,12 @@ public Builder setLockRetry(int delayMilliseconds, int retryNumber) {
* @return An immutable instance of {@link com.microsoft.aad.msal4jextensions.PersistenceSettings}.
*/
public PersistenceSettings build() {
- PersistenceSettings persistenceSettings = new PersistenceSettings(
+ if (keyringSchemaName != null && linuxUseUnprotectedFileAsCacheStorage) {
+ throw new IllegalArgumentException(
+ "Only one type of persistence can be used on Linux - KeyRing or Unprotected file");
+ }
+
+ return new PersistenceSettings(
cacheFileName,
cacheDirectoryPath,
keychainService,
@@ -327,8 +332,6 @@ public PersistenceSettings build() {
linuxUseUnprotectedFileAsCacheStorage,
lockRetryDelayMilliseconds,
lockRetryNumber);
-
- return persistenceSettings;
}
}
}
diff --git a/src/main/java/com/microsoft/aad/msal4jextensions/PersistenceTokenCacheAccessAspect.java b/src/main/java/com/microsoft/aad/msal4jextensions/PersistenceTokenCacheAccessAspect.java
index fe86e56..cd8850d 100644
--- a/src/main/java/com/microsoft/aad/msal4jextensions/PersistenceTokenCacheAccessAspect.java
+++ b/src/main/java/com/microsoft/aad/msal4jextensions/PersistenceTokenCacheAccessAspect.java
@@ -35,7 +35,7 @@ public class PersistenceTokenCacheAccessAspect implements ITokenCacheAccessAspec
private PersistenceSettings parameters;
private String getCacheLockFilePath() {
- return parameters.getCacheDirectoryPath() + File.separator + ".lock";
+ return parameters.getCacheDirectoryPath() + File.separator + ".lockfile";
}
private String getCacheFilePath() {
@@ -94,7 +94,7 @@ private boolean isReadAccess(ITokenCacheAccessContext iTokenCacheAccessContext)
return !isWriteAccess(iTokenCacheAccessContext);
}
- private void updateLastSeenCacheFileModifiedTimestamp() throws IOException {
+ private void updateLastSeenCacheFileModifiedTimestamp() {
lastSeenCacheFileModifiedTimestamp = getCurrentCacheFileModifiedTimestamp();
}
@@ -106,14 +106,14 @@ public Long getCurrentCacheFileModifiedTimestamp() {
public void beforeCacheAccess(ITokenCacheAccessContext iTokenCacheAccessContext) {
try {
if (isWriteAccess(iTokenCacheAccessContext)) {
- lock.writeLock();
+ lock.lock();
} else {
Long currentCacheFileModifiedTimestamp = getCurrentCacheFileModifiedTimestamp();
if (currentCacheFileModifiedTimestamp != null &&
currentCacheFileModifiedTimestamp.equals(lastSeenCacheFileModifiedTimestamp)) {
return;
} else {
- lock.readLock();
+ lock.lock();
}
}
byte[] data = cacheAccessor.read();
@@ -138,8 +138,6 @@ public void afterCacheAccess(ITokenCacheAccessContext iTokenCacheAccessContext)
cacheAccessor.write(iTokenCacheAccessContext.tokenCache().serialize().getBytes(StandardCharset.UTF_8));
updateLastSeenCacheFileModifiedTimestamp();
}
- } catch (IOException ex) {
- LOG.error(ex.getMessage());
} finally {
try {
lock.unlock();
diff --git a/src/main/java/com/microsoft/aad/msal4jextensions/persistence/CacheFileAccessor.java b/src/main/java/com/microsoft/aad/msal4jextensions/persistence/CacheFileAccessor.java
index 625f16e..7eae6dd 100644
--- a/src/main/java/com/microsoft/aad/msal4jextensions/persistence/CacheFileAccessor.java
+++ b/src/main/java/com/microsoft/aad/msal4jextensions/persistence/CacheFileAccessor.java
@@ -5,16 +5,23 @@
import com.sun.jna.Platform;
import com.sun.jna.platform.win32.Crypt32Util;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.FileTime;
/**
* Implementation of CacheAccessor based on File persistence
*/
public class CacheFileAccessor implements ICacheAccessor {
+ private final static Logger LOG = LoggerFactory.getLogger(CacheFileAccessor.class);
+
private String cacheFilePath;
private File cacheFile;
@@ -47,11 +54,38 @@ public byte[] read() {
public void write(byte[] data) {
if (Platform.isWindows()) {
data = Crypt32Util.cryptProtectData(data);
+
+ try (FileOutputStream stream = new FileOutputStream(cacheFile)) {
+ stream.write(data);
+ }
+ catch (IOException e) {
+ throw new CacheFileAccessException("Failed to write to Cache File", e);
+ }
+ }
+ else {
+ writeAtomic(data);
}
+ }
- try (FileOutputStream stream = new FileOutputStream(cacheFilePath)) {
- stream.write(data);
- } catch (IOException e) {
+ private void writeAtomic(byte[] data) {
+ File tempFile = null;
+ try {
+ try {
+ tempFile = File.createTempFile("JavaMsalExtTemp", ".tmp", cacheFile.getParentFile());
+
+ try (FileOutputStream stream = new FileOutputStream(tempFile)) {
+ stream.write(data);
+ }
+
+ Files.move(tempFile.toPath(), cacheFile.toPath(),
+ StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
+ } finally {
+ if (tempFile != null) {
+ Files.deleteIfExists(tempFile.toPath());
+ }
+ }
+ }
+ catch (IOException e) {
throw new CacheFileAccessException("Failed to write to Cache File", e);
}
}
@@ -65,7 +99,13 @@ public void delete() {
}
}
- public void updateCacheFileLastModifiedTimeByWritingDummyData() {
- write(new byte[1]);
+ public void updateCacheFileLastModifiedTime() {
+ FileTime fileTime = FileTime.fromMillis(System.currentTimeMillis());
+
+ try {
+ Files.setLastModifiedTime(Paths.get(cacheFilePath), fileTime);
+ } catch (IOException e) {
+ throw new CacheFileAccessException("Failed to set lastModified time on Cache File", e);
+ }
}
}
diff --git a/src/main/java/com/microsoft/aad/msal4jextensions/persistence/linux/KeyRingAccessor.java b/src/main/java/com/microsoft/aad/msal4jextensions/persistence/linux/KeyRingAccessor.java
index d4e9636..439f10d 100644
--- a/src/main/java/com/microsoft/aad/msal4jextensions/persistence/linux/KeyRingAccessor.java
+++ b/src/main/java/com/microsoft/aad/msal4jextensions/persistence/linux/KeyRingAccessor.java
@@ -48,7 +48,7 @@ public KeyRingAccessor(String cacheFilePath,
this.attributeValue2 = attributeValue2;
}
- public void verify() throws IOException {
+ public void verify() {
String testAttributeValue1 = "testAttr1";
String testAttributeValue2 = "testAttr2";
String testData = "Test Data";
@@ -113,7 +113,7 @@ private void write(byte[] data, String attributeValue1, String attributeValue2)
throw new KeyRingAccessException("An error while saving secret to keyring, " +
"domain:" + err.domain + " code:" + err.code + " message:" + err.message);
}
- new CacheFileAccessor(cacheFilePath).updateCacheFileLastModifiedTimeByWritingDummyData();
+ new CacheFileAccessor(cacheFilePath).updateCacheFileLastModifiedTime();
}
@Override
@@ -138,7 +138,7 @@ private void delete(String attributeValue1, String attributeValue2) {
throw new KeyRingAccessException("An error while deleting secret from keyring, " +
"domain:" + err.domain + " code:" + err.code + " message:" + err.message);
}
- new CacheFileAccessor(cacheFilePath).updateCacheFileLastModifiedTimeByWritingDummyData();
+ new CacheFileAccessor(cacheFilePath).updateCacheFileLastModifiedTime();
}
@Override
diff --git a/src/main/java/com/microsoft/aad/msal4jextensions/persistence/mac/KeyChainAccessor.java b/src/main/java/com/microsoft/aad/msal4jextensions/persistence/mac/KeyChainAccessor.java
index 2ae37d3..6f161ce 100644
--- a/src/main/java/com/microsoft/aad/msal4jextensions/persistence/mac/KeyChainAccessor.java
+++ b/src/main/java/com/microsoft/aad/msal4jextensions/persistence/mac/KeyChainAccessor.java
@@ -50,8 +50,7 @@ public byte[] read() {
}
}
- @Override
- public void write(byte[] data) {
+ private int writeNoRetry(byte[] data) {
Pointer[] itemRef = new Pointer[1];
int status;
@@ -62,33 +61,50 @@ public void write(byte[] data) {
accountNameBytes.length, accountNameBytes,
null, null, itemRef);
- if (status != ISecurityLibrary.ERR_SEC_SUCCESS
- && status != ISecurityLibrary.ERR_SEC_ITEM_NOT_FOUND) {
- throw new KeyChainAccessException(convertErrorCodeToMessage(status));
- }
+ if (status == ISecurityLibrary.ERR_SEC_SUCCESS && itemRef[0] != null) {
- if (itemRef[0] != null) {
status = ISecurityLibrary.library.SecKeychainItemModifyContent(
itemRef[0], null, data.length, data);
- } else {
+
+ } else if (status == ISecurityLibrary.ERR_SEC_ITEM_NOT_FOUND) {
status = ISecurityLibrary.library.SecKeychainAddGenericPassword(
- Pointer.NULL,
+ null,
serviceNameBytes.length, serviceNameBytes,
accountNameBytes.length, accountNameBytes,
data.length, data, null);
- }
-
- if (status != ISecurityLibrary.ERR_SEC_SUCCESS) {
+ } else {
throw new KeyChainAccessException(convertErrorCodeToMessage(status));
}
- new CacheFileAccessor(cacheFilePath).updateCacheFileLastModifiedTimeByWritingDummyData();
-
} finally {
if (itemRef[0] != null) {
ISecurityLibrary.library.CFRelease(itemRef[0]);
}
}
+ return status;
+ }
+
+ @Override
+ public void write(byte[] data) {
+ int NUM_OF_RETRIES = 3;
+ int RETRY_DELAY_IN_MS = 10;
+ int status = 0;
+
+ for (int i = 0; i < NUM_OF_RETRIES; i++) {
+ status = writeNoRetry(data);
+
+ if (status == ISecurityLibrary.ERR_SEC_SUCCESS) {
+ new CacheFileAccessor(cacheFilePath).updateCacheFileLastModifiedTime();
+ return;
+ }
+ try {
+ Thread.sleep(RETRY_DELAY_IN_MS);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ throw new KeyChainAccessException(convertErrorCodeToMessage(status));
}
@Override
@@ -117,7 +133,7 @@ public void delete() {
throw new KeyChainAccessException(convertErrorCodeToMessage(status));
}
}
- new CacheFileAccessor(cacheFilePath).updateCacheFileLastModifiedTimeByWritingDummyData();
+ new CacheFileAccessor(cacheFilePath).updateCacheFileLastModifiedTime();
} finally {
if (itemRef[0] != null) {
ISecurityLibrary.library.CFRelease(itemRef[0]);
diff --git a/src/test/java/com/microsoft/aad/msal4jextensions/CacheFileWriter.java b/src/test/java/com/microsoft/aad/msal4jextensions/CacheFileWriter.java
index 0de6366..7cc7632 100644
--- a/src/test/java/com/microsoft/aad/msal4jextensions/CacheFileWriter.java
+++ b/src/test/java/com/microsoft/aad/msal4jextensions/CacheFileWriter.java
@@ -14,8 +14,10 @@ public static void main(String[] args) throws Exception {
lockFilePath = args[1];
filePath = args[2];
+ String lockHoldingIntervalsFilePath = args[3];
+
CacheFileWriterRunnable cacheFileWriterRunnable =
- new CacheFileWriterRunnable(executionId, lockFilePath, filePath);
+ new CacheFileWriterRunnable(executionId, lockFilePath, filePath, lockHoldingIntervalsFilePath);
cacheFileWriterRunnable.run();
}
diff --git a/src/test/java/com/microsoft/aad/msal4jextensions/CacheFileWriterRunnable.java b/src/test/java/com/microsoft/aad/msal4jextensions/CacheFileWriterRunnable.java
index 365b35b..59a285d 100644
--- a/src/test/java/com/microsoft/aad/msal4jextensions/CacheFileWriterRunnable.java
+++ b/src/test/java/com/microsoft/aad/msal4jextensions/CacheFileWriterRunnable.java
@@ -3,43 +3,14 @@
package com.microsoft.aad.msal4jextensions;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
+import com.microsoft.aad.msal4jextensions.persistence.CacheFileAccessor;
-class CacheFileWriterRunnable implements Runnable {
- String id;
- String lockFilePath;
- File file;
- CrossProcessCacheFileLock lock;
+class CacheFileWriterRunnable extends CacheWriterRunnable {
- CacheFileWriterRunnable(String id, String lockFilePath, String filePath) {
- this.id = id;
- this.lockFilePath = lockFilePath;
- this.file = new File(filePath);
+ CacheFileWriterRunnable(String id, String lockFilePath, String filePath, String lockHoldingIntervalsFilePath) {
+ this.lockHoldingIntervalsFilePath = lockHoldingIntervalsFilePath;
- lock = new CrossProcessCacheFileLock(lockFilePath, 100, 10);
- }
-
- public void run() {
- try {
- lock.writeLock();
- file.createNewFile();
- try (FileOutputStream os = new FileOutputStream(file, true)) {
- os.write(("< " + id + "\n").getBytes());
- Thread.sleep(1000);
- os.write(("> " + id + "\n").getBytes());
- }
- } catch (IOException | InterruptedException ex) {
- System.out.println("File write failure");
- ex.printStackTrace();
- } finally {
- try {
- lock.unlock();
- } catch (IOException e) {
- System.out.println("Failed to unlock");
- e.printStackTrace();
- }
- }
+ lock = new CrossProcessCacheFileLock(lockFilePath, 150, 100);
+ cacheAccessor = new CacheFileAccessor(filePath);
}
}
\ No newline at end of file
diff --git a/src/test/java/com/microsoft/aad/msal4jextensions/CacheLockFileStorageTest.java b/src/test/java/com/microsoft/aad/msal4jextensions/CacheLockFileStorageTest.java
new file mode 100644
index 0000000..7d1f81c
--- /dev/null
+++ b/src/test/java/com/microsoft/aad/msal4jextensions/CacheLockFileStorageTest.java
@@ -0,0 +1,42 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.aad.msal4jextensions;
+
+import com.microsoft.aad.msal4jextensions.persistence.CacheFileAccessor;
+import org.junit.Test;
+
+import java.io.IOException;
+
+public class CacheLockFileStorageTest extends CacheLockTestBase {
+
+ CacheFileAccessor cacheFileAccessor = new CacheFileAccessor(testFilePath);
+
+ class CacheFileWriterRunnableFactory implements IRunnableFactory {
+ @Override
+ public Runnable create(String id) {
+ return new CacheFileWriterRunnable
+ (id, lockFilePath, testFilePath, lockHoldingIntervalsFilePath);
+ }
+ }
+
+ @Test
+ public void multipleThreadsWriting_CacheFile() throws IOException, InterruptedException {
+ int numOfThreads = 100;
+
+ multipleThreadsWriting(cacheFileAccessor, numOfThreads, new CacheFileWriterRunnableFactory());
+ }
+
+ @Test
+ public void multipleProcessesWriting_CacheFile() throws IOException, InterruptedException {
+ int numOfProcesses = 20;
+
+ String writerClass = com.microsoft.aad.msal4jextensions.CacheFileWriter.class.getName();
+
+ String writerClassArgs = lockFilePath + " " +
+ testFilePath + " " +
+ lockHoldingIntervalsFilePath;
+
+ multipleProcessesWriting(cacheFileAccessor, numOfProcesses, writerClass, writerClassArgs);
+ }
+}
diff --git a/src/test/java/com/microsoft/aad/msal4jextensions/CacheLockKeyChainStorageTest.java b/src/test/java/com/microsoft/aad/msal4jextensions/CacheLockKeyChainStorageTest.java
new file mode 100644
index 0000000..30c9aa8
--- /dev/null
+++ b/src/test/java/com/microsoft/aad/msal4jextensions/CacheLockKeyChainStorageTest.java
@@ -0,0 +1,57 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.aad.msal4jextensions;
+
+import com.microsoft.aad.msal4jextensions.persistence.mac.KeyChainAccessor;
+import com.sun.jna.Platform;
+import org.junit.*;
+
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class CacheLockKeyChainStorageTest extends CacheLockTestBase {
+
+ public static final String SERVICE = "testMsalService";
+ public static final String ACCOUNT = "testMsalAccount";
+
+ KeyChainAccessor keyChainAccessor =
+ new KeyChainAccessor(testFilePath, SERVICE, ACCOUNT);
+
+ class KeyChainWriterRunnableFactory implements IRunnableFactory {
+ @Override
+ public Runnable create(String id) {
+ return new KeyChainWriterRunnable
+ (id, lockFilePath, testFilePath, lockHoldingIntervalsFilePath,
+ SERVICE,
+ ACCOUNT);
+ }
+ }
+
+ @Test
+ public void multipleThreadsWriting_KeyChain() throws IOException, InterruptedException {
+ int numOfThreads = 100;
+
+ multipleThreadsWriting(keyChainAccessor, numOfThreads, new KeyChainWriterRunnableFactory());
+ }
+
+
+ @Test
+ public void multipleProcessesWriting_KeyChain() throws IOException, InterruptedException {
+ int numOfProcesses = 10;
+
+ String writerClass = com.microsoft.aad.msal4jextensions.KeyChainWriter.class.getName();
+
+ String writerClassArgs = lockFilePath + " " +
+ testFilePath + " " +
+ lockHoldingIntervalsFilePath + " " +
+ SERVICE + " " +
+ ACCOUNT;
+
+ multipleProcessesWriting(keyChainAccessor, numOfProcesses, writerClass, writerClassArgs);
+ }
+}
diff --git a/src/test/java/com/microsoft/aad/msal4jextensions/CacheLockKeyRingStorageTest.java b/src/test/java/com/microsoft/aad/msal4jextensions/CacheLockKeyRingStorageTest.java
new file mode 100644
index 0000000..925b5ed
--- /dev/null
+++ b/src/test/java/com/microsoft/aad/msal4jextensions/CacheLockKeyRingStorageTest.java
@@ -0,0 +1,68 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.aad.msal4jextensions;
+
+import com.microsoft.aad.msal4jextensions.persistence.linux.KeyRingAccessor;
+import org.junit.Test;
+
+import java.io.IOException;
+
+public class CacheLockKeyRingStorageTest extends CacheLockTestBase {
+
+ String SCHEMA = "testMsalSchema";
+ String LABEL = "testMsalLabel";
+ String ATTRIBUTE1_KEY = "testMsalAttribute1Key";
+ String ATTRIBUTE1_VALUE = "testMsalAttribute1Value";
+ String ATTRIBUTE2_KEY = "testMsalAttribute2Key";
+ String ATTRIBUTE2_VALUE = "testMsalAttribute2Value";
+
+ KeyRingAccessor keyRingAccessor = new KeyRingAccessor(testFilePath,
+ null,
+ SCHEMA,
+ LABEL,
+ ATTRIBUTE1_KEY,
+ ATTRIBUTE1_VALUE,
+ ATTRIBUTE2_KEY,
+ ATTRIBUTE2_VALUE);
+
+ class KeyRingWriterRunnableFactory implements IRunnableFactory {
+ @Override
+ public Runnable create(String id) {
+ return new KeyRingWriterRunnable
+ (id, lockFilePath, testFilePath, lockHoldingIntervalsFilePath,
+ SCHEMA,
+ LABEL,
+ ATTRIBUTE1_KEY,
+ ATTRIBUTE1_VALUE,
+ ATTRIBUTE2_KEY,
+ ATTRIBUTE2_VALUE);
+ }
+ }
+
+ @Test
+ public void multipleThreadsWriting_KeyRing() throws IOException, InterruptedException {
+ int numOfThreads = 100;
+
+ multipleThreadsWriting(keyRingAccessor, numOfThreads, new KeyRingWriterRunnableFactory());
+ }
+
+ @Test
+ public void multipleProcessesWriting_KeyRing() throws IOException, InterruptedException {
+ int numOfProcesses = 20;
+
+ String writerClass = com.microsoft.aad.msal4jextensions.KeyRingWriter.class.getName();
+
+ String writerClassArgs = lockFilePath + " " +
+ testFilePath + " " +
+ lockHoldingIntervalsFilePath+ " " +
+ SCHEMA + " " +
+ LABEL + " " +
+ ATTRIBUTE1_KEY + " " +
+ ATTRIBUTE1_VALUE + " " +
+ ATTRIBUTE2_KEY + " " +
+ ATTRIBUTE2_VALUE;
+
+ multipleProcessesWriting(keyRingAccessor, numOfProcesses, writerClass, writerClassArgs);
+ }
+}
diff --git a/src/test/java/com/microsoft/aad/msal4jextensions/CacheLockTest.java b/src/test/java/com/microsoft/aad/msal4jextensions/CacheLockTest.java
deleted file mode 100644
index 8506a4c..0000000
--- a/src/test/java/com/microsoft/aad/msal4jextensions/CacheLockTest.java
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-package com.microsoft.aad.msal4jextensions;
-
-import com.sun.jna.Platform;
-import org.junit.*;
-
-import java.io.*;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.stream.Collectors;
-
-public class CacheLockTest {
-
- private static String folder;
- private static String testFilePath;
- private static String lockFilePath;
-
- @BeforeClass
- public static void setup() {
- // get proper file paths
- String currDir = System.getProperty("user.dir");
- String home = System.getProperty("user.home");
-
- java.nio.file.Path classes = java.nio.file.Paths.get(currDir, "target", "classes");
- java.nio.file.Path tests = java.nio.file.Paths.get(currDir, "target", "test-classes");
-
- testFilePath = java.nio.file.Paths.get(home, "test.txt").toString();
- lockFilePath = java.nio.file.Paths.get(home, "testLock.lockfile").toString();
-
- String delimiter = ":";
- if (Platform.isWindows()) {
- delimiter = ";";
- }
- folder = classes.toString() + delimiter + tests;
- }
-
- @Test
- public void tenThreadsWritingToFile_notSharedLock() throws IOException, InterruptedException {
- int NUM_OF_THREADS = 10;
-
- File tester = new File(testFilePath);
- tester.delete();
-
- List writersThreads = new ArrayList<>();
- for (int i = 0; i < NUM_OF_THREADS; i++) {
- CacheFileWriterRunnable cacheFileWriterRunnable =
- new CacheFileWriterRunnable("Thread # " + i, lockFilePath, testFilePath);
-
- writersThreads.add(new Thread(cacheFileWriterRunnable));
- }
-
- for (Thread t : writersThreads) {
- t.start();
- t.join();
- }
-
- validateResult();
- }
-
-
- @Test
- public void tenThreadsWritingToFile_sharedLock() throws IOException, InterruptedException {
- int NUM_OF_THREADS = 10;
-
- File tester = new File(testFilePath);
- tester.delete();
-
- List writersThreads = new ArrayList<>();
- CacheFileWriterRunnable cacheFileWriterRunnable =
- new CacheFileWriterRunnable("Thread # ", lockFilePath, testFilePath);
-
- for (int i = 0; i < NUM_OF_THREADS; i++) {
- writersThreads.add(new Thread(cacheFileWriterRunnable));
- }
-
- for (Thread t : writersThreads) {
- t.start();
- t.join();
- }
-
- validateResult();
- }
-
- // implementation of org/slf4j/LoggerFactory should be available in Path
- //@Test
- public void tenProcessesWritingToFile() throws IOException, InterruptedException {
- int NUM_OF_PROCESSES = 10;
- // make sure tester.json file doesn't already exist
- File tester = new File(testFilePath);
- tester.delete();
-
- String mainWriterClass = com.microsoft.aad.msal4jextensions.CacheFileWriter.class.getName();
-
- List processes = new ArrayList<>();
- for (int i = 0; i < NUM_OF_PROCESSES; i++) {
- String[] command =
- new String[]{"java", "-cp", folder, mainWriterClass, "Process # " + i, lockFilePath, testFilePath};
-
- Process process = new ProcessBuilder(command).inheritIO().start();
- processes.add(process);
- }
-
- for (Process process : processes) {
- waitForProcess(process);
- }
-
- validateResult();
- }
-
- private void validateResult() throws IOException {
- String prevTag = null;
- String prevProcId = null;
-
- try (BufferedReader br = new BufferedReader(new FileReader(new File(testFilePath)))) {
- String line;
- while ((line = br.readLine()) != null) {
- String[] tokens = line.split(" ");
- String tag = tokens[0];
- String procId = tokens[1];
- switch (tag) {
- case ("<"):
- if ("<".equals(prevTag)) {
- Assert.fail("Unexpected Token");
- }
- break;
- case (">"):
- if (!"<".equals(prevTag) || !prevProcId.equals(procId)) {
- Assert.fail("Unexpected Token");
- }
- break;
- default:
- Assert.fail("Unexpected Token");
- }
- prevTag = tag;
- prevProcId = procId;
- }
- }
- if (!">".equals(prevTag)) {
- Assert.fail("Unexpected Token");
- }
- }
-
- private void waitForProcess(Process process) throws InterruptedException {
- if (process.waitFor() != 0) {
- throw new RuntimeException(new BufferedReader(new InputStreamReader(process.getErrorStream()))
- .lines().collect(Collectors.joining("\n")));
- }
- }
-}
diff --git a/src/test/java/com/microsoft/aad/msal4jextensions/CacheLockTestBase.java b/src/test/java/com/microsoft/aad/msal4jextensions/CacheLockTestBase.java
new file mode 100644
index 0000000..93ef9e6
--- /dev/null
+++ b/src/test/java/com/microsoft/aad/msal4jextensions/CacheLockTestBase.java
@@ -0,0 +1,194 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.aad.msal4jextensions;
+
+import com.microsoft.aad.msal4jextensions.persistence.ICacheAccessor;
+import com.sun.jna.Platform;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class CacheLockTestBase {
+ static String folder;
+ static String testFilePath;
+ static String lockFilePath;
+
+ static String testClassesPath;
+
+ static String lockHoldingIntervalsFilePath;
+
+ @BeforeClass
+ public static void setup() {
+ // get proper file paths
+ String currDir = System.getProperty("user.dir");
+ String home = System.getProperty("user.home");
+
+ java.nio.file.Path classes = java.nio.file.Paths.get(currDir, "target", "classes");
+ testClassesPath = java.nio.file.Paths.get(currDir, "target", "test-classes").toString();
+
+ testFilePath = java.nio.file.Paths.get(home, "test.txt").toString();
+ lockFilePath = java.nio.file.Paths.get(home, "testLock.lockfile").toString();
+
+ lockHoldingIntervalsFilePath = java.nio.file.Paths.get(home, "lockHoldingIntervals.txt").toString();
+
+ String delimiter = ":";
+ if (Platform.isWindows()) {
+ delimiter = ";";
+ }
+ folder = classes.toString() + delimiter + testClassesPath;
+ }
+
+ void waitForProcess(Process process) throws InterruptedException {
+ if (process.waitFor() != 0) {
+ throw new RuntimeException(new BufferedReader(new InputStreamReader(process.getErrorStream()))
+ .lines().collect(Collectors.joining("\n")));
+ }
+ }
+
+ void validateResult(String data, int expectedNum) {
+ System.out.println("DATA TO VALIDATE: ");
+ System.out.println(data);
+
+ String prevTag = null;
+ String prevProcId = null;
+ int count = 0;
+
+ for (String line : data.split("\\r?\\n")) {
+
+ String[] tokens = line.split(" ");
+ String tag = tokens[0];
+ String procId = tokens[1];
+ switch (tag) {
+ case ("<"):
+ if ("<".equals(prevTag)) {
+ Assert.fail("Unexpected Token");
+ }
+ break;
+ case (">"):
+ count++;
+ if (!"<".equals(prevTag) || !prevProcId.equals(procId)) {
+ Assert.fail("Unexpected Token");
+ }
+ break;
+ default:
+ Assert.fail("Unexpected Token");
+ }
+ prevTag = tag;
+ prevProcId = procId;
+ }
+ if (!">".equals(prevTag)) {
+ Assert.fail("Unexpected Token");
+ }
+ Assert.assertEquals(expectedNum, count);
+ }
+
+ void validateLockUsageIntervals(int expected_size) throws IOException {
+ List list = new ArrayList<>();
+ String data = readFile(lockHoldingIntervalsFilePath);
+
+ for (String line : data.split("\\r?\\n")) {
+ String[] split = line.split("-");
+ list.add(new Long[]{Long.parseLong(split[0]), Long.parseLong(split[1])});
+ }
+
+ //Assert.assertEquals(expected_size, list.size());
+ if (expected_size != list.size()) {
+ System.out.println("lock intervals NUM = " + list.size());
+ }
+
+ list.sort(Comparator.comparingLong(a -> a[0]));
+
+ int sum = 0;
+ Long[] prev = null;
+ for (Long[] interval : list) {
+ Assert.assertTrue(interval[0] <= interval[1]);
+ sum += interval[1] - interval[0];
+ if (prev != null) {
+ if (interval[0] < prev[1]) {
+ System.out.println("lock acquisition intersection detected");
+ //Assert.fail();
+ }
+ }
+ prev = interval;
+ }
+ System.out.println("average lock holding time in ms - " + sum/list.size());
+ }
+
+ private String readFile(String filePath) throws IOException {
+ byte[] bytes = Files.readAllBytes(Paths.get(filePath));
+ return new String(bytes, StandardCharsets.UTF_8);
+ }
+
+ void validateResultInCache(ICacheAccessor keyChainAccessor, int expectedNum) throws IOException {
+ validateResult(new String(keyChainAccessor.read(), StandardCharsets.UTF_8), expectedNum);
+ }
+
+ void multipleThreadsWriting(ICacheAccessor cacheAccessor, int num,
+ IRunnableFactory runnableFactory) throws IOException, InterruptedException {
+
+ clearFile(lockHoldingIntervalsFilePath);
+ cacheAccessor.delete();
+
+ List writersThreads = new ArrayList<>();
+
+ for (int i = 0; i < num; i++) {
+
+ Thread t = new Thread(runnableFactory.create("thread_" + i));
+ t.start();
+ writersThreads.add(t);
+ }
+
+ for (Thread t : writersThreads) {
+ t.join();
+ }
+ validateLockUsageIntervals(num);
+ validateResultInCache(cacheAccessor, num);
+ }
+
+ private void clearFile(String filePath) throws IOException {
+ new FileOutputStream(filePath).close();
+ }
+
+ void multipleProcessesWriting(ICacheAccessor cacheAccessor, int num,
+ String writerClass,
+ String writerClassArgs)
+ throws IOException, InterruptedException {
+
+ clearFile(lockHoldingIntervalsFilePath);
+ cacheAccessor.delete();
+
+ List processes = new ArrayList<>();
+ for (int i = 0; i < num; i++) {
+
+ String mvnArgs = ("Process_" + i) + " " + writerClassArgs;
+
+ String mvn = Platform.isWindows() ? "mvn.bat" : "mvn";
+
+ String[] mvnCommand =
+ new String[]{mvn, "exec:java",
+ "-Dexec.mainClass=" + writerClass,
+ "-Dexec.classpathScope=test",
+ "-Dexec.args=" + mvnArgs};
+
+ Process process = new ProcessBuilder(mvnCommand).inheritIO().start();
+ processes.add(process);
+ }
+
+ for (Process process : processes) {
+ waitForProcess(process);
+ }
+
+ validateLockUsageIntervals(num);
+ validateResultInCache(cacheAccessor, num);
+ }
+}
diff --git a/src/test/java/com/microsoft/aad/msal4jextensions/CacheWriterRunnable.java b/src/test/java/com/microsoft/aad/msal4jextensions/CacheWriterRunnable.java
new file mode 100644
index 0000000..95a7599
--- /dev/null
+++ b/src/test/java/com/microsoft/aad/msal4jextensions/CacheWriterRunnable.java
@@ -0,0 +1,59 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.aad.msal4jextensions;
+
+import com.microsoft.aad.msal4jextensions.persistence.ICacheAccessor;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+public class CacheWriterRunnable implements Runnable {
+ ICacheAccessor cacheAccessor;
+
+ CrossProcessCacheFileLock lock;
+
+ String lockHoldingIntervalsFilePath;
+
+ @Override
+ public void run() {
+ long start = 0;
+ long end = 0;
+
+ try {
+ lock.lock();
+ start = System.currentTimeMillis();
+
+ String jvmName = java.lang.management.ManagementFactory.getRuntimeMXBean().getName();
+ String id = jvmName + ":" + Thread.currentThread().getId();
+
+ byte[] data = cacheAccessor.read();
+
+ String strData = (data != null) ? new String(data, StandardCharsets.UTF_8) : "";
+ strData += "< " + id + "\n";
+ strData += "> " + id + "\n";
+
+ // in average deserialize/serialize of token cache with 100 tokens takes 130 ms
+ Thread.sleep(150);
+
+ cacheAccessor.write(strData.getBytes(StandardCharsets.UTF_8));
+ end = System.currentTimeMillis();
+ } catch (Exception ex) {
+ System.out.println("File write failure " + ex.getMessage());
+ ex.printStackTrace();
+ } finally {
+ try {
+ lock.unlock();
+ if(start > 0 && end > 0) {
+ try (FileOutputStream os = new FileOutputStream(lockHoldingIntervalsFilePath, true)) {
+ os.write((start + "-" + end + "\n").getBytes());
+ }
+ }
+ } catch (IOException e) {
+ System.out.println("Failed to unlock");
+ e.printStackTrace();
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/microsoft/aad/msal4jextensions/IRunnableFactory.java b/src/test/java/com/microsoft/aad/msal4jextensions/IRunnableFactory.java
new file mode 100644
index 0000000..4d82ae8
--- /dev/null
+++ b/src/test/java/com/microsoft/aad/msal4jextensions/IRunnableFactory.java
@@ -0,0 +1,8 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.aad.msal4jextensions;
+
+public interface IRunnableFactory {
+ Runnable create(String id);
+}
diff --git a/src/test/java/com/microsoft/aad/msal4jextensions/IntegrationTest.java b/src/test/java/com/microsoft/aad/msal4jextensions/IntegrationTest.java
index d673350..a1d2fff 100644
--- a/src/test/java/com/microsoft/aad/msal4jextensions/IntegrationTest.java
+++ b/src/test/java/com/microsoft/aad/msal4jextensions/IntegrationTest.java
@@ -1,9 +1,10 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
package com.microsoft.aad.msal4jextensions;
import com.microsoft.aad.msal4j.*;
import org.junit.Assert;
-import org.junit.Test;
-
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Path;
@@ -41,9 +42,9 @@ private ConfidentialClientApplication createConfidentialClient() throws IOExcept
ClientCredentialFactory.createFromSecret(TestData.CONFIDENTIAL_CLIENT_SECRET);
return ConfidentialClientApplication.builder(TestData.CONFIDENTIAL_CLIENT_ID, clientCredential)
- .authority(TestData.TENANT_SPECIFIC_AUTHORITY)
- .setTokenCacheAccessAspect(createPersistenceAspect())
- .build();
+ .authority(TestData.TENANT_SPECIFIC_AUTHORITY)
+ .setTokenCacheAccessAspect(createPersistenceAspect())
+ .build();
}
// @Test
@@ -81,4 +82,4 @@ private IAuthenticationResult acquireTokenSilently(ConfidentialClientApplication
return future.join();
}
-}
+}
\ No newline at end of file
diff --git a/src/test/java/com/microsoft/aad/msal4jextensions/KeyChainWriter.java b/src/test/java/com/microsoft/aad/msal4jextensions/KeyChainWriter.java
new file mode 100644
index 0000000..58e20ed
--- /dev/null
+++ b/src/test/java/com/microsoft/aad/msal4jextensions/KeyChainWriter.java
@@ -0,0 +1,30 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.aad.msal4jextensions;
+
+public class KeyChainWriter {
+
+ public static void main(String[] args) throws Exception {
+ String executionId = args[0];
+ String lockFilePath = args[1];
+ String filePath = args[2];
+ String lockHoldingIntervalsFilePath = args[3];
+
+ String serviceName = args[4];
+ String accountName = args[5];
+
+ try {
+ KeyChainWriterRunnable keyChainWriterRunnable =
+ new KeyChainWriterRunnable(executionId, lockFilePath, filePath, lockHoldingIntervalsFilePath,
+ serviceName, accountName);
+
+ keyChainWriterRunnable.run();
+ System.out.println("executionId - " + executionId + " SUCCESS");
+ }
+ catch (Throwable e){
+ System.out.println("executionId - " + executionId + ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> FAILURE <<<<<<<<<<<<<<<<<<<<<<<<<");
+ System.out.println(e.getMessage());
+ }
+ }
+}
diff --git a/src/test/java/com/microsoft/aad/msal4jextensions/KeyChainWriterRunnable.java b/src/test/java/com/microsoft/aad/msal4jextensions/KeyChainWriterRunnable.java
new file mode 100644
index 0000000..8ccb8af
--- /dev/null
+++ b/src/test/java/com/microsoft/aad/msal4jextensions/KeyChainWriterRunnable.java
@@ -0,0 +1,20 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.aad.msal4jextensions;
+
+import com.microsoft.aad.msal4jextensions.persistence.mac.KeyChainAccessor;
+
+public class KeyChainWriterRunnable extends CacheWriterRunnable {
+
+ KeyChainWriterRunnable
+ (String id, String lockFilePath, String filePath, String lockHoldingIntervalsFilePath,
+ String serviceName, String accountName) {
+
+ this.lockHoldingIntervalsFilePath = lockHoldingIntervalsFilePath;
+
+ lock = new CrossProcessCacheFileLock(lockFilePath, 150, 100);
+
+ cacheAccessor = new KeyChainAccessor(filePath, serviceName, accountName);
+ }
+}
diff --git a/src/test/java/com/microsoft/aad/msal4jextensions/KeyRingWriter.java b/src/test/java/com/microsoft/aad/msal4jextensions/KeyRingWriter.java
new file mode 100644
index 0000000..daaed96
--- /dev/null
+++ b/src/test/java/com/microsoft/aad/msal4jextensions/KeyRingWriter.java
@@ -0,0 +1,40 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.aad.msal4jextensions;
+
+public class KeyRingWriter {
+
+ public static void main(String[] args) throws Exception {
+
+ String executionId = args[0];
+ String lockFilePath = args[1];
+ String filePath = args[2];
+ String lockHoldingIntervalsFilePath = args[3];
+
+ String schema = args[4];
+ String label = args[5];
+ String attribute1Key = args[6];
+ String attribute1Value = args[7];
+ String attribute2Key = args[8];
+ String attribute2Value = args[9];
+
+ try {
+ KeyRingWriterRunnable keyRingWriterRunnable =
+ new KeyRingWriterRunnable(executionId,
+ lockFilePath, filePath, lockHoldingIntervalsFilePath,
+ schema,
+ label,
+ attribute1Key,
+ attribute1Value,
+ attribute2Key,
+ attribute2Value);
+
+ keyRingWriterRunnable.run();
+ }
+ catch (Throwable e){
+ System.out.println("executionId - " + executionId + ">>>>>>>>>>>>>>>>> KeyRingWriter FAILURE <<<<<<<<<<<<<<<<<<<<<<<<<");
+ System.out.println(e.getMessage());
+ }
+ }
+}
diff --git a/src/test/java/com/microsoft/aad/msal4jextensions/KeyRingWriterRunnable.java b/src/test/java/com/microsoft/aad/msal4jextensions/KeyRingWriterRunnable.java
new file mode 100644
index 0000000..93cf0ff
--- /dev/null
+++ b/src/test/java/com/microsoft/aad/msal4jextensions/KeyRingWriterRunnable.java
@@ -0,0 +1,33 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.aad.msal4jextensions;
+
+import com.microsoft.aad.msal4jextensions.persistence.linux.KeyRingAccessor;
+
+public class KeyRingWriterRunnable extends CacheWriterRunnable {
+
+ KeyRingWriterRunnable
+ (String id,
+ String lockFilePath, String filePath, String lockHoldingIntervalsFilePath,
+ String schema,
+ String label,
+ String attribute1Key,
+ String attribute1Value,
+ String attribute2Key,
+ String attribute2Value) {
+
+ this.lockHoldingIntervalsFilePath = lockHoldingIntervalsFilePath;
+
+ lock = new CrossProcessCacheFileLock(lockFilePath, 150, 100);
+
+ cacheAccessor = new KeyRingAccessor(filePath,
+ null,
+ schema,
+ label,
+ attribute1Key,
+ attribute1Value,
+ attribute2Key,
+ attribute2Value);
+ }
+}
diff --git a/src/test/java/com/microsoft/aad/msal4jextensions/TestData.java b/src/test/java/com/microsoft/aad/msal4jextensions/TestData.java
index ed3b052..20781cc 100644
--- a/src/test/java/com/microsoft/aad/msal4jextensions/TestData.java
+++ b/src/test/java/com/microsoft/aad/msal4jextensions/TestData.java
@@ -2,10 +2,11 @@
public class TestData {
- static String TENANT_SPECIFIC_AUTHORITY = "https://login.microsoftonline.com/";
+ static String TENANT_SPECIFIC_AUTHORITY = "https://login.microsoftonline.com/pesomka.onmicrosoft.com/";
+ static String AUTHORITY_ORGANIZATION = "https://login.microsoftonline.com/organizations/";
static String GRAPH_DEFAULT_SCOPE = "https://graph.windows.net/.default";
- static String CONFIDENTIAL_CLIENT_ID = "";
- static String CONFIDENTIAL_CLIENT_SECRET = "";
+ static String CONFIDENTIAL_CLIENT_ID = "";
+ static String CONFIDENTIAL_CLIENT_SECRET = "";
}
diff --git a/src/test/resources/log4j.properties b/src/test/resources/log4j.properties
new file mode 100644
index 0000000..73631c6
--- /dev/null
+++ b/src/test/resources/log4j.properties
@@ -0,0 +1,5 @@
+log4j.rootLogger=TRACE, stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd'T'HH:mm:ss.SSS} %-5p [%c] - %m%n
\ No newline at end of file