2016-11-25 15:42:29 +01:00
|
|
|
package org.gcube.smartgears.handlers.application.lifecycle;
|
|
|
|
|
|
|
|
import static org.gcube.common.events.Observes.Kind.resilient;
|
|
|
|
import static org.gcube.smartgears.Constants.profile_management;
|
|
|
|
import static org.gcube.smartgears.handlers.ProfileEvents.addToContext;
|
|
|
|
import static org.gcube.smartgears.handlers.ProfileEvents.changed;
|
|
|
|
import static org.gcube.smartgears.handlers.ProfileEvents.removeFromContext;
|
|
|
|
import static org.gcube.smartgears.lifecycle.application.ApplicationLifecycle.activation;
|
|
|
|
import static org.gcube.smartgears.lifecycle.application.ApplicationLifecycle.failure;
|
|
|
|
import static org.gcube.smartgears.lifecycle.application.ApplicationLifecycle.stop;
|
|
|
|
|
|
|
|
import java.util.Collections;
|
2022-06-10 17:08:44 +02:00
|
|
|
import java.util.List;
|
2016-11-25 15:42:29 +01:00
|
|
|
import java.util.concurrent.ScheduledFuture;
|
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
|
|
import org.gcube.common.events.Observes;
|
|
|
|
import org.gcube.common.events.Observes.Kind;
|
|
|
|
import org.gcube.smartgears.Constants;
|
2022-02-07 09:44:31 +01:00
|
|
|
import org.gcube.smartgears.configuration.Mode;
|
2022-06-30 12:33:55 +02:00
|
|
|
import org.gcube.smartgears.context.Property;
|
2016-11-25 15:42:29 +01:00
|
|
|
import org.gcube.smartgears.context.application.ApplicationContext;
|
|
|
|
import org.gcube.smartgears.handlers.application.ApplicationLifecycleEvent;
|
|
|
|
import org.gcube.smartgears.handlers.application.ApplicationLifecycleHandler;
|
|
|
|
import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle;
|
|
|
|
import org.gcube.smartgears.lifecycle.application.ApplicationState;
|
|
|
|
import org.gcube.smartgears.lifecycle.container.ContainerLifecycle;
|
2022-06-10 17:08:44 +02:00
|
|
|
import org.gcube.smartgears.provider.ProviderFactory;
|
|
|
|
import org.gcube.smartgears.publishing.Publisher;
|
2016-11-25 15:42:29 +01:00
|
|
|
import org.gcube.smartgears.utils.Utils;
|
|
|
|
import org.slf4j.Logger;
|
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* Manages the resource profile of the application.
|
|
|
|
* <p>
|
|
|
|
*
|
|
|
|
* The manager:
|
|
|
|
*
|
|
|
|
* <ul>
|
|
|
|
* <li>creates the profile when the application starts for the first time;
|
|
|
|
* <li>loads the profile when the application restarts;
|
|
|
|
* <li>publishes the profile when the application becomes active, and at any
|
|
|
|
* lifecycle change thereafter;
|
|
|
|
* <li>stores the profile locally after each publication;
|
|
|
|
* </ul>
|
|
|
|
*
|
|
|
|
* @author Fabio Simeoni
|
|
|
|
*/
|
2022-06-10 17:08:44 +02:00
|
|
|
public class ApplicationProfileManager extends ApplicationLifecycleHandler {
|
2016-11-25 15:42:29 +01:00
|
|
|
|
2022-06-10 17:08:44 +02:00
|
|
|
Logger log = LoggerFactory.getLogger(ApplicationProfileManager.class);
|
2016-11-25 15:42:29 +01:00
|
|
|
|
|
|
|
private ApplicationContext context;
|
|
|
|
private ScheduledFuture<?> periodicUpdates;
|
2022-06-30 12:33:55 +02:00
|
|
|
private static final String PUBLISHED_PROP = "published";
|
2022-06-10 17:08:44 +02:00
|
|
|
private List<Publisher> publishers = ProviderFactory.provider().publishers();
|
|
|
|
|
|
|
|
|
2016-11-25 15:42:29 +01:00
|
|
|
@Override
|
|
|
|
public void onStart(ApplicationLifecycleEvent.Start e) {
|
|
|
|
|
|
|
|
context = e.context();
|
|
|
|
|
|
|
|
activated();
|
2022-06-10 17:08:44 +02:00
|
|
|
|
2016-11-25 15:42:29 +01:00
|
|
|
// note we don't fire profile events, but wait for the final startup
|
|
|
|
// outcome which
|
|
|
|
// will result in a state change. only then we publish and store the
|
|
|
|
// profile
|
|
|
|
// this avoids the redundancy and performance penalty of storing and
|
|
|
|
// publishing multiple
|
|
|
|
// times in rapid succession (which would be correct). Revise if proves
|
|
|
|
// problematic in corner
|
|
|
|
// cases.
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void activated(){
|
2022-06-30 12:33:55 +02:00
|
|
|
|
2022-06-10 17:08:44 +02:00
|
|
|
publishers = context.container().configuration().mode()!=Mode.offline?
|
|
|
|
ProviderFactory.provider().publishers():
|
|
|
|
Collections.emptyList();
|
2023-02-01 17:18:13 +01:00
|
|
|
|
|
|
|
registerObservers();
|
|
|
|
schedulePeriodicUpdates();
|
2016-11-25 15:42:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// helpers
|
|
|
|
private void registerObservers() {
|
|
|
|
|
|
|
|
context.events().subscribe(new Object() {
|
|
|
|
|
|
|
|
@Observes({ activation, stop, failure })
|
|
|
|
void onChanged(ApplicationLifecycle lc) {
|
|
|
|
|
|
|
|
log.debug("moving app {} to {}",context.name(), lc.state().remoteForm());
|
|
|
|
|
|
|
|
// since we do not know the observers, they will deal with
|
|
|
|
// failures and their consequences
|
|
|
|
// any that comes back will be logged in this event thread
|
2022-06-10 17:08:44 +02:00
|
|
|
context.events().fire(context, changed);
|
2016-11-25 15:42:29 +01:00
|
|
|
}
|
|
|
|
|
2022-06-10 17:08:44 +02:00
|
|
|
/*
|
2016-11-25 15:42:29 +01:00
|
|
|
@Observes(value = published)
|
|
|
|
void shareAfterPublish(GCoreEndpoint profile) {
|
|
|
|
|
|
|
|
share(profile); // publish may produce a new profile instance
|
|
|
|
|
2022-06-10 17:08:44 +02:00
|
|
|
}*/
|
2016-11-25 15:42:29 +01:00
|
|
|
|
|
|
|
@Observes(value = changed, kind = Kind.safe)
|
2022-06-10 17:08:44 +02:00
|
|
|
void publishAfterChange(ApplicationContext context) {
|
2016-11-25 15:42:29 +01:00
|
|
|
|
|
|
|
|
|
|
|
//if we've failed before first publication do not try to publish
|
|
|
|
//(we may well have failed there)
|
2022-07-04 16:31:15 +02:00
|
|
|
if (!context.properties().contains(PUBLISHED_PROP)) {
|
2022-06-30 12:33:55 +02:00
|
|
|
log.info("publishing application for the first time");
|
|
|
|
context.properties().add(new Property(PUBLISHED_PROP, true));
|
|
|
|
if (context.lifecycle().state() != ApplicationState.failed) {
|
|
|
|
publishers.forEach(p -> {
|
|
|
|
try {
|
|
|
|
p.create(context,
|
2023-01-19 16:16:14 +01:00
|
|
|
context.container().authorizationProvider().getContexts());
|
2022-06-30 12:33:55 +02:00
|
|
|
}catch (Exception e) {
|
|
|
|
log.error("cannot publish {} for first time with publisher type {} (see details)",context.name(), p.getClass().getCanonicalName(), e);
|
|
|
|
}
|
|
|
|
});
|
2016-11-25 15:42:29 +01:00
|
|
|
}
|
2022-06-30 12:33:55 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
publishers.forEach(p -> {
|
|
|
|
try {
|
|
|
|
p.update(context);
|
|
|
|
}catch (Exception e) {
|
|
|
|
log.error("cannot publish {} with publisher type {} (see details)",context.name(), p.getClass().getCanonicalName(), e);
|
|
|
|
}
|
|
|
|
});
|
2016-11-25 15:42:29 +01:00
|
|
|
|
|
|
|
}
|
2022-07-14 11:56:32 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
//registering ContextObserver in container HUB
|
|
|
|
context.container().events().subscribe(new Object() {
|
2016-11-25 15:42:29 +01:00
|
|
|
|
|
|
|
@Observes(value = addToContext)
|
2022-06-10 17:08:44 +02:00
|
|
|
void addTo(String scope) {
|
2022-07-14 11:56:32 +02:00
|
|
|
log.info("add_to_context event arrived in app {}", context.name());
|
2022-06-10 17:08:44 +02:00
|
|
|
for (Publisher publisher: publishers)
|
|
|
|
try {
|
|
|
|
log.debug("publishing application in context {}", scope);
|
2022-06-28 13:28:16 +02:00
|
|
|
publisher.create(context,
|
2022-06-10 17:08:44 +02:00
|
|
|
Collections.singleton(scope));
|
|
|
|
|
|
|
|
}catch (Exception e) {
|
|
|
|
|
|
|
|
log.error("cannot add context {} with publisher type {} (see details)",scope, publisher.getClass().getCanonicalName(), e);
|
|
|
|
|
|
|
|
// since we've failed no published event is fired and profile
|
|
|
|
// will not be stored.
|
|
|
|
// we do it manually to ensure we leave some local trace of the
|
|
|
|
// changed profile.
|
|
|
|
//TODO: CHECK --- store(profile);
|
|
|
|
}
|
2016-11-25 15:42:29 +01:00
|
|
|
|
|
|
|
}
|
2022-06-10 17:08:44 +02:00
|
|
|
|
2016-11-25 15:42:29 +01:00
|
|
|
@Observes(value = removeFromContext)
|
2022-06-10 17:08:44 +02:00
|
|
|
void removeFrom(String scope) {
|
2022-07-14 11:56:32 +02:00
|
|
|
log.info("remove_from_context event arrived in app {}", context.name());
|
2022-06-10 17:08:44 +02:00
|
|
|
for (Publisher publisher: publishers)
|
2022-06-30 12:33:55 +02:00
|
|
|
try {
|
|
|
|
log.debug("unpublishing application from scope {}", scope);
|
|
|
|
publisher.remove(context,
|
|
|
|
Collections.singleton(scope));
|
|
|
|
}catch (Exception e) {
|
|
|
|
|
|
|
|
log.error("cannot remove scope {} with publisher type {} (see details)",scope, publisher.getClass().getCanonicalName(), e);
|
|
|
|
|
|
|
|
// since we've failed no published event is fired and profile
|
|
|
|
// will not be stored.
|
|
|
|
// we do it manually to ensure we leave some local trace of the
|
|
|
|
// changed profile.
|
|
|
|
//TODO: CHECK --- store(profile);
|
|
|
|
}
|
2016-11-25 15:42:29 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
});
|
2022-07-14 11:56:32 +02:00
|
|
|
|
2016-11-25 15:42:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private void schedulePeriodicUpdates() {
|
|
|
|
|
|
|
|
// register to cancel updates
|
|
|
|
context.events().subscribe(
|
|
|
|
|
|
|
|
new Object() {
|
|
|
|
|
|
|
|
// we register it in response to lifecycle events so that we can stop and resume along with application
|
|
|
|
@Observes(value = { activation }, kind = resilient)
|
|
|
|
synchronized void restartPeriodicUpdates(ApplicationLifecycle lc) {
|
|
|
|
|
|
|
|
//already running
|
|
|
|
if (periodicUpdates!=null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (lc.state()==ApplicationState.active)
|
|
|
|
log.info("scheduling periodic updates of application {} profile", context.name());
|
|
|
|
|
|
|
|
else
|
|
|
|
log.info("resuming periodic updates of application {} profile", context.name());
|
|
|
|
|
|
|
|
final Runnable updateTask = new Runnable() {
|
|
|
|
public void run() {
|
|
|
|
//if handling of event generates failures these will be reported
|
|
|
|
//for resilience we do not fail the application
|
2022-06-10 17:08:44 +02:00
|
|
|
log.trace("firing change event on application {} ", context.name());
|
|
|
|
context.events().fire(context,changed);
|
2016-11-25 15:42:29 +01:00
|
|
|
}
|
|
|
|
};
|
2022-06-30 12:33:55 +02:00
|
|
|
|
2022-06-24 15:45:43 +02:00
|
|
|
periodicUpdates = Utils.scheduledServicePool.scheduleAtFixedRate(updateTask,
|
|
|
|
Constants.application_republish_frequency_in_minutes,
|
|
|
|
Constants.application_republish_frequency_in_minutes , TimeUnit.MINUTES);
|
2016-11-25 15:42:29 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Observes(value = { stop, failure }, kind = resilient)
|
|
|
|
synchronized void cancelPeriodicUpdates(ContainerLifecycle ignore) {
|
|
|
|
|
|
|
|
if (periodicUpdates != null){
|
|
|
|
log.trace("stopping periodic updates of application {} profile", context.name());
|
|
|
|
|
|
|
|
try {
|
|
|
|
periodicUpdates.cancel(true);
|
|
|
|
periodicUpdates=null;
|
|
|
|
}
|
|
|
|
catch(Exception e) {
|
2022-06-10 17:08:44 +02:00
|
|
|
log.warn("could not stop periodic updates of application {}", context.name(),e);
|
2016-11-25 15:42:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
2022-06-10 17:08:44 +02:00
|
|
|
|
|
|
|
|
2016-11-25 15:42:29 +01:00
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
return profile_management;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|