org.picketlink.idm.file.internal.FileDataSource Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source
*
* Copyright 2013 Red Hat, Inc. and/or its affiliates.
*
* 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.picketlink.idm.file.internal;
import org.picketlink.idm.IdentityManagementException;
import org.picketlink.idm.config.FileIdentityStoreConfiguration;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static org.picketlink.common.util.StringUtil.isNullOrEmpty;
import static org.picketlink.idm.IDMInternalLog.FILE_STORE_LOGGER;
import static org.picketlink.idm.file.internal.FileUtils.createFileIfNotExists;
import static org.picketlink.idm.file.internal.FileUtils.delete;
import static org.picketlink.idm.file.internal.FileUtils.readObject;
/**
* @author Pedro Silva
*/
public class FileDataSource {
/**
*
* Default buffer length when flushing changes to the filesystem. The higher the value greater will be the
* throughput.
*
*/
private static final int FLUSH_BYTE_BUFFER = 1024;
private static final String DEFAULT_WORKING_DIR = System.getProperty("java.io.tmpdir", File.separator + "tmp")
+ File.separator + "pl-idm";
private static final String PARTITIONS_FILE_NAME = "pl-idm-partitions.db";
private static final String IDENTITY_TYPES__FILE_NAME = "pl-idm-identity-types.db";
private static final String ATTRIBUTED_TYPES__FILE_NAME = "pl-idm-attributed-types.db";
private static final String ATTRIBUTES_FILE_NAME = "pl-idm-attributes.db";
private static final String RELATIONSHIPS_FILE_NAME = "pl-idm-relationships.db";
private static final String CREDENTIALS_FILE_NAME = "pl-idm-credentials.db";
private static final String PERMISSIONS_FILE_NAME = "pl-idm-permissions.db";
private final FileIdentityStoreConfiguration configuration;
/**
*
* Holds all stored {@link FilePartition} instances loaded from the filesystem. This {@link Map} is also used to
* persist
* information to the filesystem.
*
*/
private Map partitions;
/**
*
* Holds all stored {@link FileRelationship} instances loaded from the filesystem. This {@link Map} is also used to
* persist
* information to the filesystem.
*
*/
private Map> relationships;
/**
*
* Holds all stored {@link FileAttribute} instances loaded from the filesystem. This {@link Map} is also used to
* persist
* information to the filesystem.
*
*/
private Map attributes;
/**
*
* Holds all stored {@link FileAttributedType} instances loaded from the filesystem. This {@link Map} is also used
* to persist
* information to the filesystem.
*
*/
private Map attributedTypes;
private ExecutorService executorService;
FileDataSource(FileIdentityStoreConfiguration configuration) {
this.configuration = configuration;
init();
}
Map getPartitions() {
return this.partitions;
}
Map> getRelationships() {
return this.relationships;
}
Map getAttributes() {
return attributes;
}
public Map getAttributedTypes() {
return this.attributedTypes;
}
void flushPartitions() {
flush(PARTITIONS_FILE_NAME, getPartitions());
}
void flushPartitions(FilePartition partition) {
initPartition(partition.getId());
flush(PARTITIONS_FILE_NAME, getPartitions());
}
void flushAttributedTypes(FilePartition partition) {
flush(partition, IDENTITY_TYPES__FILE_NAME, partition.getIdentityTypes());
}
void flushRelationships() {
flush(RELATIONSHIPS_FILE_NAME, getRelationships());
}
void flushAttributes() {
flush(ATTRIBUTES_FILE_NAME, getAttributes());
}
void flushAttributedTypes() {
flush(ATTRIBUTED_TYPES__FILE_NAME, getAttributedTypes());
}
void flushCredentials(FilePartition partition) {
FilePartition filePartition = getPartitions().get(partition.getId());
flush(filePartition, CREDENTIALS_FILE_NAME, filePartition.getCredentials());
}
void flushPermissions(FilePartition partition) {
FilePartition filePartition = getPartitions().get(partition.getId());
flush(filePartition, PERMISSIONS_FILE_NAME, filePartition.getPermissions());
}
/**
*
* Initializes the working directory.
*
*
* @return
*/
private void initWorkingDirectory() {
String workingDir = getWorkingDir();
File workingDirectoryFile = new File(workingDir);
if (workingDirectoryFile.exists()) {
if (this.configuration.isAlwaysCreateFiles()) {
FILE_STORE_LOGGER.fileConfigAlwaysCreateWorkingDir(workingDirectoryFile.getPath());
delete(workingDirectoryFile);
}
}
workingDirectoryFile.mkdirs();
FILE_STORE_LOGGER.fileConfigUsingWorkingDir(workingDirectoryFile.getPath());
}
private void init() {
initWorkingDirectory();
File partitionsFile =
createFileIfNotExists(getWorkingDirFile(PARTITIONS_FILE_NAME));
loadPartitions(partitionsFile);
Map> relationships =
readObject(createFileIfNotExists(getWorkingDirFile(RELATIONSHIPS_FILE_NAME)));
if (relationships == null) {
relationships = new ConcurrentHashMap>();
}
this.relationships = relationships;
Map attributes =
readObject(createFileIfNotExists(getWorkingDirFile(ATTRIBUTES_FILE_NAME)));
if (attributes == null) {
attributes = new ConcurrentHashMap();
}
this.attributes = attributes;
Map attrubtedTypes =
readObject(createFileIfNotExists(getWorkingDirFile(ATTRIBUTED_TYPES__FILE_NAME)));
if (attrubtedTypes == null) {
attrubtedTypes = new ConcurrentHashMap();
}
this.attributedTypes = attrubtedTypes;
if (this.configuration.isAsyncWrite()) {
FILE_STORE_LOGGER.fileAsyncWriteEnabled(this.configuration.getAsyncThreadPool());
this.executorService = Executors.newFixedThreadPool(this.configuration.getAsyncThreadPool());
}
}
private void loadPartitions(File partitionsFile) {
this.partitions = readObject(partitionsFile);
if (this.partitions == null) {
if (isDebugEnabled()) {
FILE_STORE_LOGGER.debugf("No partitions to load from %s", partitionsFile.getPath());
}
this.partitions = new ConcurrentHashMap();
} else {
if (isDebugEnabled()) {
FILE_STORE_LOGGER.debugf("Loading [%s] Partition(s) from %s", this.partitions.size(), partitionsFile.getPath());
}
Set> entrySet = this.partitions.entrySet();
for (Entry entry : entrySet) {
initPartition(entry.getKey());
}
}
}
private void initPartition(String partitionId) {
FilePartition filePartition = this.partitions.get(partitionId);
if (isDebugEnabled()) {
FILE_STORE_LOGGER.debugf("Initializing Partition [%s] with id [%s].", filePartition.getEntry().getName(), partitionId);
}
File agentsFile = createFileIfNotExists(getWorkingDirFile(partitionId + File.separator + IDENTITY_TYPES__FILE_NAME));
Map> identityTypes = readObject(agentsFile);
if (identityTypes == null) {
identityTypes = new ConcurrentHashMap>();
}
filePartition.setIdentityTypes(identityTypes);
if (isDebugEnabled()) {
FILE_STORE_LOGGER.debugf("Loaded Identity Types [%s] for Partition [%s].", filePartition.getIdentityTypes().size(), filePartition.getId());
}
File credentialsFile = createFileIfNotExists(getWorkingDirFile(partitionId + File.separator + CREDENTIALS_FILE_NAME));
Map>> credentials = readObject(credentialsFile);
if (credentials == null) {
credentials = new ConcurrentHashMap>>();
}
filePartition.setCredentials(credentials);
if (isDebugEnabled()) {
FILE_STORE_LOGGER.debugf("Loaded Credentials [%s] for Partition [%s].", filePartition.getCredentials().size(), filePartition.getId());
}
File permissionsFile = createFileIfNotExists(getWorkingDirFile(partitionId + File.separator + PERMISSIONS_FILE_NAME));
Map> permissions = readObject(permissionsFile);
if (permissions == null) {
permissions = new ConcurrentHashMap>();
}
filePartition.setPermissions(permissions);
if (isDebugEnabled()) {
FILE_STORE_LOGGER.debugf("Loaded Permissions [%s] for Partition [%s].", filePartition.getPermissions().size(), filePartition.getId());
}
}
private String getWorkingDir() {
String workingDir = this.configuration.getWorkingDir();
if (isNullOrEmpty(workingDir)) {
workingDir = DEFAULT_WORKING_DIR;
}
return workingDir;
}
private void flush(final FilePartition partition, final String fileName, final Object object) {
flush(partition.getId() + File.separator + fileName, object);
}
private void flush(final String fileName, final Object object) {
if (this.configuration.isAsyncWrite()) {
this.executorService.execute(new Runnable() {
@Override
public void run() {
performFlush(fileName, object);
}
});
} else {
performFlush(fileName, object);
}
}
private synchronized void performFlush(final String fileName, final Object object) {
ObjectOutputStream oos = null;
ByteArrayOutputStream bos = null;
RandomAccessFile randomAccessFile = null;
try {
randomAccessFile = new RandomAccessFile(getWorkingDir() + File.separator + fileName, "rw");
FileChannel channel = randomAccessFile.getChannel();
bos = new ByteArrayOutputStream(FLUSH_BYTE_BUFFER);
oos = new ObjectOutputStream(bos);
oos.writeObject(object);
channel.write(ByteBuffer.wrap(bos.toByteArray()));
} catch (Exception e) {
throw new IdentityManagementException("Error flushing changes to file system.", e);
} finally {
try {
if (randomAccessFile != null) {
randomAccessFile.close();
}
} catch (IOException e) {
}
try {
if (oos != null) {
oos.close();
}
} catch (Exception e) {
}
try {
if (bos != null) {
bos.close();
}
} catch (Exception e) {
}
}
}
private File getWorkingDirFile(String name) {
return new File(getWorkingDir() + File.separator + name);
}
private boolean isDebugEnabled() {
return FILE_STORE_LOGGER.isDebugEnabled();
}
}