diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f99c22..8f9b6d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [v2.1.0-SNAPSHOT] - [2022-02-04] + +added OfflineObserver for test purpose + ## [v2.0.3] - [2020-11-03] removed WARNING log on Vfs.Dir at startup (https://support.d4science.org/issues/18551) diff --git a/pom.xml b/pom.xml index e043aaf..9fbb391 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.gcube.core common-smartgears-app - 2.0.3 + 2.1.0-SNAPSHOT Smartgears Application diff --git a/src/main/java/org/gcube/smartgears/ApplicationManagerProvider.java b/src/main/java/org/gcube/smartgears/ApplicationManagerProvider.java index a3af77a..6877106 100644 --- a/src/main/java/org/gcube/smartgears/ApplicationManagerProvider.java +++ b/src/main/java/org/gcube/smartgears/ApplicationManagerProvider.java @@ -1,43 +1,60 @@ package org.gcube.smartgears; -import java.lang.reflect.Method; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Future; +import org.gcube.smartgears.annotations.ManagedBy; +import org.gcube.smartgears.application.manager.AppManagerObserver; +import org.gcube.smartgears.application.manager.OfflineProvider; +import org.gcube.smartgears.application.manager.OnlineProvider; +import org.gcube.smartgears.configuration.Mode; +import org.gcube.smartgears.context.application.ApplicationContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import javassist.util.proxy.MethodHandler; import javassist.util.proxy.ProxyFactory; import javassist.util.proxy.ProxyObject; -import org.gcube.common.scope.api.ScopeProvider; -import org.gcube.smartgears.annotations.ManagedBy; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ApplicationManagerProvider { +public abstract class ApplicationManagerProvider { private static Logger logger = LoggerFactory.getLogger(ApplicationManagerProvider.class); - static Map>> appManagerMap = new HashMap>>(); - - private static Map> proxyClassMap = Collections.synchronizedMap(new HashMap>()); + private Map> proxyClassMap = Collections.synchronizedMap(new HashMap>()); - private static Map classProxyObjetMap = Collections.synchronizedMap(new HashMap()); + private Map classProxyObjetMap = Collections.synchronizedMap(new HashMap()); - public static synchronized ApplicationManager get(){ - final Class applicationManagerClass = retrieveManagerClass(); - return get(applicationManagerClass); - } + protected static ApplicationManagerProvider instance; + + private static boolean initialized = false; + + protected static synchronized void init(ApplicationContext appcontext) { + if (!initialized) { + if (appcontext.container().configuration().mode()== Mode.offline) + instance = new OfflineProvider(); + else + instance = new OnlineProvider(); + } + } + public static synchronized ApplicationManager get(final Class applicationManagerClass){ + if (!initialized) throw new RuntimeException("ApplicationManagerProvider not yet initialized"); + Object obj = instance.getApplicationManagerObject(applicationManagerClass); + return applicationManagerClass.cast(obj); + } + + + private Object getApplicationManagerObject(Class applicationManagerClass) { Object obj; try { Class _class = getProxyClass(applicationManagerClass); if(classProxyObjetMap.containsKey(_class.getCanonicalName())){ obj = classProxyObjetMap.get(_class.getCanonicalName()); logger.trace("getting object {} from cache ",_class.getCanonicalName()); - return applicationManagerClass.cast(obj); + return obj; } else { obj = _class.newInstance(); classProxyObjetMap.put(_class.getCanonicalName(), obj); @@ -47,24 +64,18 @@ public class ApplicationManagerProvider { } catch (Exception e) { throw new RuntimeException("error creating proxy ", e); } - - MethodHandler handler = new MethodHandler() { - @Override - public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable { - if (ScopeProvider.instance.get()==null) throw new RuntimeException("error invoking application manager method, scope is not set in this thread"); - logger.debug("applicationManagerClass is {}",applicationManagerClass.getCanonicalName()); - Future appManagerFuture = appManagerMap.get(applicationManagerClass.getCanonicalName()).get(ScopeProvider.instance.get()); - logger.debug("appmanager future is null? {}", appManagerFuture==null); - logger.debug("thisMethod is null? {}", thisMethod==null); - return thisMethod.invoke(appManagerFuture.get(), args); - } - }; - ((ProxyObject)obj).setHandler(handler); - - return applicationManagerClass.cast(obj); + + ((ProxyObject)obj).setHandler(getMethdoHandler(applicationManagerClass)); + return obj; } - private static Class getProxyClass(Class applicationManagerClass){ + protected synchronized ApplicationManager get(){ + final Class applicationManagerClass = retrieveManagerClass(); + return get(applicationManagerClass); + } + + + private Class getProxyClass(Class applicationManagerClass){ if (proxyClassMap.containsKey(applicationManagerClass.getCanonicalName())){ logger.debug("getting proxy class {} for appManager from cache ",applicationManagerClass.getCanonicalName()); return proxyClassMap.get(applicationManagerClass.getCanonicalName()); @@ -78,7 +89,7 @@ public class ApplicationManagerProvider { } - private static Class retrieveManagerClass(){ + protected Class retrieveManagerClass(){ String classname = Thread.currentThread().getStackTrace()[3].getClassName(); logger.trace("managed servlet caller is {}",classname); ManagedBy annotation; @@ -95,4 +106,9 @@ public class ApplicationManagerProvider { return annotation.value(); } + + protected abstract Future retrieveFuture(Class applicationManagerClass); + protected abstract MethodHandler getMethdoHandler(Class applicationManagerClass); + protected abstract AppManagerObserver getObserver(); + } diff --git a/src/main/java/org/gcube/smartgears/ContextListener.java b/src/main/java/org/gcube/smartgears/ContextListener.java index ad5430f..343f66c 100644 --- a/src/main/java/org/gcube/smartgears/ContextListener.java +++ b/src/main/java/org/gcube/smartgears/ContextListener.java @@ -9,7 +9,9 @@ import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; +import org.gcube.smartgears.Constants; import org.gcube.smartgears.annotations.ManagedBy; +import org.gcube.smartgears.application.manager.AppManagerObserver; import org.gcube.smartgears.context.application.ApplicationContext; import org.reflections.Reflections; import org.reflections.scanners.SubTypesScanner; @@ -23,13 +25,12 @@ import org.slf4j.LoggerFactory; public class ContextListener implements ServletContextListener { private static Logger log = LoggerFactory.getLogger(ContextListener.class); - - RegisterApplicationManagerObserver observer; - + + AppManagerObserver observer; @Override public void contextInitialized(ServletContextEvent sce) { - + ApplicationContext context = (ApplicationContext) sce.getServletContext().getAttribute(Constants.context_attribute); if (context==null) { @@ -39,13 +40,15 @@ public class ContextListener implements ServletContextListener { log.info("configuring context provider for {}",context.name()); ContextProvider.set(context); - - + + retrieveAndRegisterManagers(context); } private void retrieveAndRegisterManagers(ApplicationContext context) { + ApplicationManagerProvider.init(context); + Collection urls = ClasspathHelper.forClassLoader(); urls.removeIf(url -> url.toString().endsWith(".so") || url.toString().endsWith(".zip") ); @@ -62,7 +65,11 @@ public class ContextListener implements ServletContextListener { managers.add(manageBy.value()); } if (managers.size()>0){ - observer = new RegisterApplicationManagerObserver(managers, context.configuration().startTokens()); + observer = ApplicationManagerProvider.instance.getObserver(); + observer.setStartingTokens(context.configuration().startTokens()); + observer.setApplicationManagerClasses(managers); + observer.register(); + context.events().subscribe(observer); } } diff --git a/src/main/java/org/gcube/smartgears/application/manager/AppManagerObserver.java b/src/main/java/org/gcube/smartgears/application/manager/AppManagerObserver.java new file mode 100644 index 0000000..13c1844 --- /dev/null +++ b/src/main/java/org/gcube/smartgears/application/manager/AppManagerObserver.java @@ -0,0 +1,25 @@ +package org.gcube.smartgears.application.manager; + +import java.util.Collection; +import java.util.Set; + +import org.gcube.smartgears.ApplicationManager; +import org.gcube.smartgears.context.application.ApplicationContext; + +public interface AppManagerObserver { + + void onRegistration(String parameter); + + void onRemove(String securityToken); + + void onStop(ApplicationContext appContext); + + void unregister(); + + void setStartingTokens(Collection startingTokens); + + void setApplicationManagerClasses(Set> managersClasses); + + public void register(); + +} \ No newline at end of file diff --git a/src/main/java/org/gcube/smartgears/application/manager/OfflineObserver.java b/src/main/java/org/gcube/smartgears/application/manager/OfflineObserver.java new file mode 100644 index 0000000..bd65043 --- /dev/null +++ b/src/main/java/org/gcube/smartgears/application/manager/OfflineObserver.java @@ -0,0 +1,121 @@ +package org.gcube.smartgears.application.manager; + +import java.util.Collection; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import org.gcube.common.events.Observes; +import org.gcube.common.events.Observes.Kind; +import org.gcube.smartgears.ApplicationManager; +import org.gcube.smartgears.Constants; +import org.gcube.smartgears.context.application.ApplicationContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class OfflineObserver implements AppManagerObserver { + + private static Logger log = LoggerFactory.getLogger(OfflineObserver.class); + + private static ExecutorService service = Executors.newCachedThreadPool(); + + private Set> managerClasses ; + + private OfflineProvider provider; + + public OfflineObserver(OfflineProvider provider) { + this.provider = provider; + } + + public void register() { + this.onRegistration(null); + } + + + @Override + @Observes(value=Constants.token_registered, kind=Kind.safe) + public synchronized void onRegistration(String parameter){ + log.info("offline registration"); + for (Class appManager: managerClasses){ + Future appManagerFuture = service.submit(new OfflineInitAppManager(appManager)); + provider.getAppmanagerMap().put(appManager.getCanonicalName(), appManagerFuture); + } + } + + @Override + @Observes(value=Constants.token_removed, kind=Kind.critical) + public synchronized void onRemove(final String securityToken){ + + } + + @Override + public synchronized void onStop(ApplicationContext appContext){ + provider.getAppmanagerMap().values().forEach( v -> { + try { + v.get().onShutdown(); + } catch (InterruptedException | ExecutionException e) { + log.warn("error shutting down appmanager "); + + } + }); + unregister(); + } + + @Override + public void unregister(){ + service.shutdownNow(); + } + + public class OfflineInitAppManager implements Callable{ + + private Class managerClass; + + public OfflineInitAppManager(Class managerClass){ + this.managerClass = managerClass; + } + + @Override + public ApplicationManager call() throws Exception { + ApplicationManager manager = managerClass.newInstance(); + try { + log.info("calling on onInit of {}",manager.getClass().getCanonicalName()); + manager.onInit(); + } catch (Exception e) { + log.warn("error on onInit of {}",manager.getClass().getCanonicalName(), e); + } + return manager; + } + } + + public class OfflineShutDownAppManager implements Runnable{ + + private Future appManager; + + public OfflineShutDownAppManager(Future appManager){ + this.appManager = appManager; + } + + @Override + public void run() { + try { + log.info("calling on ShutDown of {} ",appManager.getClass().getCanonicalName()); + appManager.get().onShutdown(); + } catch (Exception e) { + log.warn("error on onShutdown of {} ",appManager.getClass().getCanonicalName(), e); + } + } + } + + @Override + public void setStartingTokens(Collection startingTokens) { + } + + @Override + public void setApplicationManagerClasses(Set> managerClasses) { + this.managerClasses = managerClasses; + } +} + diff --git a/src/main/java/org/gcube/smartgears/application/manager/OfflineProvider.java b/src/main/java/org/gcube/smartgears/application/manager/OfflineProvider.java new file mode 100644 index 0000000..49f915f --- /dev/null +++ b/src/main/java/org/gcube/smartgears/application/manager/OfflineProvider.java @@ -0,0 +1,56 @@ +package org.gcube.smartgears.application.manager; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Future; + +import org.gcube.smartgears.ApplicationManager; +import org.gcube.smartgears.ApplicationManagerProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javassist.util.proxy.MethodHandler; + +public class OfflineProvider extends ApplicationManagerProvider { + + private static Logger logger = LoggerFactory.getLogger(OfflineProvider.class); + + private Map> appManagerMap = new HashMap>(); + + private OfflineObserver observer = new OfflineObserver(this); + + + @Override + protected Future retrieveFuture(Class applicationManagerClass) { + return appManagerMap.get(applicationManagerClass.getCanonicalName()); + } + + @Override + protected MethodHandler getMethdoHandler(Class applicationManagerClass) { + MethodHandler handler = new MethodHandler() { + @Override + public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable { + logger.debug("applicationManagerClass is {}",applicationManagerClass.getCanonicalName()); + Future appManagerFuture = retrieveFuture(applicationManagerClass); + logger.debug("appmanager future is null? {}", appManagerFuture==null); + logger.debug("thisMethod is null? {}", thisMethod==null); + return thisMethod.invoke(appManagerFuture.get(), args); + } + }; + return handler; + } + + public Map> getAppmanagerMap(){ + return appManagerMap; + } + + @Override + protected AppManagerObserver getObserver() { + return observer; + } + +} + + + diff --git a/src/main/java/org/gcube/smartgears/RegisterApplicationManagerObserver.java b/src/main/java/org/gcube/smartgears/application/manager/OnlineObserver.java similarity index 83% rename from src/main/java/org/gcube/smartgears/RegisterApplicationManagerObserver.java rename to src/main/java/org/gcube/smartgears/application/manager/OnlineObserver.java index 3c133df..7463379 100644 --- a/src/main/java/org/gcube/smartgears/RegisterApplicationManagerObserver.java +++ b/src/main/java/org/gcube/smartgears/application/manager/OnlineObserver.java @@ -1,4 +1,4 @@ -package org.gcube.smartgears; +package org.gcube.smartgears.application.manager; import static org.gcube.common.authorization.client.Constants.authorizationService; @@ -18,50 +18,55 @@ import org.gcube.common.authorization.library.provider.SecurityTokenProvider; import org.gcube.common.events.Observes; import org.gcube.common.events.Observes.Kind; import org.gcube.common.scope.api.ScopeProvider; +import org.gcube.smartgears.ApplicationManager; +import org.gcube.smartgears.Constants; import org.gcube.smartgears.context.application.ApplicationContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class RegisterApplicationManagerObserver { +public class OnlineObserver implements AppManagerObserver{ - private static Logger log = LoggerFactory.getLogger(RegisterApplicationManagerObserver.class); + private static Logger log = LoggerFactory.getLogger(OnlineObserver.class); private static ExecutorService service = Executors.newCachedThreadPool(); - - private Set> managersClass ; - private Map>> instanciatedManagerPerScope = new HashMap>>(); - public RegisterApplicationManagerObserver( - Set> managersClass, Collection startingTokens) { - super(); - this.managersClass = managersClass; - for (String startingToken : startingTokens ) - this.onRegistation(startingToken); - + private OnlineProvider provider; + + private Collection startingTokens; + + private Set> managersClasses; + + public OnlineObserver(OnlineProvider provider) { + this.provider = provider; + } + public void register() { + for (String startingToken : startingTokens ) + this.onRegistration(startingToken); + } + @Observes(value=Constants.token_registered, kind=Kind.safe) - public synchronized void onRegistation(final String securityToken){ + public synchronized void onRegistration(final String securityToken){ log.info("token registered called with token {}", securityToken); List> futureList = new ArrayList>(); try { final String context = authorizationService().get(securityToken).getContext(); - - for (Class appManager: managersClass){ + for (Class appManager: managersClasses){ Future appManagerFuture = service.submit(new InitAppManager(securityToken, context, appManager)); log.info("intializing app in context {} with token {} ",context, securityToken); futureList.add(appManagerFuture); - if (ApplicationManagerProvider.appManagerMap.containsKey(appManager.getCanonicalName())) - ApplicationManagerProvider.appManagerMap.get(appManager.getCanonicalName()).put(context, appManagerFuture); + if (provider.getAppmanagerMap().containsKey(appManager.getCanonicalName())) + provider.getAppmanagerMap().get(appManager.getCanonicalName()).put(context, appManagerFuture); else { Map> tokenFutureMap = new HashMap>(); tokenFutureMap.put(context, appManagerFuture); - ApplicationManagerProvider.appManagerMap.put(appManager.getCanonicalName(), tokenFutureMap); + provider.getAppmanagerMap().put(appManager.getCanonicalName(), tokenFutureMap); } } if (!futureList.isEmpty()) @@ -83,7 +88,7 @@ public class RegisterApplicationManagerObserver { for (Future appManager: instanciatedManagerPerScope.get(context)){ service.execute(new ShutDownAppManager(securityToken, context, appManager)); - ApplicationManagerProvider.appManagerMap.get(appManager).remove(context); + provider.getAppmanagerMap().get(appManager).remove(context); } instanciatedManagerPerScope.remove(context); @@ -192,4 +197,17 @@ public class RegisterApplicationManagerObserver { } } } + + @Override + public void setStartingTokens(Collection startingTokens) { + this.startingTokens = startingTokens; + + } + + @Override + public void setApplicationManagerClasses(Set> managersClasses) { + this.managersClasses = managersClasses; + } + + } diff --git a/src/main/java/org/gcube/smartgears/application/manager/OnlineProvider.java b/src/main/java/org/gcube/smartgears/application/manager/OnlineProvider.java new file mode 100644 index 0000000..91f034b --- /dev/null +++ b/src/main/java/org/gcube/smartgears/application/manager/OnlineProvider.java @@ -0,0 +1,56 @@ +package org.gcube.smartgears.application.manager; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Future; + +import org.gcube.common.scope.api.ScopeProvider; +import org.gcube.smartgears.ApplicationManager; +import org.gcube.smartgears.ApplicationManagerProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javassist.util.proxy.MethodHandler; + + + +public class OnlineProvider extends ApplicationManagerProvider { + + private static Logger logger = LoggerFactory.getLogger(OnlineProvider.class); + + private Map>> appManagerMap = new HashMap>>(); + + private OnlineObserver observer = new OnlineObserver(this); + + @Override + protected Future retrieveFuture(Class applicationManagerClass) { + return appManagerMap.get(applicationManagerClass.getCanonicalName()).get(ScopeProvider.instance.get()); + } + + @Override + protected MethodHandler getMethdoHandler(Class applicationManagerClass) { + MethodHandler handler = new MethodHandler() { + @Override + public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable { + if (ScopeProvider.instance.get()==null) throw new RuntimeException("error invoking application manager method, scope is not set in this thread"); + logger.debug("applicationManagerClass is {}",applicationManagerClass.getCanonicalName()); + Future appManagerFuture = retrieveFuture(applicationManagerClass); + logger.debug("appmanager future is null? {}", appManagerFuture==null); + logger.debug("thisMethod is null? {}", thisMethod==null); + return thisMethod.invoke(appManagerFuture.get(), args); + } + }; + return handler; + } + + public Map>> getAppmanagerMap(){ + return appManagerMap; + } + + @Override + protected AppManagerObserver getObserver() { + return observer; + } + +}