http://git-wip-us.apache.org/repos/asf/airavata/blob/e36c145d/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/db/utils/ConnectionPool.java ---------------------------------------------------------------------- diff --git a/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/db/utils/ConnectionPool.java b/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/db/utils/ConnectionPool.java new file mode 100644 index 0000000..02484f7 --- /dev/null +++ b/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/db/utils/ConnectionPool.java @@ -0,0 +1,382 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.airavata.sharing.registry.db.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Stack; +import java.util.concurrent.Semaphore; + + +/** + * A class for preallocating, recycling, and managing JDBC connections. + */ +public class ConnectionPool { + private static final Logger logger = LoggerFactory.getLogger(ConnectionPool.class); + + private long MAX_IDLE_TIME = 5 * 60 * 1000; // 5 minutes + + private String driver; + private String url; + private String username; + private String password; + private String jdbcUrl; + + private int maxConnections; + + private boolean autoCommit = true; + private boolean waitIfBusy; + + private Semaphore needConnection = new Semaphore(0); + private boolean stop; + + private Stack<Connection> availableConnections; + private Stack<Connection> busyConnections; + + private HashMap<Connection, Long> lastAccessTimeRecord = new HashMap<Connection, Long>(); + + private String urlType = ""; + + private DataSource datasource; + + private int transactionIsolation = Connection.TRANSACTION_NONE; + + private Thread clenupThread; + private Thread producerThread; + + public ConnectionPool(String driver, String url, String username, String password, int initialConnections, + int maxConnections, boolean waitIfBusy) throws SQLException { + this.driver = driver; + this.url = url; + this.username = username; + this.password = password; + this.urlType = "speratedURL"; + initialize(initialConnections, maxConnections, waitIfBusy); + } + + public ConnectionPool(String driver, String jdbcUrl, int initialConnections, int maxConnections, + boolean waitIfBusy, boolean autoCommit, int transactionIsolation) throws SQLException { + this.driver = driver; + this.jdbcUrl = jdbcUrl; + this.urlType = "simpleURL"; + this.autoCommit = autoCommit; + this.transactionIsolation = transactionIsolation; + initialize(initialConnections, maxConnections, waitIfBusy); + } + + public ConnectionPool(String driver, String jdbcUrl, int initialConnections, int maxConnections, boolean waitIfBusy) + throws SQLException { + this.driver = driver; + this.jdbcUrl = jdbcUrl; + this.urlType = "simpleURL"; + initialize(initialConnections, maxConnections, waitIfBusy); + } + + public ConnectionPool(DataSource dataSource, int initialConnections, int maxConnections, boolean waitIfBusy) + throws SQLException { + this.urlType = "dataSource"; + this.datasource = dataSource; + initialize(initialConnections, maxConnections, waitIfBusy); + } + + /** + * Check if this connection pool is auto commit or not + * + * @return + */ + public boolean isAutoCommit() { + return this.autoCommit; + } + + private void initialize(int initialConnections, int maxConnections, boolean waitIfBusy) throws SQLException { + this.maxConnections = maxConnections; + this.waitIfBusy = waitIfBusy; + + int sizeOfConnections = (initialConnections > maxConnections) ? maxConnections : initialConnections; + + availableConnections = new Stack<Connection>(); + busyConnections = new Stack<Connection>(); + + for (int i = 0; i < sizeOfConnections; i++) { + Connection con = makeNewConnection(); + setTimeStamp(con); + availableConnections.push(con); + + } + + producerThread = new Thread(new FillUpThread()); + producerThread.start(); + + clenupThread = new Thread(new CleanUpThread()); + clenupThread.start(); + } + + public synchronized Connection getConnection() throws SQLException { + if (!availableConnections.isEmpty()) { + Connection existingConnection = availableConnections.pop(); + + // If connection on available list is closed (e.g., + // it timed out), then remove it from available list + // and race for a connection again. + if (existingConnection.isClosed()) { + lastAccessTimeRecord.remove(existingConnection); + // notifyAll for fairness + notifyAll(); + } else { + busyConnections.push(existingConnection); + setTimeStamp(existingConnection); + return existingConnection; + } + } else if (!waitIfBusy && busyConnections.size() >= maxConnections) { + // You reached maxConnections limit and waitIfBusy flag is false. + // Throw SQLException in such a case. + throw new SQLException("Connection limit reached"); + } else { + + if (busyConnections.size() < maxConnections) { + // available connection is empty, but total number of connection + // doesn't reach maxConnection. Request for more connection + needConnection.release(); + } + + try { + // wait for free connection + wait(); + } catch (InterruptedException ie) { + } + } + // always race for connection forever + return getConnection(); + } + + // This explicitly makes a new connection. Called in + // the foreground when initializing the ConnectionPool, + // and called in the background when running. + private Connection makeNewConnection() throws SQLException { + try { + // Load database driver if not already loaded + Class.forName(driver); + Connection connection; + // Establish network connection to database + if (urlType.equals("speratedURL")) { + connection = DriverManager.getConnection(url, username, password); + } else if (urlType.equals("simpleURL")) { + connection = DriverManager.getConnection(jdbcUrl); + } else { // if(urlType.equals("dataSource")){ + connection = datasource.getConnection(); + } + connection.setTransactionIsolation(this.transactionIsolation); + connection.setAutoCommit(this.autoCommit); + return connection; + } catch (ClassNotFoundException cnfe) { + // Simplify try/catch blocks of people using this by + // throwing only one exception type. + throw new SQLException("Can't find class for driver: " + driver); + } + } + + private synchronized void fillUpConnection(Connection conn) { + setTimeStamp(conn); + availableConnections.push(conn); + + // notify all since new connection is created + notifyAll(); + } + + private void setTimeStamp(Connection connection) { + lastAccessTimeRecord.put(connection, System.currentTimeMillis()); + } + + // The database connection cannot be left idle for too long, otherwise TCP + // connection will be broken. + /** + * From http://forums.mysql.com/read.php?39,28450,57460#msg-57460 Okay, then it looks like wait_timeout on the + * server is killing your connection (it is set to 8 hours of idle time by default). Either set that value higher on + * your server, or configure your connection pool to not hold connections idle that long (I prefer the latter). Most + * folks I know that run MySQL with a connection pool in high-load production environments only let connections sit + * idle for a matter of minutes, since it only takes a few milliseconds to open a connection, and the longer one + * sits idle the more chance it will go "bad" because of a network hiccup or the MySQL server being restarted. + * + * @throws java.sql.SQLException + */ + private boolean isConnectionStale(Connection connection) { + long currentTime = System.currentTimeMillis(); + long lastAccess = lastAccessTimeRecord.get(connection); + if (currentTime - lastAccess > MAX_IDLE_TIME) { + return true; + } else + return false; + } + + private synchronized void closeStaleConnections() { + // close idle connections + Iterator<Connection> iter = availableConnections.iterator(); + while (iter.hasNext()) { + Connection existingConnection = iter.next(); + if (isConnectionStale(existingConnection)) { + try { + existingConnection.close(); + iter.remove(); + } catch (SQLException sql) { + logger.error(sql.getMessage(), sql); + } + } + } + // close busy connections that have been checked out for too long. + // This should not happen since this means program has bug for not + // releasing connections . + iter = busyConnections.iterator(); + while (iter.hasNext()) { + Connection busyConnection = iter.next(); + if (isConnectionStale(busyConnection)) { + try { + busyConnection.close(); + iter.remove(); + logger.warn("****Connection has checked out too long. Forced release. Check the program for calling release connection [free(Connection) method]"); + } catch (SQLException sql) { + logger.error(sql.getMessage(), sql); + } + } + } + } + + public synchronized void free(Connection connection) { + busyConnections.removeElement(connection); + availableConnections.addElement(connection); + // Wake up threads that are waiting for a connection + notifyAll(); + } + + /** + * Close all the connections. Use with caution: be sure no connections are in use before calling. Note that you are + * not <I>required</I> to call this when done with a ConnectionPool, since connections are guaranteed to be closed + * when garbage collected. But this method gives more control regarding when the connections are closed. + */ + public synchronized void dispose() { + logger.info("Connection Pool Shutting down"); + + // stop clean up thread + this.stop = true; + this.clenupThread.interrupt(); + + // stop producer up thread + this.producerThread.interrupt(); + + // close all connection + closeConnections(availableConnections); + availableConnections = new Stack<Connection>(); + closeConnections(busyConnections); + busyConnections = new Stack<Connection>(); + lastAccessTimeRecord.clear(); + + logger.info("All connection is closed"); + + try { + this.clenupThread.join(); + this.producerThread.join(); + } catch (Exception e) { + logger.error("Cannot shutdown cleanup thread", e); + } + + logger.info("Connection Pool Shutdown"); + } + + private void closeConnections(Stack<Connection> connections) { + while (!connections.isEmpty()) { + Connection connection = connections.pop(); + try { + if (!connection.isClosed()) { + connection.close(); + } + } catch (SQLException sqle) { + // Ignore errors; garbage collect anyhow + logger.warn(sqle.getMessage()); + } + } + } + + public synchronized String toString() { + String info = "ConnectionPool(" + url + "," + username + ")" + ", available=" + availableConnections.size() + + ", busy=" + busyConnections.size() + ", max=" + maxConnections; + return (info); + } + + class CleanUpThread implements Runnable { + public void run() { + while (!stop) { + try { + Thread.sleep(MAX_IDLE_TIME); + closeStaleConnections(); + } catch (InterruptedException e) { + logger.info("Clean up thread is interrupted to close"); + } + } + } + } + + class FillUpThread implements Runnable { + public void run() { + while (!stop) { + try { + // block until get + needConnection.acquire(); + + Connection conn = makeNewConnection(); + fillUpConnection(conn); + } catch (SQLException e) { + // cannot create connection (increase semaphore value back) + needConnection.release(); + logger.error(e.getMessage(), e); + } catch (InterruptedException e) { + logger.info("Fill up thread is interrupted to close"); + break; + } + } + } + } + + public void shutdown() throws SQLException{ + for (Connection c : availableConnections) { + try { + c.close(); + } catch (SQLException e) { + logger.error("Error while closing the connection", e); + throw new SQLException("Error while closing the connection", e); + } + } + + for (Connection c : busyConnections) { + try { + c.close(); + } catch (SQLException e) { + logger.error("Error while closing the connection", e); + throw new SQLException("Error while closing the connection", e); + } + } + } +} \ No newline at end of file
http://git-wip-us.apache.org/repos/asf/airavata/blob/e36c145d/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/db/utils/DBConstants.java ---------------------------------------------------------------------- diff --git a/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/db/utils/DBConstants.java b/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/db/utils/DBConstants.java new file mode 100644 index 0000000..387e0e5 --- /dev/null +++ b/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/db/utils/DBConstants.java @@ -0,0 +1,101 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.airavata.sharing.registry.db.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DBConstants { + private final static Logger logger = LoggerFactory.getLogger(DBConstants.class); + + public static int SELECT_MAX_ROWS = 1000; + + public static class DomainTable { + public static String DOMAIN_ID = "domainId"; + public static String NAME = "name"; + public static String DESCRIPTION = "description"; + public static final String CREATED_TIME = "createdTime"; + public static final String UPDATED_TIME = "updatedTime"; + } + + public static class UserTable { + public static String USER_ID = "userId"; + public static String DOMAIN_ID = "domainId"; + public static String USER_NAME = "userName"; + public static final String CREATED_TIME = "createdTime"; + public static final String UPDATED_TIME = "updatedTime"; + } + + public static class UserGroupTable { + public static String GROUP_ID = "groupId"; + public static String DOMAIN_ID = "domainId"; + public static String NAME = "name"; + public static String DESCRIPTION = "description"; + public static String OWNER_ID = "ownerId"; + public static String GROUP_TYPE = "groupType"; + public static final String CREATED_TIME = "createdTime"; + public static final String UPDATED_TIME = "updatedTime"; + } + + public static class GroupMembershipTable { + public static String PARENT_ID = "parentId"; + public static String CHILD_ID = "childId"; + public static String CHILD_TYPE = "childType"; + public static final String CREATED_TIME = "createdTime"; + public static final String UPDATED_TIME = "updatedTime"; + } + + public static class EntityTypeTable { + public static String ENTITY_TYPE_ID = "entityTypeId"; + public static String DOMAIN_ID = "domainId"; + public static final String CREATED_TIME = "createdTime"; + public static final String UPDATED_TIME = "updatedTime"; + } + + public static class PermissionTypeTable { + public static String ENTITY_TYPE_ID = "permissionTypeId"; + public static String DOMAIN_ID = "domainId"; + public static String NAME = "name"; + public static final String CREATED_TIME = "createdTime"; + public static final String UPDATED_TIME = "updatedTime"; + } + + public static class EntityTable { + public static String ENTITY_ID = "entityId"; + public static String PARENT_ENTITY_ID = "parentEntityId"; + public static String ENTITY_TYPE_ID = "entityTypeId"; + public static String NAME = "name"; + public static String DESCRIPTION = "description"; + public static String FULL_TEXT = "fullText"; + public static final String CREATED_TIME = "createdTime"; + public static final String UPDATED_TIME = "updatedTime"; + } + + public static class SharingTable { + public static String PERMISSION_TYPE_ID = "permissionTypeId"; + public static String ENTITY_ID = "entityId"; + public static String GROUP_ID = "groupId"; + public static String INHERITED_PARENT_ID = "inheritedParentId"; + public static final String SHARING_TYPE = "sharingType"; + public static final String CREATED_TIME = "createdTime"; + public static final String UPDATED_TIME = "updatedTime"; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/airavata/blob/e36c145d/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/db/utils/DatabaseCreator.java ---------------------------------------------------------------------- diff --git a/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/db/utils/DatabaseCreator.java b/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/db/utils/DatabaseCreator.java new file mode 100644 index 0000000..5aa423c --- /dev/null +++ b/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/db/utils/DatabaseCreator.java @@ -0,0 +1,353 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.airavata.sharing.registry.db.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.sql.*; +import java.util.StringTokenizer; + +/** + * This class creates the database tables required for airavata with default configuration this + * class creates derby database in server mode. User can specify required database in appropriate + * properties files. + */ +public class DatabaseCreator { + private final static Logger logger = LoggerFactory.getLogger(DatabaseCreator.class); + + public enum DatabaseType { + derby("(?i).*derby.*"), mysql("(?i).*mysql.*"), other(""); + + private String pattern; + + private DatabaseType(String matchingPattern) { + this.pattern = matchingPattern; + } + + public String getMatchingPattern() { + return this.pattern; + } + } + + private static DatabaseType[] supportedDatabase = new DatabaseType[] { DatabaseType.derby, DatabaseType.mysql }; + + private static Logger log = LoggerFactory.getLogger(DatabaseCreator.class); + private static final String delimiter = ";"; + + /** + * Creates database + * + * @throws Exception + */ + public static void createRegistryDatabase(String prefix, Connection conn) throws Exception { + createDatabase(prefix, conn); + } + + + + /** + * Checks whether database tables are created by using select * on given table name + * + * @param tableName + * Table which should be existed + * @return <code>true</core> if checkSQL is success, else <code>false</code> . + */ + public static boolean isDatabaseStructureCreated(String tableName, Connection conn) { + try { + + log.debug("Running a query to test the database tables existence."); + + // check whether the tables are already created with a query + Statement statement = null; + try { + statement = conn.createStatement(); + ResultSet rs = statement.executeQuery("select * from " + tableName); + if (rs != null) { + rs.close(); + } + } finally { + try { + if (statement != null) { + statement.close(); + } + } catch (SQLException e) { + return false; + } + } + } catch (SQLException e) { + return false; + } + + return true; + } + + /** + * executes given sql + * + * @param sql + * @throws Exception + */ + private static void executeSQL(String sql, Connection conn) throws Exception { + // Check and ignore empty statements + if ("".equals(sql.trim())) { + return; + } + + Statement statement = null; + try { + log.debug("SQL : " + sql); + + boolean ret; + int updateCount = 0, updateCountTotal = 0; + statement = conn.createStatement(); + ret = statement.execute(sql); + updateCount = statement.getUpdateCount(); + do { + if (!ret) { + if (updateCount != -1) { + updateCountTotal += updateCount; + } + } + ret = statement.getMoreResults(); + if (ret) { + updateCount = statement.getUpdateCount(); + } + } while (ret); + + log.debug(sql + " : " + updateCountTotal + " rows affected"); + + SQLWarning warning = conn.getWarnings(); + while (warning != null) { + log.info(warning + " sql warning"); + warning = warning.getNextWarning(); + } + conn.clearWarnings(); + } catch (SQLException e) { + if (e.getSQLState().equals("X0Y32")) { + // eliminating the table already exception for the derby + // database + log.info("Table Already Exists", e); + } else { + throw new Exception("Error occurred while executing : " + sql, e); + } + } finally { + if (statement != null) { + try { + statement.close(); + } catch (SQLException e) { + log.error("Error occurred while closing result set.", e); + } + } + } + } + + /** + * computes relatational database type using database name + * + * @return DatabaseType + * @throws Exception + * + */ + public static DatabaseType getDatabaseType(Connection conn) throws Exception { + try { + if (conn != null && (!conn.isClosed())) { + DatabaseMetaData metaData = conn.getMetaData(); + String databaseProductName = metaData.getDatabaseProductName(); + return checkType(databaseProductName); + } + } catch (SQLException e) { + String msg = "Failed to create Airavata database." + e.getMessage(); + log.error(msg, e); + throw new Exception(msg, e); + } + return DatabaseType.other; + } + + /** + * Overloaded method with String input + * + * @return DatabaseType + * @throws Exception + * + */ + public static DatabaseType getDatabaseType(String dbUrl) throws Exception { + return checkType(dbUrl); + } + + private static DatabaseType checkType(String text) throws Exception { + try { + if (text != null) { + for (DatabaseType type : supportedDatabase) { + if (text.matches(type.getMatchingPattern())) + return type; + } + } + String msg = "Unsupported database: " + text + + ". Database will not be created automatically by the Airavata. " + + "Please create the database using appropriate database scripts for " + "the database."; + throw new Exception(msg); + + } catch (SQLException e) { + String msg = "Failed to create Airavatadatabase." + e.getMessage(); + log.error(msg, e); + throw new Exception(msg, e); + } + } + + /** + * Get scripts location which is prefix + "-" + databaseType + ".sql" + * + * @param prefix + * @param databaseType + * @return script location + */ + private static String getScriptLocation(String prefix, DatabaseType databaseType) { + String scriptName = prefix + "-" + databaseType + ".sql"; + log.debug("Loading database script from :" + scriptName); + return scriptName; + } + + private static void createDatabase(String prefix, Connection conn) throws Exception { + Statement statement = null; + try { + conn.setAutoCommit(false); + statement = conn.createStatement(); + executeSQLScript(getScriptLocation(prefix, DatabaseCreator.getDatabaseType(conn)), conn); + conn.commit(); + log.debug("Tables are created successfully."); + } catch (SQLException e) { + String msg = "Failed to create database tables for Airavata resource store. " + e.getMessage(); + log.error(msg, e); + conn.rollback(); + throw new Exception(msg, e); + } finally { + conn.setAutoCommit(true); + try { + if (statement != null) { + statement.close(); + } + } catch (SQLException e) { + log.error("Failed to close statement.", e); + } + } + } + + private static void executeSQLScript(String dbscriptName, Connection conn) throws Exception { + StringBuffer sql = new StringBuffer(); + BufferedReader reader = null; + + try { + InputStream is = DatabaseCreator.class.getClassLoader().getResourceAsStream(dbscriptName); + if(is == null) { + logger.info("Script file not found at " + dbscriptName + ". Uses default database script file"); + DatabaseType databaseType = DatabaseCreator.getDatabaseType(conn); + if(databaseType.equals(DatabaseType.derby)){ + is = DatabaseCreator.class.getClassLoader().getResourceAsStream("experiment-derby.sql"); + }else if(databaseType.equals(DatabaseType.mysql)){ + is = DatabaseCreator.class.getClassLoader().getResourceAsStream("experiment-mysql.sql"); + } + } + reader = new BufferedReader(new InputStreamReader(is)); + String line; + while ((line = reader.readLine()) != null) { + line = line.trim(); + if (line.startsWith("//")) { + continue; + } + if (line.startsWith("--")) { + continue; + } + StringTokenizer st = new StringTokenizer(line); + if (st.hasMoreTokens()) { + String token = st.nextToken(); + if ("REM".equalsIgnoreCase(token)) { + continue; + } + } + sql.append(" ").append(line); + + // SQL defines "--" as a comment to EOL + // and in Oracle it may contain a hint + // so we cannot just remove it, instead we must end it + if (line.indexOf("--") >= 0) { + sql.append("\n"); + } + if ((checkStringBufferEndsWith(sql, delimiter))) { + executeSQL(sql.substring(0, sql.length() - delimiter.length()), conn); + sql.replace(0, sql.length(), ""); + } + } + // Catch any statements not followed by ; + if (sql.length() > 0) { + executeSQL(sql.toString(), conn); + } + } catch (IOException e) { + log.error("Error occurred while executing SQL script for creating Airavata database", e); + throw new Exception("Error occurred while executing SQL script for creating Airavata database", e); + + } finally { + if (reader != null) { + reader.close(); + } + } + } + + /** + * Checks that a string buffer ends up with a given string. It may sound trivial with the existing JDK API but the + * various implementation among JDKs can make those methods extremely resource intensive and perform poorly due to + * massive memory allocation and copying. See + * + * @param buffer + * the buffer to perform the check on + * @param suffix + * the suffix + * @return <code>true</code> if the character sequence represented by the argument is a suffix of the character + * sequence represented by the StringBuffer object; <code>false</code> otherwise. Note that the result will + * be <code>true</code> if the argument is the empty string. + */ + public static boolean checkStringBufferEndsWith(StringBuffer buffer, String suffix) { + if (suffix.length() > buffer.length()) { + return false; + } + // this loop is done on purpose to avoid memory allocation performance + // problems on various JDKs + // StringBuffer.lastIndexOf() was introduced in jdk 1.4 and + // implementation is ok though does allocation/copying + // StringBuffer.toString().endsWith() does massive memory + // allocation/copying on JDK 1.5 + // See http://issues.apache.org/bugzilla/show_bug.cgi?id=37169 + int endIndex = suffix.length() - 1; + int bufferIndex = buffer.length() - 1; + while (endIndex >= 0) { + if (buffer.charAt(bufferIndex) != suffix.charAt(endIndex)) { + return false; + } + bufferIndex--; + endIndex--; + } + return true; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/airavata/blob/e36c145d/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/db/utils/JPAUtils.java ---------------------------------------------------------------------- diff --git a/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/db/utils/JPAUtils.java b/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/db/utils/JPAUtils.java new file mode 100644 index 0000000..f879c15 --- /dev/null +++ b/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/db/utils/JPAUtils.java @@ -0,0 +1,230 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.airavata.sharing.registry.db.utils; + +import org.apache.airavata.common.exception.ApplicationSettingsException; +import org.apache.airavata.common.utils.ServerSettings; +import org.apache.airavata.sharing.registry.models.SharingRegistryException; +import org.apache.derby.drda.NetworkServerControl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.persistence.*; +import java.io.IOException; +import java.net.InetAddress; +import java.net.URI; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +public class JPAUtils { + private final static Logger logger = LoggerFactory.getLogger(JPAUtils.class); + + public static final String PERSISTENCE_UNIT_NAME = "airavata-sharing-registry"; + public static final String SHARING_REG_JDBC_DRIVER = "sharingcatalog.jdbc.driver"; + public static final String SHARING_REG_JDBC_URL = "sharingcatalog.jdbc.url"; + public static final String SHARING_REG_JDBC_USER = "sharingcatalog.jdbc.user"; + public static final String SHARING_REG_JDBC_PWD = "sharingcatalog.jdbc.password"; + public static final String SHARING_REG_VALIDATION_QUERY = "sharingcatalog.validationQuery"; + public static final String JPA_CACHE_SIZE = "jpa.cache.size"; + public static final String JPA_CACHE_ENABLED = "cache.enable"; + + public static final String CONFIGURATION = "CONFIGURATION"; + public static final String START_DERBY_ENABLE = "start.derby.server.mode"; + public static final String DERBY_SERVER_MODE_SYS_PROPERTY = "derby.drda.startNetworkServer"; + private static NetworkServerControl server; + private static JdbcStorage db; + private static String jdbcURl; + private static String jdbcDriver; + private static String jdbcUser; + private static String jdbcPassword; + + + @PersistenceUnit(unitName = PERSISTENCE_UNIT_NAME) + protected static EntityManagerFactory factory; + @PersistenceContext(unitName = PERSISTENCE_UNIT_NAME) + private static EntityManager entityManager; + + public static EntityManager getEntityManager() throws SharingRegistryException { + if (factory == null) { + String connectionProperties = "DriverClassName=" + readServerProperties(SHARING_REG_JDBC_DRIVER) + "," + + "Url=" + readServerProperties(SHARING_REG_JDBC_URL) + "?autoReconnect=true," + + "Username=" + readServerProperties(SHARING_REG_JDBC_USER) + "," + + "Password=" + readServerProperties(SHARING_REG_JDBC_PWD) + + ",validationQuery=" + readServerProperties(SHARING_REG_VALIDATION_QUERY); +// + +// String connectionProperties = "DriverClassName=com.mysql.jdbc.Driver," + +// "Url=jdbc:mysql://localhost:3306/airavata_sharing_catalog?autoReconnect=true," + +// "Username=root," + +// "Password=," + +// ",validationQuery=SELECT 1 FROM CONFIGURATION"; + + Map<String, String> properties = new HashMap<String, String>(); + properties.put("openjpa.ConnectionDriverName", "org.apache.commons.dbcp.BasicDataSource"); + properties.put("openjpa.ConnectionProperties", connectionProperties); + properties.put("openjpa.DynamicEnhancementAgent", "true"); + properties.put("openjpa.RuntimeUnenhancedClasses", "unsupported"); + // For app catalog, we don't need caching +// properties.put("openjpa.DataCache","" + readServerProperties(JPA_CACHE_ENABLED) + "(CacheSize=" + Integer.valueOf(readServerProperties(JPA_CACHE_SIZE)) + ", SoftReferenceSize=0)"); +// properties.put("openjpa.QueryCache","" + readServerProperties(JPA_CACHE_ENABLED) + "(CacheSize=" + Integer.valueOf(readServerProperties(JPA_CACHE_SIZE)) + ", SoftReferenceSize=0)"); + properties.put("openjpa.RemoteCommitProvider", "sjvm"); + properties.put("openjpa.Log", "DefaultLevel=INFO, Runtime=INFO, Tool=INFO, SQL=INFO"); + properties.put("openjpa.jdbc.SynchronizeMappings", "buildSchema(ForeignKeys=true)"); + properties.put("openjpa.jdbc.QuerySQLCache", "false"); + properties.put("openjpa.ConnectionFactoryProperties", "PrettyPrint=true, PrettyPrintLineLength=72," + + " PrintParameters=true, MaxActive=10, MaxIdle=5, MinIdle=2, MaxWait=31536000, autoReconnect=true"); + properties.put("openjpa.RuntimeUnenhancedClasses", "warn"); + factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME, properties); + } + entityManager = factory.createEntityManager(); + return entityManager; + } + + public static <R> R execute(Committer<EntityManager, R> committer) throws SharingRegistryException { + EntityManager entityManager = JPAUtils.getEntityManager(); + try { + entityManager.getTransaction().begin(); + R r = committer.commit(entityManager); + entityManager.getTransaction().commit(); + return r; + }finally { + if (entityManager != null && entityManager.isOpen()) { + if (entityManager.getTransaction().isActive()) { + entityManager.getTransaction().rollback(); + } + entityManager.close(); + } + } + } + + public static void initializeDB() throws SharingRegistryException { + jdbcDriver = readServerProperties(SHARING_REG_JDBC_DRIVER); + jdbcURl = readServerProperties(SHARING_REG_JDBC_URL); + jdbcUser = readServerProperties(SHARING_REG_JDBC_USER); + jdbcPassword = readServerProperties(SHARING_REG_JDBC_PWD); + jdbcURl = jdbcURl + "?" + "user=" + jdbcUser + "&" + "password=" + jdbcPassword; + + if (getDBType(jdbcURl).equals("derby") && isDerbyStartEnabled()) { + startDerbyInServerMode(); + } + db = new JdbcStorage(10, 50, jdbcURl, jdbcDriver, true); + + Connection conn = null; + try { + conn = db.connect(); + if (!DatabaseCreator.isDatabaseStructureCreated(CONFIGURATION, conn)) { + DatabaseCreator.createRegistryDatabase("database_scripts/sharing-registry", conn); + logger.info("New Database created for Sharing Catalog !!! "); + } else { + logger.info("Database already created for Sharing Catalog !!!"); + } + } catch (Exception e) { + logger.error(e.getMessage(), e); + throw new RuntimeException("Database failure", e); + } finally { + db.closeConnection(conn); + try { + if(conn != null){ + if (!conn.getAutoCommit()) { + conn.commit(); + } + conn.close(); + } + } catch (SQLException e) { + logger.error("Error while closing database connection...", e.getMessage(), e); + } + } + } + + public static String getDBType(String jdbcUrl){ + try{ + String cleanURI = jdbcUrl.substring(5); + URI uri = URI.create(cleanURI); + return uri.getScheme(); + } catch (Exception e) { + logger.error(e.getMessage(), e); + return null; + } + } + + public static boolean isDerbyStartEnabled(){ + try { + String s = ServerSettings.getSetting(START_DERBY_ENABLE); + if("true".equals(s)){ + return true; + } + } catch (ApplicationSettingsException e) { + logger.error("Unable to read airavata server properties", e.getMessage(), e); + return false; + } + return false; + } + + public static void startDerbyInServerMode() { + try { + System.setProperty(DERBY_SERVER_MODE_SYS_PROPERTY, "true"); + server = new NetworkServerControl(InetAddress.getByName("0.0.0.0"), + getPort(jdbcURl), + jdbcUser, jdbcPassword); + java.io.PrintWriter consoleWriter = new java.io.PrintWriter(System.out, true); + server.start(consoleWriter); + } catch (IOException e) { + logger.error("Unable to start Apache derby in the server mode! Check whether " + + "specified port is available"); + } catch (Exception e) { + logger.error("Unable to start Apache derby in the server mode! Check whether " + + "specified port is available"); + } + } + + public static void stopDerbyInServerMode() { + System.setProperty(DERBY_SERVER_MODE_SYS_PROPERTY, "false"); + if (server!=null){ + try { + server.shutdown(); + } catch (Exception e) { + logger.error("Error when stopping the derby server : "+e.getLocalizedMessage()); + } + } + } + + public static int getPort(String jdbcURL){ + try{ + String cleanURI = jdbcURL.substring(5); + URI uri = URI.create(cleanURI); + return uri.getPort(); + } catch (Exception e) { + logger.error(e.getMessage(), e); + return -1; + } + } + + public static String readServerProperties(String propertyName) throws SharingRegistryException { + try { + return ServerSettings.getSetting(propertyName); + } catch (ApplicationSettingsException e) { + logger.error("Unable to read airavata-server.properties...", e); + throw new SharingRegistryException("Unable to read airavata-server.properties..."); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/airavata/blob/e36c145d/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/db/utils/JdbcStorage.java ---------------------------------------------------------------------- diff --git a/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/db/utils/JdbcStorage.java b/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/db/utils/JdbcStorage.java new file mode 100644 index 0000000..377c50b --- /dev/null +++ b/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/db/utils/JdbcStorage.java @@ -0,0 +1,175 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.airavata.sharing.registry.db.utils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.*; + +public class JdbcStorage { + private static Logger log = LoggerFactory.getLogger(JdbcStorage.class); + + private ConnectionPool connectionPool; + + public JdbcStorage(String jdbcUrl, String jdbcDriver) { + // default init connection and max connection + this(3, 50, jdbcUrl, jdbcDriver, true); + } + + public JdbcStorage(int initCon, int maxCon, String url, String driver, boolean enableTransactions) { + try { + if (enableTransactions) { + connectionPool = new ConnectionPool(driver, url, initCon, maxCon, true, false, + Connection.TRANSACTION_SERIALIZABLE); + } else { + connectionPool = new ConnectionPool(driver, url, initCon, maxCon, true); + } + } catch (Exception e) { + throw new RuntimeException("Failed to create database connection pool.", e); + } + } + + /** + * Check if this connection pool is auto commit or not + * + * @return + */ + public boolean isAutoCommit() { + return connectionPool.isAutoCommit(); + } + + public void commit(Connection conn) { + try { + if (conn != null && !conn.getAutoCommit()) { + conn.commit(); + } + } catch (SQLException sqle) { + log.error("Cannot commit data", sqle); + } + } + + public void commitAndFree(Connection conn) { + commit(conn); + closeConnection(conn); + } + + public void rollback(Connection conn) { + try { + if (conn != null && !conn.getAutoCommit()) { + conn.rollback(); + } + } catch (SQLException sqle) { + log.error("Cannot Rollback data", sqle); + } + } + + public void rollbackAndFree(Connection conn) { + rollback(conn); + closeConnection(conn); + } + + public Connection connect() { + + Connection conn = null; + try { + conn = connectionPool.getConnection(); + } catch (SQLException e) { + log.error(e.getMessage(), e); + } + return conn; + } + + /** + * This method is provided so that you can have better control over the statement. For example: You can use + * stmt.setString to convert quotation mark automatically in an UPDATE statement + * + * NOTE: Statement is closed after execution + */ + public int executeUpdateAndClose(PreparedStatement stmt) throws SQLException { + int rows = 0; + try { + rows = stmt.executeUpdate(); + if (rows == 0) { + log.info("Problem: 0 rows affected by insert/update/delete statement."); + } + } finally { + stmt.close(); + } + return rows; + } + + public int countRow(String tableName, String columnName) throws SQLException { + String query = new String("SELECT COUNT(" + columnName + ") FROM " + tableName); + int count = -1; + Connection conn = null; + PreparedStatement stmt = null; + try { + conn = connectionPool.getConnection(); + stmt = conn.prepareStatement(query); + ResultSet rs = stmt.executeQuery(); + rs.next(); + count = rs.getInt(1); + commit(conn); + } catch (SQLException sql) { + rollback(conn); + throw sql; + } finally { + try { + if (stmt != null && !stmt.isClosed()) { + stmt.close(); + } + } finally { + closeConnection(conn); + } + } + return count; + } + + public void quietlyClose(Connection conn, Statement... stmts) { + if (stmts != null) { + for (Statement stmt : stmts) { + try { + if (stmt != null && !stmt.isClosed()) { + stmt.close(); + } + } catch (SQLException sql) { + log.error(sql.getMessage(), sql); + } + } + } + closeConnection(conn); + } + + public void closeConnection(Connection conn) { + if (conn != null) { + connectionPool.free(conn); + } + } + + public void closeAllConnections() { + if (connectionPool != null) + connectionPool.dispose(); + } + + public void shutdown() throws SQLException { + connectionPool.shutdown(); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/airavata/blob/e36c145d/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/db/utils/ObjectMapperSingleton.java ---------------------------------------------------------------------- diff --git a/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/db/utils/ObjectMapperSingleton.java b/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/db/utils/ObjectMapperSingleton.java new file mode 100644 index 0000000..de6bea9 --- /dev/null +++ b/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/db/utils/ObjectMapperSingleton.java @@ -0,0 +1,39 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.airavata.sharing.registry.db.utils; + +import org.dozer.DozerBeanMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ObjectMapperSingleton extends DozerBeanMapper{ + private final static Logger logger = LoggerFactory.getLogger(ObjectMapperSingleton.class); + + private static ObjectMapperSingleton instance; + + private ObjectMapperSingleton(){} + + public static ObjectMapperSingleton getInstance(){ + if(instance == null) + instance = new ObjectMapperSingleton(); + return instance; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/airavata/blob/e36c145d/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/server/SharingRegistryServer.java ---------------------------------------------------------------------- diff --git a/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/server/SharingRegistryServer.java b/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/server/SharingRegistryServer.java new file mode 100644 index 0000000..96fdb90 --- /dev/null +++ b/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/server/SharingRegistryServer.java @@ -0,0 +1,28 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.airavata.sharing.registry.server; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SharingRegistryServer { + private final static Logger logger = LoggerFactory.getLogger(SharingRegistryServer.class); +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/airavata/blob/e36c145d/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/server/SharingRegistryServerHandler.java ---------------------------------------------------------------------- diff --git a/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/server/SharingRegistryServerHandler.java b/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/server/SharingRegistryServerHandler.java new file mode 100644 index 0000000..7f2eb32 --- /dev/null +++ b/modules/sharing-registry/sharing-registry-server/src/main/java/org/apache/airavata/sharing/registry/server/SharingRegistryServerHandler.java @@ -0,0 +1,613 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.airavata.sharing.registry.server; + +import org.apache.airavata.common.exception.ApplicationSettingsException; +import org.apache.airavata.sharing.registry.db.entities.GroupMembershipEntityPK; +import org.apache.airavata.sharing.registry.db.entities.SharingEntityPK; +import org.apache.airavata.sharing.registry.db.repositories.*; +import org.apache.airavata.sharing.registry.db.utils.DBConstants; +import org.apache.airavata.sharing.registry.db.utils.JPAUtils; +import org.apache.airavata.sharing.registry.models.*; +import org.apache.airavata.sharing.registry.service.cpi.GovRegistryService; +import org.apache.thrift.TException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Field; +import java.util.*; + +public class SharingRegistryServerHandler implements GovRegistryService.Iface{ + private final static Logger logger = LoggerFactory.getLogger(SharingRegistryServerHandler.class); + + public static String GLOBAL_PERMISSION_NAME = "OWNER"; + + private DomainRepository domainRepository; + private UserRepository userRepository; + private UserGroupRepository userGroupRepository; + private GroupMembershipRepository groupMembershipRepository; + private EntityTypeRepository entityTypeRepository; + private PermissionTypeRepository permissionTypeRepository; + private EntityRepository entityRepository; + private SharingRepository sharingRepository; + + public SharingRegistryServerHandler() throws ApplicationSettingsException, TException { + JPAUtils.initializeDB(); + + this.domainRepository = new DomainRepository(); + this.userRepository = new UserRepository(); + this.userGroupRepository = new UserGroupRepository(); + this.groupMembershipRepository = new GroupMembershipRepository(); + this.entityTypeRepository = new EntityTypeRepository(); + this.permissionTypeRepository = new PermissionTypeRepository(); + this.entityRepository = new EntityRepository(); + this.sharingRepository = new SharingRepository(); + } + + /** + * * Domain Operations + * * + */ + @Override + public String createDomain(Domain domain) throws SharingRegistryException, TException { + if(domainRepository.get(domain.domainId) != null) + throw new SharingRegistryException("There exist domain with given domain id"); + + domain.setCreatedTime(System.currentTimeMillis()); + domain.setUpdatedTime(System.currentTimeMillis()); + domainRepository.create(domain); + + //create the global permission for the domain + PermissionType permissionType = new PermissionType(); + permissionType.setPermissionTypeId(domain.domainId+":"+GLOBAL_PERMISSION_NAME); + permissionType.setDomainId(domain.domainId); + permissionType.setName(GLOBAL_PERMISSION_NAME); + permissionType.setDescription("GLOBAL permission to " + domain.domainId); + permissionType.setCreatedTime(System.currentTimeMillis()); + permissionType.setUpdatedTime(System.currentTimeMillis()); + permissionTypeRepository.create(permissionType); + + return domain.domainId; + } + + @Override + public boolean updateDomain(Domain domain) throws SharingRegistryException, TException { + Domain oldDomain = domainRepository.get(domain.domainId); + domain.setCreatedTime(oldDomain.createdTime); + domain.setUpdatedTime(System.currentTimeMillis()); + domain = getUpdatedObject(oldDomain, domain); + domainRepository.update(domain); + return true; + } + + @Override + public boolean deleteDomain(String domainId) throws SharingRegistryException, TException { + domainRepository.delete(domainId); + return true; + } + + @Override + public Domain getDomain(String domainId) throws SharingRegistryException, TException { + return domainRepository.get(domainId); + } + + @Override + public List<Domain> getDomains(int offset, int limit) throws TException { + return domainRepository.select(new HashMap<>(), offset, limit); + } + + /** + * * User Operations + * * + */ + @Override + public String createUser(User user) throws SharingRegistryException, TException { + if(userRepository.get(user.userId) != null) + throw new SharingRegistryException("There exist user with given user id"); + + user.setCreatedTime(System.currentTimeMillis()); + user.setUpdatedTime(System.currentTimeMillis()); + userRepository.create(user); + + UserGroup userGroup = new UserGroup(); + userGroup.setGroupId(user.userId); + userGroup.setDomainId(user.domainId); + userGroup.setName(user.userName); + userGroup.setDescription("user " + user.userName + " group"); + userGroup.setOwnerId(user.userId); + userGroup.setGroupType(GroupType.SINGLE_USER); + createGroup(userGroup); + + return user.userId; + } + + @Override + public boolean updatedUser(User user) throws SharingRegistryException, TException { + User oldUser = userRepository.get(user.userId); + user.setCreatedTime(oldUser.createdTime); + user.setUpdatedTime(System.currentTimeMillis()); + user = getUpdatedObject(oldUser, user); + userRepository.update(user); + + UserGroup userGroup = userGroupRepository.get(user.userId); + userGroup.setName(user.userName); + userGroup.setDescription("user " + user.userName + " group"); + updateGroup(userGroup); + return true; + } + + @Override + public boolean deleteUser(String userId) throws SharingRegistryException, TException { + userRepository.delete(userId); + userGroupRepository.delete(userId); + return true; + } + + @Override + public User getUser(String userId) throws SharingRegistryException, TException { + return userRepository.get(userId); + } + + @Override + public List<User> getUsers(String domain, int offset, int limit) throws SharingRegistryException, TException { + HashMap<String, String> filters = new HashMap<>(); + filters.put(DBConstants.UserTable.DOMAIN_ID, domain); + return userRepository.select(filters, offset, limit); + } + + /** + * * Group Operations + * * + */ + @Override + public String createGroup(UserGroup group) throws SharingRegistryException, TException { + if(userGroupRepository.get(group.groupId) != null) + throw new SharingRegistryException("There exist group with given group id"); + + group.setCreatedTime(System.currentTimeMillis()); + group.setUpdatedTime(System.currentTimeMillis()); + userGroupRepository.create(group); + return group.groupId; + } + + @Override + public boolean updateGroup(UserGroup group) throws SharingRegistryException, TException { + group.setUpdatedTime(System.currentTimeMillis()); + UserGroup oldGroup = userGroupRepository.get(group.groupId); + group.setCreatedTime(oldGroup.createdTime); + group = getUpdatedObject(oldGroup, group); + userGroupRepository.update(group); + return true; + } + + @Override + public boolean deleteGroup(String groupId) throws SharingRegistryException, TException { + userGroupRepository.delete(groupId); + return true; + } + + @Override + public UserGroup getGroup(String groupId) throws SharingRegistryException, TException { + return userGroupRepository.get(groupId); + } + + @Override + public List<UserGroup> getGroups(String domain, int offset, int limit) throws TException { + HashMap<String, String> filters = new HashMap<>(); + filters.put(DBConstants.UserTable.DOMAIN_ID, domain); + return userGroupRepository.select(filters, offset, limit); + } + + @Override + public boolean addUsersToGroup(List<String> userIds, String groupId) throws SharingRegistryException, TException { + for(int i=0; i < userIds.size(); i++){ + GroupMembership groupMembership = new GroupMembership(); + groupMembership.setParentId(groupId); + groupMembership.setChildId(userIds.get(i)); + groupMembership.setChildType(GroupChildType.USER); + groupMembership.setCreatedTime(System.currentTimeMillis()); + groupMembership.setUpdatedTime(System.currentTimeMillis()); + groupMembershipRepository.create(groupMembership); + } + return true; + } + + @Override + public boolean removeUsersFromGroup(List<String> userIds, String groupId) throws SharingRegistryException, TException { + for(int i=0; i < userIds.size(); i++){ + GroupMembershipEntityPK groupMembershipEntityPK = new GroupMembershipEntityPK(); + groupMembershipEntityPK.setParentId(groupId); + groupMembershipEntityPK.setChildId(userIds.get(i)); + groupMembershipRepository.delete(groupMembershipEntityPK); + } + return true; + } + + @Override + public Map<String, GroupChildType> getGroupMembers(String groupId, int offset, int limit) throws SharingRegistryException, TException { + HashMap<String, GroupChildType> groupMembers = new HashMap<>(); + HashMap<String, String> filters = new HashMap<>(); + filters.put(DBConstants.GroupMembershipTable.PARENT_ID, groupId); + List<GroupMembership> groupMembershipList = groupMembershipRepository.select(filters, 0, -1); + groupMembershipList.stream().forEach(gm->{groupMembers.put(gm.getChildId(), gm.getChildType());}); + return groupMembers; + } + + @Override + public boolean addChildGroupToParentGroup(String childId, String groupId) throws SharingRegistryException, TException { + //Todo check for cyclic dependencies + GroupMembership groupMembership = new GroupMembership(); + groupMembership.setParentId(groupId); + groupMembership.setChildId(childId); + groupMembership.setChildType(GroupChildType.GROUP); + groupMembership.setCreatedTime(System.currentTimeMillis()); + groupMembership.setUpdatedTime(System.currentTimeMillis()); + groupMembershipRepository.create(groupMembership); + return true; + } + + @Override + public boolean removeChildGroupFromParentGroup(String childId, String groupId) throws SharingRegistryException, TException { + GroupMembershipEntityPK groupMembershipEntityPK = new GroupMembershipEntityPK(); + groupMembershipEntityPK.setParentId(groupId); + groupMembershipEntityPK.setChildId(childId); + groupMembershipRepository.delete(groupMembershipEntityPK); + return true; + } + + /** + * * EntityType Operations + * * + */ + @Override + public String createEntityType(EntityType entityType) throws SharingRegistryException, TException { + if(entityTypeRepository.get(entityType.entityTypeId) != null) + throw new SharingRegistryException("There exist EntityType with given EntityType id"); + + entityType.setCreatedTime(System.currentTimeMillis()); + entityType.setUpdatedTime(System.currentTimeMillis()); + entityTypeRepository.create(entityType); + return entityType.entityTypeId; + } + + @Override + public boolean updateEntityType(EntityType entityType) throws SharingRegistryException, TException { + entityType.setUpdatedTime(System.currentTimeMillis()); + EntityType oldEntityType = entityTypeRepository.get(entityType.entityTypeId); + entityType.setCreatedTime(oldEntityType.createdTime); + entityType = getUpdatedObject(oldEntityType, entityType); + entityTypeRepository.update(entityType); + return true; + } + + @Override + public boolean deleteEntityType(String entityTypeId) throws SharingRegistryException, TException { + entityTypeRepository.delete(entityTypeId); + return true; + } + + @Override + public EntityType getEntityType(String entityTypeId) throws SharingRegistryException, TException { + return entityTypeRepository.get(entityTypeId); + } + + @Override + public List<EntityType> getEntityTypes(String domain, int offset, int limit) throws TException { + HashMap<String, String> filters = new HashMap<>(); + filters.put(DBConstants.EntityTypeTable.DOMAIN_ID, domain); + return entityTypeRepository.select(domain, offset, limit); + } + + /** + * * Permission Operations + * * + */ + @Override + public String createPermissionType(PermissionType permissionType) throws SharingRegistryException, TException { + if(permissionTypeRepository.get(permissionType.permissionTypeId) != null) + throw new SharingRegistryException("There exist PermissionType with given PermissionType id"); + permissionType.setCreatedTime(System.currentTimeMillis()); + permissionType.setUpdatedTime(System.currentTimeMillis()); + permissionTypeRepository.create(permissionType); + return permissionType.permissionTypeId; + } + + @Override + public boolean updatePermissionType(PermissionType permissionType) throws SharingRegistryException, TException { + permissionType.setUpdatedTime(System.currentTimeMillis()); + PermissionType oldPermissionType = permissionTypeRepository.get(permissionType.permissionTypeId); + permissionType = getUpdatedObject(oldPermissionType, permissionType); + permissionTypeRepository.update(permissionType); + return true; + } + + @Override + public boolean deletePermissionType(String entityTypeId) throws SharingRegistryException, TException { + permissionTypeRepository.delete(entityTypeId); + return true; + } + + @Override + public PermissionType getPermissionType(String permissionTypeId) throws SharingRegistryException, TException { + return permissionTypeRepository.get(permissionTypeId); + } + + @Override + public List<PermissionType> getPermissionTypes(String domain, int offset, int limit) throws SharingRegistryException, TException { + HashMap<String, String> filters = new HashMap<>(); + filters.put(DBConstants.PermissionTypeTable.DOMAIN_ID, domain); + return permissionTypeRepository.select(filters, offset, limit); + } + + /** + * * Entity Operations + * * + */ + @Override + public String createEntity(Entity entity) throws SharingRegistryException, TException { + if(entityRepository.get(entity.entityId) != null) + throw new SharingRegistryException("There exist Entity with given Entity id"); + + if(!userRepository.isExists(entity.getOwnerId())){ + User user = new User(); + user.setUserId(entity.getOwnerId()); + user.setDomainId(entity.domainId); + user.setUserName(user.userId.split("@")[0]); + + createUser(user); + } + + entity.setCreatedTime(System.currentTimeMillis()); + entity.setUpdatedTime(System.currentTimeMillis()); + entityRepository.create(entity); + + //Assigning global permission for the owner + Sharing newSharing = new Sharing(); + newSharing.setPermissionTypeId(permissionTypeRepository.getGlobalPermissionTypeIdForDomain(entity.domainId)); + newSharing.setEntityId(entity.entityId); + newSharing.setGroupId(entity.ownerId); + newSharing.setSharingType(SharingType.DIRECT_CASCADING); + newSharing.setInheritedParentId(entity.entityId); + newSharing.setCreatedTime(System.currentTimeMillis()); + newSharing.setUpdatedTime(System.currentTimeMillis()); + + sharingRepository.create(newSharing); + + //creating records for inherited permissions + if(entity.getParentEntityId() != null && entity.getParentEntityId() != ""){ + List<Sharing> sharings = sharingRepository.getCascadingPermissionsForEntity(entity.parentEntityId); + for(Sharing sharing : sharings){ + newSharing = new Sharing(); + newSharing.setPermissionTypeId(sharing.permissionTypeId); + newSharing.setEntityId(entity.entityId); + newSharing.setGroupId(sharing.groupId); + newSharing.setInheritedParentId(sharing.inheritedParentId); + newSharing.setSharingType(SharingType.INDIRECT_CASCADING); + newSharing.setCreatedTime(System.currentTimeMillis()); + newSharing.setUpdatedTime(System.currentTimeMillis()); + + sharingRepository.create(newSharing); + } + } + + return entity.entityId; + } + + @Override + public boolean updateEntity(Entity entity) throws SharingRegistryException, TException { + //TODO Check for permission changes + entity.setUpdatedTime(System.currentTimeMillis()); + Entity oldEntity = entityRepository.get(entity.getEntityId()); + entity.setCreatedTime(oldEntity.createdTime); + entity = getUpdatedObject(oldEntity, entity); + entityRepository.update(entity); + return true; + } + + @Override + public boolean deleteEntity(String entityId) throws SharingRegistryException, TException { + //TODO Check for permission changes + entityRepository.delete(entityId); + return true; + } + + @Override + public Entity getEntity(String entityId) throws SharingRegistryException, TException { + return entityRepository.get(entityId); + } + + @Override + public List<Entity> searchEntities(String userId, String entityTypeId, List<SearchCriteria> filters, + int offset, int limit) throws SharingRegistryException, TException { + List<String> groupIds = new ArrayList<>(); + groupIds.add(userId); + groupMembershipRepository.getAllParentMembershipsForChild(userId).stream().forEach(gm->groupIds.add(gm.parentId)); + return entityRepository.searchEntities(groupIds, entityTypeId, filters, offset, limit); + } + + @Override + public List<User> getListOfSharedUsers(String entityId, String permissionTypeId) throws SharingRegistryException, TException { + return userRepository.getAccessibleUsers(entityId, permissionTypeId); + } + + @Override + public List<UserGroup> getListOfSharedGroups(String entityId, String permissionTypeId) throws SharingRegistryException, TException { + return userGroupRepository.getAccessibleGroups(entityId, permissionTypeId); + } + + /** + * * Sharing Entity with Users and Groups + * * + * + * @param entityId + * @param userList + * @param permissionType + */ + @Override + public boolean shareEntityWithUsers(String entityId, List<String> userList, String permissionTypeId, boolean cascadePermission) throws SharingRegistryException, TException { + return shareEntity(entityId, userList, permissionTypeId, GroupType.SINGLE_USER, cascadePermission); + } + + @Override + public boolean shareEntityWithGroups(String entityId, List<String> groupList, String permissionTypeId, boolean cascadePermission) throws SharingRegistryException, TException { + return shareEntity(entityId, groupList, permissionTypeId, GroupType.MULTI_USER, cascadePermission); + } + + private boolean shareEntity(String entityId, List<String> groupOrUserList, String permissionTypeId, GroupType groupType, boolean cascadePermission) throws SharingRegistryException, TException { + //Adding permission for the specified users/groups for the specified entity + LinkedList<Entity> temp = new LinkedList<>(); + for(String userId : groupOrUserList){ + Sharing sharing = new Sharing(); + sharing.setPermissionTypeId(permissionTypeId); + sharing.setEntityId(entityId); + sharing.setGroupId(userId); + sharing.setInheritedParentId(entityId); + if(cascadePermission) { + sharing.setSharingType(SharingType.DIRECT_CASCADING); + }else { + sharing.setSharingType(SharingType.DIRECT_NON_CASCADING); + } + sharing.setCreatedTime(System.currentTimeMillis()); + sharing.setUpdatedTime(System.currentTimeMillis()); + + sharingRepository.create(sharing); + } + + if(cascadePermission){ + //Adding permission for the specified users/groups for all child entities + entityRepository.getChildEntities(entityId).stream().forEach(e-> temp.addLast(e)); + while(temp.size() > 0){ + Entity entity = temp.pop(); + String childEntityId = entity.entityId; + for(String userId : groupOrUserList){ + Sharing sharing = new Sharing(); + sharing.setPermissionTypeId(permissionTypeId); + sharing.setEntityId(childEntityId); + sharing.setGroupId(userId); + sharing.setInheritedParentId(entityId); + sharing.setSharingType(SharingType.INDIRECT_CASCADING); + sharing.setInheritedParentId(entityId); + sharing.setCreatedTime(System.currentTimeMillis()); + sharing.setUpdatedTime(System.currentTimeMillis()); + sharingRepository.create(sharing); + entityRepository.getChildEntities(childEntityId).stream().forEach(e-> temp.addLast(e)); + } + } + } + return true; + } + + @Override + public boolean revokeEntitySharingFromUsers(String entityId, List<String> userList, String permissionTypeId) throws SharingRegistryException, TException { + return revokeEntitySharing(entityId, userList, permissionTypeId); + } + + + @Override + public boolean revokeEntitySharingFromGroups(String entityId, List<String> groupList, String permissionTypeId) throws SharingRegistryException, TException { + return revokeEntitySharing(entityId, groupList, permissionTypeId); + } + + @Override + public boolean userHasAccess(String domainId, String userId, String entityId, String permissionTypeId) throws SharingRegistryException, TException { + //check whether the user has permission directly or indirectly + List<GroupMembership> parentMemberships = groupMembershipRepository.getAllParentMembershipsForChild(userId); + List<String> groupIds = new ArrayList<>(); + parentMemberships.stream().forEach(pm->groupIds.add(pm.parentId)); + groupIds.add(userId); + return sharingRepository.hasAccess(entityId, groupIds, Arrays.asList(permissionTypeId, + permissionTypeRepository.getGlobalPermissionTypeIdForDomain(domainId))); + } + + public boolean revokeEntitySharing(String entityId, List<String> groupOrUserList, String permissionTypeId) throws SharingRegistryException { + //revoking permission for the entity + for(String groupId : groupOrUserList){ + SharingEntityPK sharingEntityPK = new SharingEntityPK(); + sharingEntityPK.setEntityId(entityId); + sharingEntityPK.setGroupId(groupId); + sharingEntityPK.setPermissionTypeId(permissionTypeId); + sharingEntityPK.setInheritedParentId(entityId); + + sharingRepository.delete(sharingEntityPK); + } + + //revoking permission from inheritance + List<Sharing> temp = new ArrayList<>(); + sharingRepository.getIndirectSharedChildren(entityId, permissionTypeId).stream().forEach(s->temp.add(s)); + for(Sharing sharing : temp){ + String childEntityId = sharing.entityId; + for(String groupId : groupOrUserList){ + SharingEntityPK sharingEntityPK = new SharingEntityPK(); + sharingEntityPK.setEntityId(childEntityId); + sharingEntityPK.setGroupId(groupId); + sharingEntityPK.setPermissionTypeId(permissionTypeId); + sharingEntityPK.setInheritedParentId(entityId); + + sharingRepository.delete(sharingEntityPK); + } + } + return true; + } + + + + private <T> T getUpdatedObject(T oldEntity, T newEntity) throws SharingRegistryException { + Field[] newEntityFields = newEntity.getClass().getDeclaredFields(); + Hashtable newHT = fieldsToHT(newEntityFields, newEntity); + + Class oldEntityClass = oldEntity.getClass(); + Field[] oldEntityFields = oldEntityClass.getDeclaredFields(); + + for (Field field : oldEntityFields){ + field.setAccessible(true); + Object o = newHT.get(field.getName()); + if (o != null){ + Field f = null; + try { + f = oldEntityClass.getDeclaredField(field.getName()); + f.setAccessible(true); + logger.debug("setting " + f.getName()); + f.set(oldEntity, o); + } catch (Exception e) { + throw new SharingRegistryException(e.getMessage()); + } + } + } + return oldEntity; + } + + private static Hashtable<String, Object> fieldsToHT(Field[] fields, Object obj){ + Hashtable<String,Object> hashtable = new Hashtable<>(); + for (Field field: fields){ + field.setAccessible(true); + try { + Object retrievedObject = field.get(obj); + if (retrievedObject != null){ + logger.debug("scanning " + field.getName()); + hashtable.put(field.getName(), field.get(obj)); + } + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + return hashtable; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/airavata/blob/e36c145d/modules/sharing-registry/sharing-registry-server/src/main/resources/META-INF/persistence.xml ---------------------------------------------------------------------- diff --git a/modules/sharing-registry/sharing-registry-server/src/main/resources/META-INF/persistence.xml b/modules/sharing-registry/sharing-registry-server/src/main/resources/META-INF/persistence.xml new file mode 100644 index 0000000..7b08528 --- /dev/null +++ b/modules/sharing-registry/sharing-registry-server/src/main/resources/META-INF/persistence.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0"> + + <persistence-unit name="airavata-sharing-registry"> + <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider> + <class>org.apache.airavata.sharing.registry.db.entities.DomainEntity</class> + <class>org.apache.airavata.sharing.registry.db.entities.EntityEntity</class> + <class>org.apache.airavata.sharing.registry.db.entities.EntityTypeEntity</class> + <class>org.apache.airavata.sharing.registry.db.entities.GroupMembershipEntity</class> + <class>org.apache.airavata.sharing.registry.db.entities.PermissionTypeEntity</class> + <class>org.apache.airavata.sharing.registry.db.entities.SharingEntity</class> + <class>org.apache.airavata.sharing.registry.db.entities.SharingUserEntity</class> + <class>org.apache.airavata.sharing.registry.db.entities.UserGroupEntity</class> + </persistence-unit> +</persistence>