1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.opencastproject.terminationstate.impl;
22
23 import org.opencastproject.serviceregistry.api.ServiceRegistry;
24 import org.opencastproject.serviceregistry.api.ServiceRegistryException;
25 import org.opencastproject.terminationstate.api.AbstractJobTerminationStateService;
26 import org.opencastproject.terminationstate.api.TerminationStateService;
27 import org.opencastproject.util.NotFoundException;
28 import org.opencastproject.util.OsgiUtil;
29
30 import org.osgi.service.cm.ConfigurationException;
31 import org.osgi.service.component.ComponentContext;
32 import org.osgi.service.component.annotations.Activate;
33 import org.osgi.service.component.annotations.Component;
34 import org.osgi.service.component.annotations.Deactivate;
35 import org.osgi.service.component.annotations.Reference;
36 import org.quartz.Job;
37 import org.quartz.JobDetail;
38 import org.quartz.JobExecutionContext;
39 import org.quartz.JobExecutionException;
40 import org.quartz.Scheduler;
41 import org.quartz.SchedulerException;
42 import org.quartz.Trigger;
43 import org.quartz.TriggerUtils;
44 import org.quartz.impl.StdSchedulerFactory;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 import java.util.Dictionary;
49
50 @Component(
51 immediate = true,
52 service = TerminationStateService.class,
53 property = {
54 "service.description=Termination State Service",
55 "service.pid=org.opencastproject.terminationstate.impl.TerminationStateService",
56 "vendor.name=opencast",
57 "vendor.service=basic"
58 }
59 )
60 public final class TerminationStateServiceImpl extends AbstractJobTerminationStateService {
61 private static final Logger logger = LoggerFactory.getLogger(TerminationStateServiceImpl.class);
62
63 public static final String CONFIG_JOB_POLLING_PERIOD = "job.polling.period";
64 private static final int DEFAULT_JOB_POLLING_PERIOD = 300;
65
66 protected static final String SCHEDULE_GROUP = AbstractJobTerminationStateService.class.getSimpleName();
67 private static final String SCHEDULE_JOB_POLLING = "JobPolling";
68 protected static final String SCHEDULE_JOB_POLLING_TRIGGER = "TriggerJobPolling";
69 private static final String SCHEDULE_JOB_PARAM_PARENT = "parent";
70
71 private Scheduler scheduler;
72
73 private int jobPollingPeriod = DEFAULT_JOB_POLLING_PERIOD;
74
75
76 @Activate
77 protected void activate(ComponentContext componentContext) {
78 try {
79 configure(componentContext.getProperties());
80 } catch (ConfigurationException e) {
81 logger.error("Unable to read configuration, using defaults", e);
82 }
83
84 try {
85 scheduler = new StdSchedulerFactory().getScheduler();
86 } catch (SchedulerException e) {
87 logger.error("Cannot create quartz scheduler", e);
88 }
89 }
90
91
92 protected void configure(Dictionary config) throws ConfigurationException {
93 this.jobPollingPeriod = OsgiUtil.getOptCfgAsInt(config, CONFIG_JOB_POLLING_PERIOD)
94 .getOrElse(DEFAULT_JOB_POLLING_PERIOD);
95 }
96
97 @Override
98 public void setState(TerminationState state) {
99 super.setState(state);
100
101 if (getState() != TerminationState.NONE) {
102
103 try {
104 String host = getServiceRegistry().getRegistryHostname();
105 getServiceRegistry().setMaintenanceStatus(host, true);
106 } catch (ServiceRegistryException | NotFoundException e) {
107 logger.error("Cannot put this host into maintenance", e);
108 }
109 startJobPolling();
110 } else {
111
112 try {
113 String host = getServiceRegistry().getRegistryHostname();
114 getServiceRegistry().setMaintenanceStatus(host, false);
115 } catch (ServiceRegistryException | NotFoundException e) {
116 logger.error("Cannot take this host out of maintenance", e);
117 }
118 }
119 }
120
121 protected void startJobPolling() {
122 try {
123
124 final JobDetail job = new JobDetail(SCHEDULE_GROUP, SCHEDULE_JOB_POLLING, CheckTerminationState.class);
125 job.getJobDataMap().put(SCHEDULE_JOB_PARAM_PARENT, this);
126 final Trigger trigger = TriggerUtils.makeSecondlyTrigger(jobPollingPeriod);
127 trigger.setGroup(SCHEDULE_GROUP);
128 trigger.setName(SCHEDULE_JOB_POLLING_TRIGGER);
129 scheduler.scheduleJob(job, trigger);
130 scheduler.start();
131 logger.info("Started polling if jobs are complete");
132 } catch (org.quartz.SchedulerException e) {
133 throw new RuntimeException(e);
134 }
135 }
136
137 protected void stopJobPolling() {
138 try {
139 scheduler.deleteJob(SCHEDULE_GROUP, SCHEDULE_JOB_POLLING);
140 } catch (SchedulerException e) {
141
142 }
143 }
144
145 public static class CheckTerminationState implements Job {
146
147 @Override
148 public void execute(JobExecutionContext context) throws JobExecutionException {
149 TerminationStateServiceImpl parent
150 = (TerminationStateServiceImpl) context.getJobDetail().getJobDataMap().get(SCHEDULE_JOB_PARAM_PARENT);
151
152 if (parent.readyToTerminate()) {
153 logger.info("No jobs running, sent complete Lifecycle action");
154 parent.stopJobPolling();
155 } else if (parent.getState() == TerminationState.WAIT) {
156 logger.info("Jobs still running");
157 }
158 }
159 }
160
161
162
163
164 private void stop() {
165 try {
166 if (scheduler != null) {
167 this.scheduler.shutdown();
168 }
169 } catch (SchedulerException e) {
170 logger.error("Failed to stop scheduler", e);
171 }
172 }
173
174
175
176
177 @Deactivate
178 public void deactivate() {
179 stop();
180 }
181
182
183
184 protected void setScheduler(Scheduler scheduler) {
185 this.scheduler = scheduler;
186 }
187
188 @Reference
189 @Override
190 public void setServiceRegistry(ServiceRegistry serviceRegistry) {
191 super.setServiceRegistry(serviceRegistry);
192 }
193
194 }