From: Richard Millet Date: Mon, 10 Feb 2014 18:05:21 +0000 (-0800) Subject: PAHMA-963-REM-A: Adding code to look into Nuxeo's wrapped exceptions for network... X-Git-Url: https://git.aero2k.de/?a=commitdiff_plain;h=6f5b180cf0483e8bbd8321538dddd589fadf5aaa;p=tmp%2Fjakarta-migration.git PAHMA-963-REM-A: Adding code to look into Nuxeo's wrapped exceptions for network related errors. --- diff --git a/build.properties b/build.properties index 245a59236..f50b08ab6 100644 --- a/build.properties +++ b/build.properties @@ -123,6 +123,7 @@ db.cspace.user.password=${env.DB_PASSWORD_CSPACE} #db.host=dba-postgres-qa-21.ist.berkeley.edu #db.host=173.255.228.202 db.host=169.229.248.106 +#db.host=localhost #db.host=169.229.199.44 db.jdbc.baseurl=jdbc:${db}://${db.host}:${db.port} diff --git a/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CollectionSpaceJaxRsApplication.java b/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CollectionSpaceJaxRsApplication.java index 956c52802..f9a1c656f 100644 --- a/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CollectionSpaceJaxRsApplication.java +++ b/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CollectionSpaceJaxRsApplication.java @@ -51,8 +51,6 @@ import org.collectionspace.services.organization.OrgAuthorityResource; import org.collectionspace.services.person.PersonAuthorityResource; import org.collectionspace.services.citation.CitationAuthorityResource; -//import org.collectionspace.services.query.QueryResource; - import javax.servlet.ServletContext; import javax.ws.rs.core.Application; @@ -130,7 +128,6 @@ public class CollectionSpaceJaxRsApplication extends Application /* singletons.add(new WorkflowResource()); */ -// singletons.add(new QueryResource()); // singletons.add(new DomainIdentifierResource()); // singletons.add(new PingResource()); } diff --git a/services/JaxRsServiceProvider/src/main/resources/log4j.properties b/services/JaxRsServiceProvider/src/main/resources/log4j.properties index 508a64903..3677268c9 100644 --- a/services/JaxRsServiceProvider/src/main/resources/log4j.properties +++ b/services/JaxRsServiceProvider/src/main/resources/log4j.properties @@ -55,6 +55,8 @@ log4j.additivity.perf.collectionspace=false log4j.logger.org.collectionspace=INFO log4j.logger.org.collectionspace.services.nuxeo.client.java=TRACE log4j.logger.org.collectionspace.services.common.storage.JDBCTools=ERROR +log4j.logger.org.collectionspace.services.common.profile.CSpaceFilter=DEBUG + #log4j.logger.org.collectionspace.services.common.vocabulary.nuxeo=TRACE # diff --git a/services/JaxRsServiceProvider/src/main/webapp/WEB-INF/login.conf b/services/JaxRsServiceProvider/src/main/webapp/WEB-INF/login.conf index c8eddb09c..bf28e215e 100644 --- a/services/JaxRsServiceProvider/src/main/webapp/WEB-INF/login.conf +++ b/services/JaxRsServiceProvider/src/main/webapp/WEB-INF/login.conf @@ -8,9 +8,14 @@ CSpaceJBossDBLoginModule { rolesQuery="select r.rolename, 'Role' from roles as r, accounts_roles as ar where ar.user_id=? and ar.role_id=r.csid" tenantsQueryWithDisabled="select t.id, t.name, 'Tenants' from accounts_common as a, accounts_tenants as at, tenants as t where a.userid=? and a.csid = at.TENANTS_ACCOUNTSCOMMON_CSID and at.tenant_id = t.id" tenantsQueryNoDisabled="select t.id, t.name, 'Tenants' from accounts_common as a, accounts_tenants as at, tenants as t where a.userid=? and a.csid = at.TENANTS_ACCOUNTSCOMMON_CSID and at.tenant_id = t.id and NOT t.disabled" + maxRetrySeconds="5" + delayBetweenAttemptsMillis="200" debug=true; }; + /** + * The JAAS login configuration. + */ cspace { org.collectionspace.authentication.jaas.CSpaceJBossDBLoginModule required dsJndiName="CspaceDS" @@ -21,5 +26,7 @@ CSpaceJBossDBLoginModule { rolesQuery="select r.rolename, 'Role' from roles as r, accounts_roles as ar where ar.user_id=? and ar.role_id=r.csid" tenantsQueryWithDisabled="select t.id, t.name, 'Tenants' from accounts_common as a, accounts_tenants as at, tenants as t where a.userid=? and a.csid = at.TENANTS_ACCOUNTSCOMMON_CSID and at.tenant_id = t.id" tenantsQueryNoDisabled="select t.id, t.name, 'Tenants' from accounts_common as a, accounts_tenants as at, tenants as t where a.userid=? and a.csid = at.TENANTS_ACCOUNTSCOMMON_CSID and at.tenant_id = t.id and NOT t.disabled" + maxRetrySeconds="5" + delayBetweenAttemptsMillis="200" debug=true; }; diff --git a/services/JaxRsServiceProvider/src/main/webapp/WEB-INF/web.xml b/services/JaxRsServiceProvider/src/main/webapp/WEB-INF/web.xml index e633882ce..e329897dd 100644 --- a/services/JaxRsServiceProvider/src/main/webapp/WEB-INF/web.xml +++ b/services/JaxRsServiceProvider/src/main/webapp/WEB-INF/web.xml @@ -45,6 +45,9 @@ + springSecurityFilterChain org.springframework.web.filter.DelegatingFilterProxy @@ -55,6 +58,34 @@ /* + + + networkErrorRetryFilter + org.collectionspace.services.common.NetworkErrorRetryFilter + + maxRetrySeconds + 5 + + + delayBetweenAttemptsMillis + 200 + + + + + networkErrorRetryFilter + /* + + + CSpaceFilter org.collectionspace.services.common.profile.CSpaceFilter diff --git a/services/authentication/service/src/main/java/org/collectionspace/authentication/jaas/CSpaceJBossDBLoginModule.java b/services/authentication/service/src/main/java/org/collectionspace/authentication/jaas/CSpaceJBossDBLoginModule.java index 4a2dec8b3..bc585755b 100644 --- a/services/authentication/service/src/main/java/org/collectionspace/authentication/jaas/CSpaceJBossDBLoginModule.java +++ b/services/authentication/service/src/main/java/org/collectionspace/authentication/jaas/CSpaceJBossDBLoginModule.java @@ -27,15 +27,14 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; - import java.security.acl.Group; + import javax.security.auth.Subject; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.login.LoginException; import org.collectionspace.authentication.realm.db.CSpaceDbRealm; import org.jboss.security.auth.spi.UsernamePasswordLoginModule; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -68,10 +67,26 @@ public class CSpaceJBossDBLoginModule extends UsernamePasswordLoginModule { realm = new CSpaceDbRealm(options); } + @Override + protected String createPasswordHash(String username, String password, + String digestOption) + throws LoginException { + String result = super.createPasswordHash(username, password, digestOption); + + if (result == null) { + String message = "Could not create a password hash for the supplied password. Check your login.conf configuration's hash algorithm setting."; + log.error(message); + throw new LoginException(message); + } + + return result; + } + protected String getUsersPassword() throws LoginException { String username = getUsername(); String password = null; + try { password = realm.getUsersPassword(username); password = convertRawPassword(password); @@ -79,12 +94,15 @@ public class CSpaceJBossDBLoginModule extends UsernamePasswordLoginModule { logger.debug("Obtained user password for: " + username); } } catch (LoginException lex) { + log.error("Could not retrieve user password for: " + username, lex); throw lex; } catch (Exception ex) { + log.error("Could not retrieve user password for: " + username, ex); LoginException le = new LoginException("Unknown Exception"); le.initCause(ex); throw le; } + return password; } diff --git a/services/authentication/service/src/main/java/org/collectionspace/authentication/realm/db/CSpaceDbRealm.java b/services/authentication/service/src/main/java/org/collectionspace/authentication/realm/db/CSpaceDbRealm.java index aa67924f3..342ab2159 100644 --- a/services/authentication/service/src/main/java/org/collectionspace/authentication/realm/db/CSpaceDbRealm.java +++ b/services/authentication/service/src/main/java/org/collectionspace/authentication/realm/db/CSpaceDbRealm.java @@ -50,6 +50,7 @@ package org.collectionspace.authentication.realm.db; import java.lang.reflect.Constructor; +import java.net.ConnectException; import java.security.Principal; import java.security.acl.Group; import java.sql.Connection; @@ -70,10 +71,13 @@ import javax.sql.DataSource; //import org.apache.commons.logging.Log; //import org.apache.commons.logging.LogFactory; + + + + import org.collectionspace.authentication.AuthN; import org.collectionspace.authentication.CSpaceTenant; import org.collectionspace.authentication.realm.CSpaceRealm; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -82,8 +86,7 @@ import org.slf4j.LoggerFactory; * @author */ public class CSpaceDbRealm implements CSpaceRealm { - - private Logger logger = LoggerFactory.getLogger(CSpaceDbRealm.class); + private Logger logger = LoggerFactory.getLogger(CSpaceDbRealm.class); private String datasourceName; private String principalsQuery; @@ -92,6 +95,48 @@ public class CSpaceDbRealm implements CSpaceRealm { private String tenantsQueryWithDisabled; private boolean suspendResume; + private long maxRetrySeconds = MAX_RETRY_SECONDS; + private static final int MAX_RETRY_SECONDS = 5; + private static final String MAX_RETRY_SECONDS_STR = "maxRetrySeconds"; + + private long delayBetweenAttemptsMillis = DELAY_BETWEEN_ATTEMPTS_MILLISECONDS; + private static final String DELAY_BETWEEN_ATTEMPTS_MILLISECONDS_STR = "delayBetweenAttemptsMillis"; + private static final long DELAY_BETWEEN_ATTEMPTS_MILLISECONDS = 200; + + protected void setMaxRetrySeconds(Map options) { + Object optionsObj = options.get(MAX_RETRY_SECONDS_STR); + if (optionsObj != null) { + String paramValue = optionsObj.toString(); + try { + maxRetrySeconds = Long.parseLong(paramValue); + } catch (NumberFormatException e) { + logger.warn(String.format("The Spring Security login authentication parameter '%s' with value '%s' could not be parsed to a long value. The default value of '%d' will be used instead.", + MAX_RETRY_SECONDS_STR, paramValue, maxRetrySeconds)); + } + } + } + + protected long getMaxRetrySeconds() { + return this.maxRetrySeconds; + } + + protected void setDelayBetweenAttemptsMillis(Map options) { + Object optionsObj = options.get(DELAY_BETWEEN_ATTEMPTS_MILLISECONDS_STR); + if (optionsObj != null) { + String paramValue = optionsObj.toString(); + try { + delayBetweenAttemptsMillis = Long.parseLong(paramValue); + } catch (NumberFormatException e) { + logger.warn(String.format("The Spring Security login authentication parameter '%s' with value '%s' could not be parsed to a long value. The default value of '%d' will be used instead.", + MAX_RETRY_SECONDS_STR, paramValue, delayBetweenAttemptsMillis)); + } + } + } + + protected long getDelayBetweenAttemptsMillis() { + return this.delayBetweenAttemptsMillis; + } + /** * CSpace Database Realm * @param datasourceName datasource name @@ -121,6 +166,10 @@ public class CSpaceDbRealm implements CSpaceRealm { if (tmp != null) { suspendResume = Boolean.valueOf(tmp.toString()).booleanValue(); } + + this.setMaxRetrySeconds(options); + this.setDelayBetweenAttemptsMillis(options); + if (logger.isTraceEnabled()) { logger.trace("DatabaseServerLoginModule, dsJndiName=" + datasourceName); logger.trace("principalsQuery=" + principalsQuery); @@ -475,11 +524,71 @@ public class CSpaceDbRealm implements CSpaceRealm { return p; } - private Connection getConnection() throws LoginException, SQLException { + /* + * This method will attempt to get a connection. If a network error prevents it from getting a connection on the first try + * it will retry for the next 'getMaxRetrySeconds()' seconds. If it is unable to get the connection then it will timeout and + * throw an exception. + */ + private Connection getConnection() throws Exception { + Connection result = null; + boolean failed = true; + Exception lastException = null; + int requestAttempts = 0; + + long quittingTime = System.currentTimeMillis() + getMaxRetrySeconds() * 1000; // This is how long we attempt retries + do { + if (requestAttempts > 0) { + Thread.sleep(getDelayBetweenAttemptsMillis()); // Wait a little time between reattempts. + } + + try { + // proceed to the original request by calling doFilter() + result = this.getConnection(getDataSourceName()); + if (result != null) { + failed = false; + break; // the request was successfully executed, so we can break out of this retry loop + } else { + failed = true; + throw new ConnectException(); // The 'response' argument indicated a network related failure, so let's throw a generic connection exception + } + } catch (Exception e) { + lastException = e; + if (exceptionChainContainsNetworkError(lastException) == false) { + // Break if the exception chain does not contain a + // network related exception because we don't want to retry if it's not a network related failure + break; + } + requestAttempts++; // keep track of how many times we've tried the request + } + } while (System.currentTimeMillis() < quittingTime); // keep trying until we run out of time + + // + // Add a warning to the logs if we encountered *any* failures on our re-attempts. Only add the warning + // if we were eventually successful. + // + if (requestAttempts > 0 && failed == false) { + logger.warn(String.format("Request to get a connection from data source '%s' failed with exception '%s' at attempt number '%d' before finally succeeding on the next attempt.", + getDataSourceName(), + lastException.getClass().getName(), + requestAttempts)); + } + + if (failed == true) { + // If we get here, it means all of our attempts to get a successful call to chain.doFilter() have failed. + throw lastException; + } + + return result; + } + + /* + * Don't call this method directly. Instead, use the getConnection() method that take no arguments. + */ + private Connection getConnection(String dataSourceName) throws LoginException, SQLException { InitialContext ctx = null; Connection conn = null; - String dataSourceName = getDataSourceName(); DataSource ds = null; + try { ctx = new InitialContext(); try { @@ -522,7 +631,9 @@ public class CSpaceDbRealm implements CSpaceRealm { if (conn == null) { conn = AuthN.getDataSource().getConnection(); //FIXME:REM - This is the result of some type of JNDI mess. Should try to solve this problem and clean up this code. } + return conn; + } catch (NamingException ex) { LoginException le = new LoginException("Error looking up DataSource from: " + dataSourceName); le.initCause(ex); @@ -532,7 +643,7 @@ public class CSpaceDbRealm implements CSpaceRealm { try { ctx.close(); } catch (Exception e) { - e.printStackTrace(); + e.printStackTrace(); // We should be using a logger here instead. } } } @@ -587,4 +698,39 @@ public class CSpaceDbRealm implements CSpaceRealm { this.tenantsQueryNoDisabled = tenantQuery; } */ + + /* + * This method crawls the exception chain looking for network related exceptions and + * returns 'true' if it finds one. + */ + public static boolean exceptionChainContainsNetworkError(Throwable exceptionChain) { + boolean result = false; + Throwable cause = exceptionChain; + + while (cause != null) { + if (isExceptionNetworkRelated(cause) == true) { + result = true; + break; + } + + cause = cause.getCause(); + } + + return result; + } + + /* + * Return 'true' if the exception is in the "java.net" package. + */ + private static boolean isExceptionNetworkRelated(Throwable cause) { + boolean result = false; + + String className = cause.getClass().getCanonicalName(); + if (className.contains("java.net") == true) { + result = true; + } + + return result; + } + } diff --git a/services/authentication/service/src/main/java/org/collectionspace/authentication/spring/CSpaceAuthorityGranter.java b/services/authentication/service/src/main/java/org/collectionspace/authentication/spring/CSpaceAuthorityGranter.java index 66355bf78..85ec97db7 100644 --- a/services/authentication/service/src/main/java/org/collectionspace/authentication/spring/CSpaceAuthorityGranter.java +++ b/services/authentication/service/src/main/java/org/collectionspace/authentication/spring/CSpaceAuthorityGranter.java @@ -54,6 +54,7 @@ import java.security.acl.Group; import java.util.Enumeration; import java.util.HashSet; import java.util.Set; + import org.springframework.security.authentication.jaas.AuthorityGranter; /** @@ -66,7 +67,7 @@ public class CSpaceAuthorityGranter implements AuthorityGranter { Set authorities = new HashSet(); if (principal instanceof Group) { Group g = (Group) principal; - Enumeration members = g.members(); + Enumeration members = g.members(); while (members.hasMoreElements()) { Principal p = (Principal) members.nextElement(); authorities.add(p.getName()); diff --git a/services/authentication/service/src/main/resources/config/jboss-login-config.xml b/services/authentication/service/src/main/resources/config/jboss-login-config.xml deleted file mode 100644 index ee670014e..000000000 --- a/services/authentication/service/src/main/resources/config/jboss-login-config.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - CspaceDS - SHA-256 - false - org.collectionspace.authentication.CSpacePrincipal - - select passwd from users where username=? - - - select r.rolename, 'Role' from roles as r, accounts_roles as ar where ar.user_id=? and ar.role_id=r.csid - - - select t.id, t.name, 'Tenants' from accounts_common as a, accounts_tenants as at, tenants as t where a.userid=? and a.csid = at.TENANTS_ACCOUNTSCOMMON_CSID and at.tenant_id = t.id - - - - diff --git a/services/authentication/service/src/main/resources/config/login-config.xml b/services/authentication/service/src/main/resources/config/login-config.xml deleted file mode 100644 index 948145332..000000000 --- a/services/authentication/service/src/main/resources/config/login-config.xml +++ /dev/null @@ -1,182 +0,0 @@ - - - - - - - - - - - true - - - - - - - - - guest - java:/DefaultDS - SELECT PASSWD FROM JMS_USERS WHERE USERID=? - SELECT ROLEID, 'Roles' FROM JMS_ROLES WHERE USERID=? - - - - - - - - - - - sa - sa - - jboss.jca:service=LocalTxCM,name=DefaultDS - - - - - - - - guest - guest - guest - jboss.jca:service=TxCM,name=JmsXA - - - - - - - - - props/jmx-console-users.properties - props/jmx-console-roles.properties - - - - - - - - - web-console-users.properties - web-console-roles.properties - - - - - - - - - props/jbossws-users.properties - props/jbossws-roles.properties - anonymous - - - - - - - - CspaceDS - SHA-256 - false - org.collectionspace.authentication.CSpacePrincipal - - select passwd from users where username=? - - - select r.rolename, 'Role' from roles as r, accounts_roles as ar where ar.user_id=? and ar.role_id=r.csid - - - select t.id, t.name, 'Tenants' from accounts_common as a, accounts_tenants as at, tenants as t where a.userid=? and a.csid = at.TENANTS_ACCOUNTSCOMMON_CSID and at.tenant_id = t.id and NOT t.disabled - - - select t.id, t.name, 'Tenants' from accounts_common as a, accounts_tenants as at, tenants as t where a.userid=? and a.csid = at.TENANTS_ACCOUNTSCOMMON_CSID and at.tenant_id = t.id - - - - - - - - - - - - - - - diff --git a/services/authorization/service/src/main/java/org/collectionspace/services/authorization/spring/SpringPermissionEvaluator.java b/services/authorization/service/src/main/java/org/collectionspace/services/authorization/spring/SpringPermissionEvaluator.java index bdb1e80cd..c04449e0e 100644 --- a/services/authorization/service/src/main/java/org/collectionspace/services/authorization/spring/SpringPermissionEvaluator.java +++ b/services/authorization/service/src/main/java/org/collectionspace/services/authorization/spring/SpringPermissionEvaluator.java @@ -25,11 +25,11 @@ package org.collectionspace.services.authorization.spring; import java.util.List; import java.io.Serializable; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.collectionspace.services.authorization.CSpaceAction; import org.collectionspace.services.authorization.spi.CSpacePermissionEvaluator; - import org.collectionspace.services.authorization.CSpaceResource; import org.springframework.security.access.PermissionEvaluator; import org.springframework.security.acls.model.Permission; @@ -64,10 +64,13 @@ public class SpringPermissionEvaluator implements CSpacePermissionEvaluator { debug(res, authToken, objectIdId, objectIdType, perm); result = eval.hasPermission(authToken, objectIdId, objectIdType, perm); - } catch (Exception e) { - // - // If this exception is caused by a network timeout, we may want to reattempt - // + } catch (Throwable e) { + if (exceptionChainContainsNetworkError(e) == true) { + // + // If this exception is caused by a network timeout, we want to re-attempt + // + throw e; + } log.error("Unexpected exception encountered while evaluating permissions.", e); } @@ -92,4 +95,32 @@ public class SpringPermissionEvaluator implements CSpacePermissionEvaluator { System.out.println(""); } } + + public static boolean exceptionChainContainsNetworkError(Throwable exceptionChain) { + boolean result = false; + Throwable cause = exceptionChain; + + while (cause != null) { + if (isCauseNetworkRelated(cause) == true) { + result = true; + break; + } + + cause = cause.getCause(); + } + + return result; + } + + private static boolean isCauseNetworkRelated(Throwable cause) { + boolean result = false; + + String className = cause.getClass().getCanonicalName(); + if (className.contains("java.net") || className.contains("java.io")) { + result = true; + } + + return result; + } + } diff --git a/services/common/src/main/java/org/collectionspace/services/common/CSWebApplicationException.java b/services/common/src/main/java/org/collectionspace/services/common/CSWebApplicationException.java new file mode 100644 index 000000000..6c4ee7082 --- /dev/null +++ b/services/common/src/main/java/org/collectionspace/services/common/CSWebApplicationException.java @@ -0,0 +1,148 @@ +package org.collectionspace.services.common; + +import java.net.SocketException; +import java.util.Random; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; + +import org.collectionspace.services.common.document.DocumentException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CSWebApplicationException extends WebApplicationException { + static final Logger logger = LoggerFactory.getLogger(CSWebApplicationException.class); + public final static String NETWORK_ERROR_TYPE = "text/neterr"; + public final static String TEXT_MIME_TYPE = "text/plain"; + /** + * + */ + private static final long serialVersionUID = 1L; + private Response response; + + // + // Override of constructors + // + public CSWebApplicationException(Response response) { + super(response); // this set's our parent's private response member + this.response = response; // we need to set our copy since we override the getResponse() method. + logger.warn("CSWebApplicationException instance created without an underlying 'cause' exception."); + } + + public CSWebApplicationException(Throwable cause) { + super(cause); + this.response = getFinalResponse(cause, null); + } + + public CSWebApplicationException(Throwable cause, Response response) { + super(cause); + this.response = getFinalResponse(cause, response); + } + + // + // Overrides and custom methods + // + + @Override + public Response getResponse() { + return response; + } + + private Response getFinalResponse(Throwable cause, Response response) { + Response finalResponse = response; + + if (exceptionChainContainsNetworkError(cause) == true) { + if (response != null) { + finalResponse = Response.fromResponse(response).entity(cause.getMessage()).type(NETWORK_ERROR_TYPE).build(); + } else { + finalResponse = Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity( + cause.getMessage()).type(NETWORK_ERROR_TYPE).build(); + + } + if (logger.isTraceEnabled() == true) { + logger.trace(cause.getMessage(), cause); + } + } + + if (finalResponse == null) { + finalResponse = Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity( + cause.getMessage()).type(TEXT_MIME_TYPE).build(); + } + + return finalResponse; + } + + /* + * This method crawls the exception chain looking for network related exceptions and + * returns 'true' if it finds one. + */ + public static boolean exceptionChainContainsNetworkError(Throwable exceptionChain) { + boolean result = false; + + Throwable cause = exceptionChain; + while (cause != null) { + if (isExceptionNetworkRelated(cause) == true) { + result = true; + break; + } + + Throwable nextCause = cause.getCause(); + if (nextCause == null) { + // Since we reached the end of the exception chain, we can see if the last code + // executed was network related. + StackTraceElement[] finalStackTrace = cause.getStackTrace(); + } + } + + return result; + } + + private static boolean isStackTraceNetworkRelated(StackTraceElement[] stackTraceElementList) { + boolean result = false; + + for (StackTraceElement stackTraceElement : stackTraceElementList) { + if (stackTraceElement.getClassName().contains("") == true) { + + } + } + + return result; + } + + /* + * Return 'true' if the exception is in the "java.net" package. + */ + private static boolean isExceptionNetworkRelated(Throwable cause) { + boolean result = false; + + if (cause != null) { + String className = cause.getClass().getCanonicalName(); + if (cause instanceof DocumentException) { + className = ((DocumentException) cause).getCausesClassName(); // Since Nuxeo wraps the real exception, we needed to create this special getCausesClassName() method -see NuxeoDocumentException for details + } + if (className != null && className.contains("java.net") == true) { + result = true; + } + } + + return result; + } + + /* + * This Method is intended only for testing the robustness of our network exception handling + * and retry code. You can place a call to this method is various locations throughout the code + * and it will randomly throw an exception. + */ + public static void throwAnException(int chance) throws SocketException { + Random generator = new Random(System.currentTimeMillis()); + int roll = generator.nextInt(chance) + 1; + if (roll != 1) { + if (logger.isDebugEnabled()) { + logger.debug("Throwing a SocketException at random to test our network robustness."); + } + throw new SocketException(); + } + } + +} diff --git a/services/common/src/main/java/org/collectionspace/services/common/NetworkErrorRetryFilter.java b/services/common/src/main/java/org/collectionspace/services/common/NetworkErrorRetryFilter.java new file mode 100644 index 000000000..ab05ee06d --- /dev/null +++ b/services/common/src/main/java/org/collectionspace/services/common/NetworkErrorRetryFilter.java @@ -0,0 +1,207 @@ +/** + * This document is a part of the source code and related artifacts + * for CollectionSpace, an open source collections management system + * for museums and related institutions: + * + * http://www.collectionspace.org + * http://wiki.collectionspace.org + * + * Copyright © 2009 {Contributing Institution} + * + * Licensed under the Educational Community License (ECL), Version 2.0. + * You may not use this file except in compliance with this License. + * + * You may obtain a copy of the ECL 2.0 License at + * https://source.collectionspace.org/collection-space/LICENSE.txt + */ +package org.collectionspace.services.common; + +import java.io.IOException; +import java.net.ConnectException; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; + +import org.collectionspace.services.common.CSWebApplicationException; +import org.collectionspace.services.common.ServletTools; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * CSpaceFilter.java + * + * A filter that performs specified actions at the time + * each new request is received by the servlet container. + * + * This filter is currently used for recording performance + * metrics for requests to the CollectionSpace services. + * + * $LastChangedRevision: $ + * $LastChangedDate: $ + * + */ +public class NetworkErrorRetryFilter implements Filter { + final Logger logger = LoggerFactory.getLogger(NetworkErrorRetryFilter.class); + + /** The filter config. */ + FilterConfig filterConfig = null; + private final String CLASS_NAME = this.getClass().getSimpleName(); + + private long maxRetrySeconds = MAX_RETRY_SECONDS; + private static final int MAX_RETRY_SECONDS = 5; + private static final String MAX_RETRY_SECONDS_STR = "maxRetrySeconds"; + + private long delayBetweenAttemptsMillis = DELAY_BETWEEN_ATTEMPTS_MILLISECONDS; + private static final String DELAY_BETWEEN_ATTEMPTS_MILLISECONDS_STR = "delayBetweenAttemptsMillis"; + private static final long DELAY_BETWEEN_ATTEMPTS_MILLISECONDS = 200; + + protected void setMaxRetrySeconds(FilterConfig filterConfig) { + String paramValue = filterConfig.getInitParameter(MAX_RETRY_SECONDS_STR); + if (paramValue != null) { + try { + maxRetrySeconds = Long.parseLong(paramValue); + } catch (NumberFormatException e) { + logger.warn(String.format("The init parameter '%s' with value '%s' of the servlet filter '%s' could not be parsed to a long value. The default value of '%d' will be used instead.", + MAX_RETRY_SECONDS_STR, paramValue, CLASS_NAME, maxRetrySeconds)); + } + } + } + + protected long getMaxRetrySeconds() { + return this.maxRetrySeconds; + } + + protected void setDelayBetweenAttemptsMillis(FilterConfig filterConfig) { + String paramValue = filterConfig.getInitParameter(DELAY_BETWEEN_ATTEMPTS_MILLISECONDS_STR); + if (paramValue != null) { + try { + delayBetweenAttemptsMillis = Long.parseLong(paramValue); + } catch (NumberFormatException e) { + logger.warn(String.format("The init parameter '%s' with value '%s' of the servlet filter '%s' could not be parsed to a long value. The default value of '%d' will be used instead.", + MAX_RETRY_SECONDS_STR, paramValue, CLASS_NAME, delayBetweenAttemptsMillis)); + } + } + } + + protected long getDelayBetweenAttemptsMillis() { + return this.delayBetweenAttemptsMillis; + } + + /* (non-Javadoc) + * @see javax.servlet.Filter#init(javax.servlet.FilterConfig) + */ + @Override + public void init(FilterConfig theFilterConfig) throws ServletException { + filterConfig = theFilterConfig; + + if (filterConfig != null) { + setMaxRetrySeconds(theFilterConfig); + setDelayBetweenAttemptsMillis(theFilterConfig); + } + } + + /* (non-Javadoc) + * @see javax.servlet.Filter#destroy() + */ + @Override + public void destroy() { + // Empty method. + } + + /* (non-Javadoc) + * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) + */ + @Override + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { + if (request != null) { + try { + doWrappedFilter(request, response, chain); + } catch (Throwable e) { + if (logger.isDebugEnabled() == true) { + logger.debug(CLASS_NAME, e); + } + throw new ServletException(e); + } + } + } + + // + // This method will attempt to repeat the chain.doFilter() method if it fails because of a + // network related reason. It looks for failures in two ways: + // + // 1. It looks at the response content type for a special string that we set in the CSWebApplicationException class. + // 2. If we catch an exception, we look at the exception chain for network related exceptions. + // + public void doWrappedFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws Throwable { + boolean failed = true; + Throwable lastException = null; + int requestAttempts = 0; + + long quittingTime = System.currentTimeMillis() + getMaxRetrySeconds() * 1000; // This is how long we attempt retries + do { + if (requestAttempts > 0) { + response.reset(); // This will reset the response instance from the last failed attempt -i.e., we're retrying the request so we don't care about the last response + Thread.sleep(getDelayBetweenAttemptsMillis()); // Wait a little time between reattempts. + } + + try { + // proceed to the original request by calling doFilter() + chain.doFilter(request, response); + // check the response for network related errors + if (hasNetworkRelatedError(response) == false) { + failed = false; + break; // the request was successfully executed, so we can break out of this retry loop + } else { + throw new ConnectException(); // The 'response' argument indicated a network related failure, so let's throw a generic connection exception + } + } catch (Exception e) { + lastException = e; + if (CSWebApplicationException.exceptionChainContainsNetworkError(lastException) == false) { + // Break if the exception chain does not contain a + // SocketException because we don't want to retry if it's not a network related failure + break; + } + requestAttempts++; // keep track of how many times we've tried the request + } + } while (System.currentTimeMillis() < quittingTime); // keep trying until we run out of time + + // + // Add a warning to the logs if we encountered *any* failures on our re-attempts. Only add the warning + // if we were eventually successful. + // + if (requestAttempts > 0 && failed == false) { + logger.warn(String.format("Request to '%s' URL failed with exception '%s' at attempt number '%d' before finally succeeding on the next attempt.", + ServletTools.getURL((HttpServletRequest) request), + lastException.getClass().getName(), + requestAttempts)); + } + + if (failed == true) { + // If we get here, it means all of our attempts to get a successful call to chain.doFilter() have failed. + throw lastException; + } + } + + private boolean hasNetworkRelatedError(ServletResponse response) { + boolean result = false; + + String contentType = response.getContentType(); + if (contentType != null && contentType.equalsIgnoreCase(CSWebApplicationException.NETWORK_ERROR_TYPE) == true) { + result = true; + } + + if (contentType == null) { + logger.debug("Response had no content type specified. Probably just a successful POST which will have content type."); + } + + return result; + } +} diff --git a/services/common/src/main/java/org/collectionspace/services/common/document/DocumentException.java b/services/common/src/main/java/org/collectionspace/services/common/document/DocumentException.java index 953641911..0060e8683 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/document/DocumentException.java +++ b/services/common/src/main/java/org/collectionspace/services/common/document/DocumentException.java @@ -114,5 +114,16 @@ public class DocumentException extends ServiceException { public DocumentException(Throwable cause) { super(cause); } + + public String getCausesClassName() { + String result = null; + + Throwable cause = super.getCause(); + if (cause != null) { + result = cause.getClass().getCanonicalName(); + } + + return result; + } } diff --git a/services/common/src/main/java/org/collectionspace/services/common/profile/CSpaceFilter.java b/services/common/src/main/java/org/collectionspace/services/common/profile/CSpaceFilter.java index 459cb4193..fad825f92 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/profile/CSpaceFilter.java +++ b/services/common/src/main/java/org/collectionspace/services/common/profile/CSpaceFilter.java @@ -17,7 +17,6 @@ package org.collectionspace.services.common.profile; import java.io.IOException; -import java.net.SocketException; import javax.servlet.Filter; import javax.servlet.FilterChain; @@ -26,11 +25,10 @@ import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; -//import javax.servlet.ServletContext; - import org.collectionspace.services.client.Profiler; import org.collectionspace.services.common.ServletTools; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,13 +46,11 @@ import org.slf4j.LoggerFactory; * */ public class CSpaceFilter implements Filter { + final Logger logger = LoggerFactory.getLogger(CSpaceFilter.class); /** The filter config. */ FilterConfig filterConfig = null; - private final String CLASS_NAME = this.getClass().getSimpleName(); - private static final int MAX_RETRY_SECONDS = 5; - /* (non-Javadoc) * @see javax.servlet.Filter#destroy() @@ -100,13 +96,7 @@ public class CSpaceFilter implements Filter { profiler.log(csvMsg, FORMAT_LOG_MESSAGE); // Process the request. - //chain.doFilter(request, response); - try { - invoke(request, response, chain); - } catch (Throwable e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } + chain.doFilter(request, response); // Stop timing and log performance-related metrics. profiler.stop(); @@ -127,61 +117,6 @@ public class CSpaceFilter implements Filter { } } - public void invoke(ServletRequest request, ServletResponse response, - FilterChain chain) throws Throwable { - Throwable lastException = null; - int attempt = 0; - - long quittingTime = System.currentTimeMillis() + MAX_RETRY_SECONDS * 1000; - do { - try { - // proceed to original method call - chain.doFilter(request, response); - String contentType = response.getContentType(); - lastException = null; - break; - } catch (Exception e) { - lastException = e; - if (exceptionChainContains(lastException, SocketException.class) == false) { - // Break if the exception chain does not contain a - // SocketException. - break; - } - attempt++; - System.out - .println(String - .format("'%s' URL request failed with exception '%s' at attemp '%d'", - ServletTools.getURL((HttpServletRequest) request), - lastException.getClass().getName(), - attempt)); - } - } while (System.currentTimeMillis() < quittingTime); - - if (lastException != null) { - throw lastException; - } - - System.out.println("Success!"); - } - - private boolean exceptionChainContains(Throwable exceptionChain, - Class target) { - boolean result = false; - Throwable top = exceptionChain; - - while (top != null) { - System.out.println(top.getClass().getCanonicalName()); - if (target.isInstance(top) == true) { - result = true; - break; - } - top = top.getCause(); - } - - return result; - } - - /* (non-Javadoc) * @see javax.servlet.Filter#init(javax.servlet.FilterConfig) */ diff --git a/services/common/src/main/java/org/collectionspace/services/common/query/QueryManager.java b/services/common/src/main/java/org/collectionspace/services/common/query/QueryManager.java index 8863aaeb7..6e330883b 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/query/QueryManager.java +++ b/services/common/src/main/java/org/collectionspace/services/common/query/QueryManager.java @@ -28,7 +28,6 @@ package org.collectionspace.services.common.query; import org.collectionspace.services.client.IQueryManager; import org.collectionspace.services.common.ServiceMain; -import org.collectionspace.services.common.api.Tools; import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl; import org.collectionspace.services.common.config.TenantBindingUtils; import org.collectionspace.services.common.context.ServiceContext; @@ -38,10 +37,6 @@ import org.collectionspace.services.config.tenant.TenantBindingType; public class QueryManager { static private final IQueryManager queryManager = new QueryManagerNuxeoImpl(); - static public void execQuery(String queryString) { - queryManager.execQuery(queryString); - } - /** * Creates the where clause from keywords. * diff --git a/services/common/src/main/java/org/collectionspace/services/common/query/nuxeo/QueryManagerNuxeoImpl.java b/services/common/src/main/java/org/collectionspace/services/common/query/nuxeo/QueryManagerNuxeoImpl.java index 744e998b3..a8c4734ed 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/query/nuxeo/QueryManagerNuxeoImpl.java +++ b/services/common/src/main/java/org/collectionspace/services/common/query/nuxeo/QueryManagerNuxeoImpl.java @@ -32,16 +32,11 @@ import org.slf4j.LoggerFactory; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.nuxeo.ecm.core.api.DocumentModel; -import org.nuxeo.ecm.core.api.DocumentModelList; -import org.nuxeo.ecm.core.api.repository.RepositoryInstance; //import org.nuxeo.ecm.core.client.NuxeoClient; import org.collectionspace.services.jaxb.InvocableJAXBSchema; //import org.collectionspace.services.nuxeo.client.java.NuxeoConnector; //import org.collectionspace.services.nuxeo.client.java.NxConnect; -import org.collectionspace.services.nuxeo.client.java.NuxeoClientEmbedded; -import org.collectionspace.services.nuxeo.client.java.NuxeoConnectorEmbedded; import org.collectionspace.services.client.IQueryManager; import org.collectionspace.services.common.invocable.InvocableUtils; @@ -105,32 +100,7 @@ public class QueryManagerNuxeoImpl implements IQueryManager { @Override @Deprecated public void execQuery(String queryString) { - NuxeoClientEmbedded client = null; - try { - client = NuxeoConnectorEmbedded.getInstance().getClient(); - RepositoryInstance repoSession = client.openRepository(); - - DocumentModelList docModelList = repoSession - .query("SELECT * FROM Relation WHERE relations_common:subjectCsid='updated-Subject-1'"); - // DocumentModelList docModelList = - // repoSession.query("SELECT * FROM Relation"); - // DocumentModelList docModelList = - // repoSession.query("SELECT * FROM CollectionObject WHERE collectionobject:objectNumber='objectNumber-1251305545865'"); - for (DocumentModel docModel : docModelList) { - System.out - .println("--------------------------------------------"); - System.out.println(docModel.getPathAsString()); - System.out.println(docModel.getName()); - System.out.println(docModel.getPropertyValue("dc:title")); - // System.out.println("subjectCsid=" + - // docModel.getProperty("relations_common", - // "subjectCsid").toString()); - } - - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } + // Intentionally left blank } @Override diff --git a/services/common/src/main/java/org/collectionspace/services/common/security/SecurityInterceptor.java b/services/common/src/main/java/org/collectionspace/services/common/security/SecurityInterceptor.java index ce5189c65..0b634de81 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/security/SecurityInterceptor.java +++ b/services/common/src/main/java/org/collectionspace/services/common/security/SecurityInterceptor.java @@ -39,12 +39,12 @@ import org.jboss.resteasy.annotations.interception.SecurityPrecedence; import org.jboss.resteasy.annotations.interception.ServerInterceptor; import org.jboss.resteasy.spi.Failure; import org.jboss.resteasy.spi.HttpRequest; + import org.nuxeo.runtime.api.Framework; import javax.security.auth.Subject; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; -import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; import javax.ws.rs.ext.Provider; @@ -58,6 +58,7 @@ import org.collectionspace.services.common.CollectionSpaceResource; import org.collectionspace.services.common.document.JaxbUtils; import org.collectionspace.services.common.storage.jpa.JpaStorageUtils; import org.collectionspace.services.common.security.SecurityUtils; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -87,21 +88,7 @@ public class SecurityInterceptor implements PreProcessInterceptor, PostProcessIn // private static final String ERROR_NUXEO_LOGOUT = "Attempt to logout when Nuxeo login context was null"; private static final String ERROR_UNBALANCED_LOGINS = "The number of Logins vs Logouts to the Nuxeo framework was unbalanced."; - - private boolean implementsInterface(Class clazz, Class interfaze) { - boolean result = false; - - Class[] interfaces = clazz.getInterfaces(); - for (Class interfaceItem : interfaces) { - if (interfaceItem.equals(interfaze)) { - result = true; - break; - } - } - - return result; - } - + private boolean isAnonymousRequest(HttpRequest request, ResourceMethod resourceMethod) { boolean result = false; @@ -126,85 +113,92 @@ public class SecurityInterceptor implements PreProcessInterceptor, PostProcessIn throws Failure, CSWebApplicationException { ServerResponse result = null; // A null value essentially means success for this method - if (isAnonymousRequest(request, resourceMethod) == true) { - // We don't need to check credentials for anonymous requests. Just login to Nuxeo and - // exit - nuxeoPreProcess(request, resourceMethod); - - return result; - } - - final String servicesResource = "/cspace-services/"; // HACK - this is configured in war - final int servicesResourceLen = servicesResource.length(); - String httpMethod = request.getHttpMethod(); - String uriPath = request.getUri().getPath(); - - if (logger.isDebugEnabled()) { - String fullRequest = request.getUri().getRequestUri().toString(); - int servicesResourceIdx = fullRequest.indexOf(servicesResource); - String relativeRequest = (servicesResourceIdx<=0)? fullRequest - : fullRequest.substring(servicesResourceIdx+servicesResourceLen); - logger.debug("received " + httpMethod + " on " + relativeRequest); - } - - String resName = SecurityUtils.getResourceName(request.getUri()); - String resEntity = SecurityUtils.getResourceEntity(resName); - - // - // If the resource entity is acting as a proxy then all sub-resource will map to the resource itself. - // This essentially means that the sub-resource inherit all the authz permissions of the entity. - // - if (SecurityUtils.isEntityProxy() == true && !resName.equalsIgnoreCase(ACCOUNT_PERMISSIONS)) { - resName = resEntity; - } - // - // Make sure the account is current and active - // - checkActive(); - - // - // All active users are allowed to the *their* (we enforce this) current list of permissions. If this is not - // the request, then we'll do a full AuthZ check. - // - if (resName.equalsIgnoreCase(ACCOUNT_PERMISSIONS) != true) { //see comment immediately above - AuthZ authZ = AuthZ.get(); - CSpaceResource res = new URIResourceImpl(AuthN.get().getCurrentTenantId(), resName, httpMethod); - if (authZ.isAccessAllowed(res) == false) { - logger.error("Access to " + res.getId() + " is NOT allowed to " - + " user=" + AuthN.get().getUserId()); - Response response = Response.status( - Response.Status.FORBIDDEN).entity(uriPath + " " + httpMethod).type("text/plain").build(); - throw new CSWebApplicationException(response); - } else { - // - // They passed the first round of security checks, so now let's check to see if they're trying - // to perform a workflow state change and make sure they are allowed to to this. - // - if (uriPath.contains(WorkflowClient.SERVICE_PATH) == true) { - String workflowProxyResource = SecurityUtils.getWorkflowResourceName(request); - res = new URIResourceImpl(AuthN.get().getCurrentTenantId(), workflowProxyResource, httpMethod); - if (authZ.isAccessAllowed(res) == false) { - logger.error("Access to " + resName + ":" + res.getId() + " is NOT allowed to " + try { + if (isAnonymousRequest(request, resourceMethod) == true) { + // We don't need to check credentials for anonymous requests. Just login to Nuxeo and + // exit + nuxeoPreProcess(request, resourceMethod); + + return result; + } + + final String servicesResource = "/cspace-services/"; // HACK - this is configured in war + final int servicesResourceLen = servicesResource.length(); + String httpMethod = request.getHttpMethod(); + String uriPath = request.getUri().getPath(); + + if (logger.isDebugEnabled()) { + String fullRequest = request.getUri().getRequestUri().toString(); + int servicesResourceIdx = fullRequest.indexOf(servicesResource); + String relativeRequest = (servicesResourceIdx<=0)? fullRequest + : fullRequest.substring(servicesResourceIdx+servicesResourceLen); + logger.debug("received " + httpMethod + " on " + relativeRequest); + } + + String resName = SecurityUtils.getResourceName(request.getUri()); + String resEntity = SecurityUtils.getResourceEntity(resName); + + // + // If the resource entity is acting as a proxy then all sub-resource will map to the resource itself. + // This essentially means that the sub-resource inherit all the authz permissions of the entity. + // + if (SecurityUtils.isEntityProxy() == true && !resName.equalsIgnoreCase(ACCOUNT_PERMISSIONS)) { + resName = resEntity; + } + // + // Make sure the account is current and active + // + checkActive(); + + // + // All active users are allowed to the *their* (we enforce this) current list of permissions. If this is not + // the request, then we'll do a full AuthZ check. + // + if (resName.equalsIgnoreCase(ACCOUNT_PERMISSIONS) != true) { //see comment immediately above + AuthZ authZ = AuthZ.get(); + CSpaceResource res = new URIResourceImpl(AuthN.get().getCurrentTenantId(), resName, httpMethod); + if (authZ.isAccessAllowed(res) == false) { + logger.error("Access to " + res.getId() + " is NOT allowed to " + " user=" + AuthN.get().getUserId()); Response response = Response.status( Response.Status.FORBIDDEN).entity(uriPath + " " + httpMethod).type("text/plain").build(); throw new CSWebApplicationException(response); + } else { + // + // They passed the first round of security checks, so now let's check to see if they're trying + // to perform a workflow state change and make sure they are allowed to to this. + // + if (uriPath.contains(WorkflowClient.SERVICE_PATH) == true) { + String workflowProxyResource = SecurityUtils.getWorkflowResourceName(request); + res = new URIResourceImpl(AuthN.get().getCurrentTenantId(), workflowProxyResource, httpMethod); + if (authZ.isAccessAllowed(res) == false) { + logger.error("Access to " + resName + ":" + res.getId() + " is NOT allowed to " + + " user=" + AuthN.get().getUserId()); + Response response = Response.status( + Response.Status.FORBIDDEN).entity(uriPath + " " + httpMethod).type("text/plain").build(); + throw new CSWebApplicationException(response); + } } } + // + // Login to Nuxeo + // + nuxeoPreProcess(request, resourceMethod); + + // + // We've passed all the checks. Now just log the results + // + if (logger.isTraceEnabled()) { + logger.trace("Access to " + res.getId() + " is allowed to " + + " user=" + AuthN.get().getUserId() + + " for tenant id=" + AuthN.get().getCurrentTenantName()); + } } - // - // Login to Nuxeo - // - nuxeoPreProcess(request, resourceMethod); - - // - // We've passed all the checks. Now just log the results - // - if (logger.isTraceEnabled()) { - logger.trace("Access to " + res.getId() + " is allowed to " + - " user=" + AuthN.get().getUserId() + - " for tenant id=" + AuthN.get().getCurrentTenantName()); + } catch (Throwable t) { + if (logger.isTraceEnabled() == true) { + t.printStackTrace(); } + throw t; } return result; @@ -220,14 +214,11 @@ public class SecurityInterceptor implements PreProcessInterceptor, PostProcessIn /** * checkActive check if account is active - * @throws CSWebApplicationException + * @throws CSWebApplicationException */ private void checkActive() throws CSWebApplicationException { String userId = AuthN.get().getUserId(); - - if (true) - throw new CSWebApplicationException(new java.net.SocketException("An faux exception thrown for testing.")); - + try { // Need to ensure that user is associated to a tenant String tenantId = AuthN.get().getCurrentTenantId(); @@ -335,7 +326,9 @@ public class SecurityInterceptor implements PreProcessInterceptor, PostProcessIn + threadLocalLoginContext.get()); } } + LoginContext loginContext = threadLocalLoginContext.get(); + if (loginContext == null) { loginContext = Framework.loginAs(user); frameworkLogins++; diff --git a/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/NuxeoClientEmbedded.java b/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/NuxeoClientEmbedded.java index c9d6ed6da..f7aeb66ef 100644 --- a/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/NuxeoClientEmbedded.java +++ b/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/NuxeoClientEmbedded.java @@ -155,22 +155,6 @@ public final class NuxeoClientEmbedded { return openRepository(repoName, -1); } - /* - * Open a Nuxeo repo session using the default repo with the specified (passed in) tx timeout period - */ - @Deprecated - public RepositoryInstance openRepository(int timeoutSeconds) throws Exception { - return openRepository(null, timeoutSeconds); - } - - /* - * Open a Nuxeo repo session using the default repo with the default tx timeout period - */ - @Deprecated - public RepositoryInstance openRepository() throws Exception { - return openRepository(null, -1 /*default timeout period*/); - } - public RepositoryInstance openRepository(String repoName, int timeoutSeconds) throws Exception { RepositoryInstance result = null; diff --git a/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/NuxeoDocumentException.java b/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/NuxeoDocumentException.java new file mode 100644 index 000000000..193185778 --- /dev/null +++ b/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/NuxeoDocumentException.java @@ -0,0 +1,68 @@ +package org.collectionspace.services.nuxeo.client.java; + +import org.collectionspace.services.common.document.DocumentException; +import org.nuxeo.ecm.core.api.WrappedException; + +public class NuxeoDocumentException extends DocumentException { + + /** + * + */ + private static final long serialVersionUID = 1L; + + public NuxeoDocumentException() { + super(); + } + + public NuxeoDocumentException(String msg) { + super(msg); + // TODO Auto-generated constructor stub + } + + public NuxeoDocumentException(int errorCode) { + super(errorCode); + // TODO Auto-generated constructor stub + } + + public NuxeoDocumentException(int errorCode, String errorReason) { + super(errorCode, errorReason); + // TODO Auto-generated constructor stub + } + + public NuxeoDocumentException(String message, Throwable cause) { + super(message, cause); + // TODO Auto-generated constructor stub + } + + public NuxeoDocumentException(Throwable cause) { + super(cause); + // TODO Auto-generated constructor stub + } + + @Override + public String getCausesClassName() { + String result = null; + Throwable cause = super.getCause(); + + if (cause != null && cause instanceof WrappedException) { + WrappedException wrappedException = (WrappedException)cause; + result = wrappedException.getClassName(); + } else { + result = cause != null ? super.getCausesClassName() : null; + } + + return result; + } + + protected boolean isNuxeoWrappedException(Throwable cause) { + boolean result = false; + + String className = cause.getClass().getCanonicalName(); + if (className.contains("org.nuxeo.ecm.core.api.WrappedException") == true) { + result = true; + } + + return result; + } + +} diff --git a/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RepositoryJavaClientImpl.java b/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RepositoryJavaClientImpl.java index e44ad6ee0..2b562fe0e 100644 --- a/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RepositoryJavaClientImpl.java +++ b/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RepositoryJavaClientImpl.java @@ -207,7 +207,7 @@ public class RepositoryJavaClientImpl implements RepositoryClient