Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/**
* Copyright (C) 2015 BonitaSoft S.A.
* BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble
* This library is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation
* version 2.1 of the License.
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301, USA.
**/
package org.bonitasoft.engine.api.impl;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import org.bonitasoft.engine.api.NoSessionRequired;
import org.bonitasoft.engine.api.PlatformAPI;
import org.bonitasoft.engine.api.TenantAdministrationAPI;
import org.bonitasoft.engine.api.impl.transaction.CustomTransactions;
import org.bonitasoft.engine.api.internal.ServerAPI;
import org.bonitasoft.engine.api.internal.ServerWrappedException;
import org.bonitasoft.engine.classloader.ClassLoaderService;
import org.bonitasoft.engine.classloader.SClassLoaderException;
import org.bonitasoft.engine.commons.ClassReflector;
import org.bonitasoft.engine.commons.exceptions.SBonitaException;
import org.bonitasoft.engine.core.login.LoginService;
import org.bonitasoft.engine.core.platform.login.PlatformLoginService;
import org.bonitasoft.engine.dependency.model.ScopeType;
import org.bonitasoft.engine.exception.BonitaContextException;
import org.bonitasoft.engine.exception.BonitaException;
import org.bonitasoft.engine.exception.BonitaHomeConfigurationException;
import org.bonitasoft.engine.exception.BonitaHomeNotSetException;
import org.bonitasoft.engine.exception.BonitaRuntimeException;
import org.bonitasoft.engine.exception.TenantStatusException;
import org.bonitasoft.engine.log.technical.TechnicalLogSeverity;
import org.bonitasoft.engine.log.technical.TechnicalLoggerService;
import org.bonitasoft.engine.platform.NodeNotStartedException;
import org.bonitasoft.engine.platform.PlatformService;
import org.bonitasoft.engine.platform.session.PlatformSessionService;
import org.bonitasoft.engine.platform.session.SSessionException;
import org.bonitasoft.engine.scheduler.SchedulerService;
import org.bonitasoft.engine.scheduler.exception.SSchedulerException;
import org.bonitasoft.engine.service.APIAccessResolver;
import org.bonitasoft.engine.service.PlatformServiceAccessor;
import org.bonitasoft.engine.service.TenantServiceAccessor;
import org.bonitasoft.engine.service.impl.ServiceAccessorFactory;
import org.bonitasoft.engine.session.APISession;
import org.bonitasoft.engine.session.InvalidSessionException;
import org.bonitasoft.engine.session.PlatformSession;
import org.bonitasoft.engine.session.Session;
import org.bonitasoft.engine.session.SessionService;
import org.bonitasoft.engine.sessionaccessor.SessionAccessor;
import org.bonitasoft.engine.transaction.UserTransactionService;
/**
* @author Matthieu Chaffotte
* @author Baptiste Mesta
* @author Celine Souchet
*/
public class ServerAPIImpl implements ServerAPI {
private static final String SESSION = "session";
private static final String IS_NODE_STARTED_METHOD_NAME = "isNodeStarted";
private static final long serialVersionUID = -161775388604256321L;
private static final String IS_PAUSED = "isPaused";
protected final APIAccessResolver accessResolver;
private final boolean cleanSession;
private TechnicalLoggerService technicalLogger;
public ServerAPIImpl() {
this(true);
}
public ServerAPIImpl(final boolean cleanSession) {
try {
this.cleanSession = cleanSession;
accessResolver = getServiceAccessorFactoryInstance().createAPIAccessResolver();
} catch (final Exception e) {
throw new BonitaRuntimeException(e);
}
}
/**
* For Test Mock usage
*/
public ServerAPIImpl(boolean cleanSession, APIAccessResolver accessResolver) {
this.cleanSession = cleanSession;
this.accessResolver = accessResolver;
}
void setTechnicalLogger(final TechnicalLoggerService technicalLoggerService) {
technicalLogger = technicalLoggerService;
}
private ServiceAccessorFactory getServiceAccessorFactoryInstance() {
return ServiceAccessorFactory.getInstance();
}
@Override
public Object invokeMethod(final Map options, final String apiInterfaceName, final String methodName,
final List classNameParameters, final Object[] parametersValues) throws ServerWrappedException {
technicalTraceLog("Starting ", apiInterfaceName, methodName);
final ClassLoader baseClassLoader = Thread.currentThread().getContextClassLoader();
SessionAccessor sessionAccessor = null;
Session session = null;
try {
try {
session = (Session) options.get(SESSION);
sessionAccessor = beforeInvokeMethod(session, apiInterfaceName);
return invokeAPI(apiInterfaceName, methodName, classNameParameters, parametersValues, session);
} catch (final ServerAPIRuntimeException sapire) {
throw sapire.getCause();
}
} catch (final BonitaRuntimeException | BonitaException bre) {
fillGlobalContextForException(session, bre);
throw createServerWrappedException(bre);
} catch (final UndeclaredThrowableException ute) {
technicalDebugLog(ute);
throw createServerWrappedException(ute);
} catch (final Throwable cause) {
technicalDebugLog(cause);
final BonitaRuntimeException throwableToWrap = wrapThrowable(cause);
fillGlobalContextForException(session, throwableToWrap);
throw createServerWrappedException(throwableToWrap);
} finally {
cleanSessionIfNeeded(sessionAccessor);
// reset class loader
Thread.currentThread().setContextClassLoader(baseClassLoader);
technicalTraceLog("End ", apiInterfaceName, methodName);
}
}
protected BonitaRuntimeException wrapThrowable(final Throwable cause) {
return new BonitaRuntimeException(cause);
}
private ServerWrappedException createServerWrappedException(final Throwable throwableToWrap) {
return new ServerWrappedException(throwableToWrap);
}
private void fillGlobalContextForException(final Session session, final BonitaContextException be) {
fillUserNameContextForException(session, be);
}
private void fillUserNameContextForException(final Session session, final BonitaContextException be) {
if (session != null) {
final String userName = session.getUserName();
if (userName != null) {
be.setUserName(userName);
}
}
}
private void cleanSessionIfNeeded(SessionAccessor sessionAccessor) {
if (cleanSession) {
// clean session id
if (sessionAccessor != null) {
sessionAccessor.deleteSessionId();
}
}
}
private void technicalDebugLog(final Throwable throwableToLog) {
if (technicalLogger != null && technicalLogger.isLoggable(this.getClass(), TechnicalLogSeverity.DEBUG)) {
technicalLogger.log(this.getClass(), TechnicalLogSeverity.DEBUG, throwableToLog);
}
}
private void technicalTraceLog(String prefix, String apiInterfaceName, String methodName) {
if (technicalLogger != null && technicalLogger.isLoggable(this.getClass(), TechnicalLogSeverity.TRACE)) {
technicalLogger.log(this.getClass(), TechnicalLogSeverity.TRACE, prefix + "Server API call " + apiInterfaceName + " " + methodName);
}
}
SessionAccessor beforeInvokeMethod(final Session session, final String apiInterfaceName) throws BonitaHomeNotSetException,
InstantiationException, IllegalAccessException, ClassNotFoundException, BonitaHomeConfigurationException, IOException, NoSuchMethodException,
InvocationTargetException, SBonitaException {
SessionAccessor sessionAccessor = null;
final ServiceAccessorFactory serviceAccessorFactory = getServiceAccessorFactoryInstance();
final PlatformServiceAccessor platformServiceAccessor = serviceAccessorFactory.createPlatformServiceAccessor();
ClassLoader serverClassLoader = null;
if (session != null) {
final SessionType sessionType = getSessionType(session);
sessionAccessor = serviceAccessorFactory.createSessionAccessor();
switch (sessionType) {
case PLATFORM:
serverClassLoader = beforeInvokeMethodForPlatformSession(sessionAccessor, platformServiceAccessor, session);
break;
case API:
serverClassLoader = beforeInvokeMethodForAPISession(sessionAccessor, serviceAccessorFactory, platformServiceAccessor, session);
break;
default:
throw new InvalidSessionException("Unknown session type: " + session.getClass().getName());
}
} else if (accessResolver.needSession(apiInterfaceName)) {
throw new InvalidSessionException("Session is null!");
}
if (serverClassLoader != null) {
Thread.currentThread().setContextClassLoader(serverClassLoader);
}
return sessionAccessor;
}
private ClassLoader beforeInvokeMethodForAPISession(final SessionAccessor sessionAccessor, final ServiceAccessorFactory serviceAccessorFactory,
final PlatformServiceAccessor platformServiceAccessor, final Session session) throws SBonitaException, BonitaHomeNotSetException, IOException,
BonitaHomeConfigurationException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException {
checkTenantSession(platformServiceAccessor, session);
final long tenantId = ((APISession) session).getTenantId();
final SessionService sessionService = platformServiceAccessor.getTenantServiceAccessor(tenantId).getSessionService();
sessionService.renewSession(session.getId());
sessionAccessor.setSessionInfo(session.getId(), tenantId);
final ClassLoader serverClassLoader = getTenantClassLoader(platformServiceAccessor, session);
setTechnicalLogger(serviceAccessorFactory.createTenantServiceAccessor(tenantId).getTechnicalLoggerService());
return serverClassLoader;
}
private ClassLoader beforeInvokeMethodForPlatformSession(final SessionAccessor sessionAccessor, final PlatformServiceAccessor platformServiceAccessor,
final Session session) throws SSessionException, SClassLoaderException {
final PlatformSessionService platformSessionService = platformServiceAccessor.getPlatformSessionService();
final PlatformLoginService loginService = platformServiceAccessor.getPlatformLoginService();
if (!loginService.isValid(session.getId())) {
throw new InvalidSessionException("Invalid session");
}
platformSessionService.renewSession(session.getId());
sessionAccessor.setSessionInfo(session.getId(), -1);
final ClassLoader serverClassLoader = getPlatformClassLoader(platformServiceAccessor);
setTechnicalLogger(platformServiceAccessor.getTechnicalLoggerService());
return serverClassLoader;
}
protected SessionType getSessionType(final Session session) {
SessionType sessionType = null;
if (session instanceof PlatformSession) {
sessionType = SessionType.PLATFORM;
} else if (session instanceof APISession) {
sessionType = SessionType.API;
}
return sessionType;
}
Object invokeAPI(final String apiInterfaceName, final String methodName, final List classNameParameters, final Object[] parametersValues,
final Session session) throws Throwable {
final Class>[] parameterTypes = getParameterTypes(classNameParameters);
final Object apiImpl = accessResolver.getAPIImplementation(apiInterfaceName);
final Method method = ClassReflector.getMethod(apiImpl.getClass(), methodName, parameterTypes);
// No session required means that there is no transaction
if (method.isAnnotationPresent(CustomTransactions.class) || method.isAnnotationPresent(NoSessionRequired.class)) {
return invokeAPIOutsideTransaction(parametersValues, apiImpl, method, apiInterfaceName, session);
} else {
return invokeAPIInTransaction(parametersValues, apiImpl, method, session, apiInterfaceName);
}
}
protected Object invokeAPIOutsideTransaction(Object[] parametersValues, Object apiImpl, Method method, String apiInterfaceName, Session session)
throws Throwable {
checkMethodAccessibility(apiImpl, apiInterfaceName, method, session, /* Not in transaction */false);
return invokeAPI(method, apiImpl, parametersValues);
}
protected void checkMethodAccessibility(final Object apiImpl, final String apiInterfaceName, final Method method, final Session session,
boolean isAlreadyInTransaction) {
warnIfDeprecated(method, apiInterfaceName);
if (!isNodeInAValidStateFor(method)) {
logNodeNotStartedMessage(apiInterfaceName, method);
throw new NodeNotStartedException();
}
// we don't check if tenant is in pause mode at platform level and when there is no session
// when there is no session means that we are trying to log in, in this case it is the LoginApiExt that check if the user is the technical user
// For tenant level method call:
if (session instanceof APISession) {
final long tenantId = ((APISession) session).getTenantId();
checkTenantIsInAValidModeFor(apiImpl, method, apiInterfaceName, tenantId, session, isAlreadyInTransaction);
}
}
protected void checkTenantIsInAValidModeFor(final Object apiImpl, final Method method, final String apiInterfaceName, final long tenantId,
final Session session, boolean isAlreadyInTransaction) {
final boolean tenantRunning = isTenantAvailable(tenantId, session, isAlreadyInTransaction);
final AvailableWhenTenantIsPaused methodAnnotation = method.getAnnotation(AvailableWhenTenantIsPaused.class);
AvailableWhenTenantIsPaused annotation;
if (methodAnnotation != null) {
annotation = methodAnnotation;
} else {
final Class> apiClass = apiImpl.getClass();
annotation = apiClass.getAnnotation(AvailableWhenTenantIsPaused.class);
}
checkIsValidModeFor(tenantRunning, annotation, tenantId, apiImpl, method, apiInterfaceName);
}
protected void checkIsValidModeFor(final boolean tenantRunning, final AvailableWhenTenantIsPaused annotation, final long tenantId, final Object apiImpl,
final Method method, final String apiInterfaceName) {
// On running tenant, annotation must not be present, or present without ONLY flag:
boolean okOnRunningTenant = isMethodAvailableOnRunningTenant(tenantRunning, annotation);
// On paused tenant, annotation must be present (without consideration on ONLY flag):
boolean okOnPausedTenant = isMethodAvailableOnPausedTenant(tenantRunning, annotation);
if (!(okOnRunningTenant || okOnPausedTenant)) {
if (tenantRunning) {
methodCannotBeCalledOnRunningTenant(apiImpl, apiInterfaceName, method, tenantId);
} else {
methodCannotBeCalledOnPausedTenant(apiImpl, apiInterfaceName, method, tenantId);
}
}
}
protected void methodCannotBeCalledOnRunningTenant(final Object apiImpl, final String apiInterfaceName, final Method method, final long tenantId) {
logTechnicalErrorMessage("Tenant is running. Method '" + apiInterfaceName + "." + method.getName() + "' on implementation '"
+ apiImpl.getClass().getSimpleName() + "' can only be called when the tenant is PAUSED (call TenantManagementAPI.pause() first)");
throw new TenantStatusException("Tenant with ID " + tenantId + " is running, method '" + apiInterfaceName + "." + method.getName()
+ "()' cannot be called.");
}
protected void methodCannotBeCalledOnPausedTenant(final Object apiImpl, final String apiInterfaceName, final Method method, final long tenantId) {
logTechnicalErrorMessage("Tenant in pause. Method '" + apiInterfaceName + "." + method.getName() + "' on implementation '"
+ apiImpl.getClass().getSimpleName() + "' cannot be called until the tenant mode is RUNNING again (call TenantManagementAPI.resume() first)");
throw new TenantStatusException("Tenant with ID " + tenantId + " is in pause, no API call on this tenant can be made for now.");
}
protected boolean isMethodAvailableOnPausedTenant(final boolean tenantRunning, final AvailableWhenTenantIsPaused annotation) {
return !tenantRunning && annotation != null;
}
protected boolean isMethodAvailableOnRunningTenant(final boolean tenantRunning, final AvailableWhenTenantIsPaused annotation) {
return tenantRunning && (annotation == null || !annotation.only());
}
/**
* @param tenantId
* the ID of the tenant to check
* @param session
* the session to user
* @param isAlreadyInTransaction
* if the request is made in a transaction
* @return true if the tenant is available, false otherwise (if the tenant is paused)
*/
protected boolean isTenantAvailable(final long tenantId, final Session session, boolean isAlreadyInTransaction) {
final Object apiImpl;
try {
apiImpl = accessResolver.getAPIImplementation(TenantAdministrationAPI.class.getName());
final Method method = ClassReflector.getMethod(apiImpl.getClass(), IS_PAUSED);
final Boolean paused;
if (isAlreadyInTransaction) {
paused = (Boolean) invokeAPI(method, apiImpl);
} else {
final UserTransactionService userTransactionService = selectUserTransactionService(session, getSessionType(session));
final Callable