All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.att.research.xacml.std.pip.engines.jdbc.JDBCEngine Maven / Gradle / Ivy
/*
*
* Copyright (c) 2013,2019 AT&T Knowledge Ventures
* SPDX-License-Identifier: MIT
*/
package com.att.research.xacml.std.pip.engines.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.att.research.xacml.api.Attribute;
import com.att.research.xacml.api.pip.PIPException;
import com.att.research.xacml.api.pip.PIPFinder;
import com.att.research.xacml.api.pip.PIPRequest;
import com.att.research.xacml.api.pip.PIPResponse;
import com.att.research.xacml.std.pip.StdMutablePIPResponse;
import com.att.research.xacml.std.pip.StdPIPResponse;
import com.att.research.xacml.std.pip.engines.StdConfigurableEngine;
import com.att.research.xacml.util.AttributeUtils;
import com.google.common.base.Splitter;
import com.google.common.cache.Cache;
/**
* PIPEgineJDBC extends {@link com.att.research.xacml.std.pip.engines.StdConfigurableEngine} to implement a PIP that
* retrieves XACML attributes from a database using JDBC. This is a minimal implementation that does not do any caching of
* results. It does not perform JDBC connection pooling either.
*
* @author Christopher A. Rath
* @version $Revision$
*/
public class JDBCEngine extends StdConfigurableEngine {
public static final String PROP_TYPE = "type";
public static final String PROP_JDBC_DRIVER = "jdbc.driver";
public static final String PROP_JDBC_URL = "jdbc.url";
public static final String PROP_JDBC_CONN = "jdbc.conn";
public static final String PROP_JDBC_CONN_USER = "jdbc.conn.user";
public static final String PROP_JDBC_CONN_PASS = "jdbc.conn.password";
public static final String PROP_RESOLVERS = "resolvers";
public static final String PROP_RESOLVER = "resolver";
public static final String PROP_CLASSNAME = "classname";
public static final String TYPE_JDBC = "jdbc";
public static final String TYPE_JNDI = "jndi";
protected Logger logger = LoggerFactory.getLogger(this.getClass());
private String type;
private String jndiDataSource;
private String jdbcDriverClass;
private boolean jdbcDriverClassLoaded;
private String jdbcUrl;
private Properties jdbcConnProperties = new Properties();
private List jdbcResolvers = new ArrayList();
/**
* If the JDBC driver Class
has not been loaded yet, do so now.
*
* @throws ClassNotFoundException
*/
protected void loadDriverClass() throws ClassNotFoundException {
if (!this.jdbcDriverClassLoaded) {
synchronized(this) {
if (!this.jdbcDriverClassLoaded) {
Class.forName(this.jdbcDriverClass);
this.jdbcDriverClassLoaded = true;
}
}
}
}
/**
* Creates a JDBC {@link java.sql.Connection} to the database. Extensions to the JDBCEngine
class can perform
* connection pooling or other connection reuse optimizations here.
*
* @return a Connection
to use to execute the query
* @throws PIPException if there is an error creating the JDBC Connection
.
*/
protected Connection getConnection() throws PIPException {
/*
* Check what type we are
*/
if (this.type.equals(TYPE_JDBC)) {
return this.getJDBCConnection();
} else {
return this.getJNDIConnection();
}
}
protected Connection getJDBCConnection() throws PIPException {
/*
* Ensure the driver class is loaded
*/
try {
this.loadDriverClass();
} catch (ClassNotFoundException ex) {
this.logger.error("ClassNotFoundException loading JDBC driver class '" + this.jdbcDriverClass + "'", ex);
throw new PIPException("ClassNotFoundException loading JDBC driver class '" + this.jdbcDriverClass + "'", ex);
}
/*
* Try to create a new Connection
*/
Connection connectionResult = null;
try {
connectionResult = DriverManager.getConnection(this.jdbcUrl, this.jdbcConnProperties);
} catch (SQLException ex) {
this.logger.error("SQLException creating Connection", ex);
throw new PIPException("SQLException creating Connection", ex);
}
return connectionResult;
}
protected Connection getJNDIConnection() throws PIPException {
try {
Context initialContext = new InitialContext();
DataSource datasource = (DataSource) initialContext.lookup(this.jndiDataSource);
if (datasource == null) {
throw new PIPException("");
}
return datasource.getConnection();
} catch (NamingException | SQLException e) {
this.logger.error("JNDIException creating Connection", e);
throw new PIPException("JNDIException creating Connection", e);
}
}
protected void getAttributes(PIPRequest pipRequest, PIPFinder pipFinder, JDBCResolver jdbcResolver, StdMutablePIPResponse pipResponse) throws PIPException {
/*
* First we need to get a PreparedStatement
*/
Connection connection = this.getConnection();
PreparedStatement preparedStatement = jdbcResolver.getPreparedStatement(this, pipRequest, pipFinder, connection);
if (preparedStatement == null) {
this.logger.debug(this.getName() + " does not handle " + pipRequest.toString());
try {
if (connection != null) {
connection.close();
}
} catch (Exception e) {
}
return;
}
/*
* Is it in the cache?
*/
this.logger.debug(preparedStatement.toString());
Cache cache = this.getCache();
if (cache != null) {
// TODO - a cache key
}
/*
* Execute the prepared statement
*/
ResultSet resultSet = null;
try {
resultSet = preparedStatement.executeQuery();
} catch (SQLException ex) {
this.logger.error("SQLException executing query: " + ex.toString(), ex);
// TODO: Should we re-throw the exception, or just return an empty response?
}
if (resultSet == null) {
try {
preparedStatement.close();
} catch (SQLException e) {
this.logger.error("SQLException closing preparedStatment: " + e.toString(), e);
}
try {
if (connection != null) {
connection.close();
}
} catch (Exception e) {
}
return;
}
try {
/*
* Get all the results
*/
while (resultSet.next()) {
List listAttributes = jdbcResolver.decodeResult(resultSet);
if (listAttributes != null) {
pipResponse.addAttributes(listAttributes);
}
}
/*
* Save it in the cache
*/
if (cache != null) {
// TODO
}
} catch (SQLException ex) {
this.logger.error("SQLException decoding results: " + ex.toString());
// TODO: Should we re-throw the exception or just continue
} finally {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
this.logger.error("SQLException closing resultSet: " + e.toString() + " (May be memory leak)");
}
}
try {
preparedStatement.close();
} catch (SQLException e) {
this.logger.error("SQLException closing preparedStatement: " + e.toString() + " (May be memory leak)");
}
try {
connection.close();
} catch (SQLException e) {
this.logger.error("SQLException closing connection: " + e.toString() + " (May be memory leak)");
}
}
}
@Override
public PIPResponse getAttributes(PIPRequest pipRequest, PIPFinder pipFinder) throws PIPException {
if (this.jdbcResolvers.size() == 0) {
throw new IllegalStateException(this.getClass().getCanonicalName() + " is not configured");
}
StdMutablePIPResponse mutablePIPResponse = new StdMutablePIPResponse();
for (JDBCResolver jdbcResolver : this.jdbcResolvers) {
this.getAttributes(pipRequest, pipFinder, jdbcResolver, mutablePIPResponse);
}
if (mutablePIPResponse.getAttributes().size() == 0) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("returning empty response");
}
return StdPIPResponse.PIP_RESPONSE_EMPTY;
} else {
if (this.logger.isDebugEnabled()) {
this.logger.trace("Returning " + mutablePIPResponse.getAttributes().size() + " attributes");
for (Attribute attribute : mutablePIPResponse.getAttributes()) {
this.logger.debug(AttributeUtils.prettyPrint(attribute));
}
} else if (this.logger.isDebugEnabled()) {
// this.logger.debug("Returning " + mutablePIPResponse.getAttributes().size() + " attributes");
// this.logger.debug(mutablePIPResponse.getAttributes());
}
return new StdPIPResponse(mutablePIPResponse);
}
}
/**
* Creates a new {@link com.att.research.xacml.std.pip.engines.jdbc.JDBCResolver} by looking up the "classname"
* property for the given String
resolver ID and then calling its configure
method.
*
* @param resolverId the String
identifier of the resolver to configure
* @param properties the Properties
to search for the "classname" and any resolver-specific properties
* @throws PIPException if there is an error creating the JDBCResolver
.
*/
protected void createResolver(String resolverId, Properties properties) throws PIPException {
String propPrefix = resolverId + ".";
String resolverClassName = properties.getProperty(propPrefix + PROP_CLASSNAME);
if (resolverClassName == null || resolverClassName.length() == 0) {
this.logger.error("No '" + propPrefix + PROP_CLASSNAME + "' property.");
throw new PIPException("No '" + propPrefix + PROP_CLASSNAME + "' property.");
}
try {
Class> resolverClass = Class.forName(resolverClassName);
if (!JDBCResolver.class.isAssignableFrom(resolverClass)) {
this.logger.error("JDBCResolver class " + propPrefix + " does not implement " + JDBCResolver.class.getCanonicalName());
throw new PIPException("JDBCResolver class " + propPrefix + " does not implement " + JDBCResolver.class.getCanonicalName());
}
JDBCResolver jdbcResolver = JDBCResolver.class.cast(resolverClass.newInstance());
jdbcResolver.configure(resolverId, properties, this.getIssuer());
this.jdbcResolvers.add(jdbcResolver);
} catch (Exception ex) {
this.logger.error("Exception creating JDBCResolver: " + ex.getMessage(), ex);
throw new PIPException("Exception creating JDBCResolver", ex);
}
}
@Override
public void configure(String id, Properties properties) throws PIPException {
//
// Our configurable properties
//
super.configure(id, properties);
//
// Prefix
//
String propPrefix = id + ".";
//
// What is our type?
//
this.type = properties.getProperty(propPrefix + PROP_TYPE, TYPE_JDBC);
//
// These are mandatory for our engine to work.
//
if ((this.jdbcDriverClass = properties.getProperty(propPrefix + PROP_JDBC_DRIVER)) == null) {
this.logger.error("No '" + propPrefix + PROP_JDBC_DRIVER + "' property");
throw new PIPException("No '" + propPrefix + PROP_JDBC_DRIVER + "' property");
}
try {
Class.forName(this.jdbcDriverClass);
} catch (Exception ex) {
this.logger.error("Exception instantiating JDBC driver class '" + this.jdbcDriverClass + "'", ex);
throw new PIPException("Exception instantiating JDBC driver class '" + this.jdbcDriverClass + "'", ex);
}
if ((this.jdbcUrl = properties.getProperty(propPrefix + PROP_JDBC_URL)) == null) {
this.logger.error("No '" + propPrefix + PROP_JDBC_URL + "' property");
throw new PIPException("No '" + propPrefix + PROP_JDBC_URL + "' property");
}
//
// Go through all our resolvers
//
String propResolverPrefix = propPrefix + PROP_RESOLVERS;
String stringProp = properties.getProperty(propResolverPrefix);
if (stringProp == null || stringProp.isEmpty()) {
this.logger.error("No '" + propResolverPrefix + "' property");
throw new PIPException("No '" + propResolverPrefix + "' property");
}
for (String resolverId : Splitter.on(',').trimResults().omitEmptyStrings().split(stringProp)) {
this.createResolver(propPrefix + PROP_RESOLVER + "." + resolverId, properties);
}
//
// Check for these properties. They are not required.
//
if ((stringProp = properties.getProperty(propPrefix + PROP_JDBC_CONN_USER)) != null) {
this.jdbcConnProperties.setProperty("user", stringProp);
}
if ((stringProp = properties.getProperty(propPrefix + PROP_JDBC_CONN_PASS)) != null) {
this.jdbcConnProperties.setProperty("password", stringProp);
}
String jdbcConnPrefix = propPrefix + PROP_JDBC_CONN;
if ((stringProp = properties.getProperty(jdbcConnPrefix)) != null) {
jdbcConnPrefix = jdbcConnPrefix + ".";
String[] connProperties = stringProp.split("[,]",0);
for (String connProperty : connProperties) {
if ((stringProp = properties.getProperty(jdbcConnPrefix + connProperty)) != null) {
this.jdbcConnProperties.setProperty(connProperty, stringProp);
}
}
}
}
@Override
public Collection attributesRequired() {
Set attributes = new HashSet();
for (JDBCResolver jdbcResolver : this.jdbcResolvers) {
jdbcResolver.attributesRequired(attributes);
}
return attributes;
}
@Override
public Collection attributesProvided() {
Set attributes = new HashSet();
for (JDBCResolver jdbcResolver : this.jdbcResolvers) {
jdbcResolver.attributesProvided(attributes);
}
return attributes;
}
}