• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CeON / dataverse / 1371

28 Jun 2024 05:54AM UTC coverage: 25.453% (-0.05%) from 25.503%
1371

push

jenkins

web-flow
Closes #2474: Store the entity id in saml session once user logs in with the identity provider (#2486)

49 of 54 new or added lines in 4 files covered. (90.74%)

860 existing lines in 14 files now uncovered.

17805 of 69953 relevant lines covered (25.45%)

0.25 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.47
/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/timer/DataverseTimerServiceBean.java
1
package edu.harvard.iq.dataverse.timer;
2

3
import com.google.api.client.util.Preconditions;
4
import edu.harvard.iq.dataverse.DatasetDao;
5
import edu.harvard.iq.dataverse.featured.FeaturedDataverseServiceBean;
6
import edu.harvard.iq.dataverse.authorization.AuthenticationServiceBean;
7
import edu.harvard.iq.dataverse.datafile.FileIntegrityChecker;
8
import edu.harvard.iq.dataverse.datafile.pojo.FilesIntegrityReport;
9
import edu.harvard.iq.dataverse.dataset.DatasetCitationsCountUpdater;
10
import edu.harvard.iq.dataverse.engine.command.DataverseRequest;
11
import edu.harvard.iq.dataverse.harvest.client.HarvestTimerInfo;
12
import edu.harvard.iq.dataverse.harvest.client.HarvesterServiceBean;
13
import edu.harvard.iq.dataverse.harvest.client.HarvestingClientDao;
14
import edu.harvard.iq.dataverse.harvest.server.OAISetServiceBean;
15
import edu.harvard.iq.dataverse.persistence.dataset.Dataset;
16
import edu.harvard.iq.dataverse.persistence.harvest.HarvestingClient;
17
import edu.harvard.iq.dataverse.persistence.user.AuthenticatedUser;
18
import edu.harvard.iq.dataverse.search.index.IndexServiceBean;
19
import edu.harvard.iq.dataverse.settings.SettingsServiceBean;
20
import edu.harvard.iq.dataverse.settings.SettingsServiceBean.Key;
21
import edu.harvard.iq.dataverse.util.SystemConfig;
22
import org.apache.commons.lang3.StringUtils;
23

24
import javax.annotation.PostConstruct;
25
import javax.annotation.Resource;
26
import javax.ejb.DependsOn;
27
import javax.ejb.EJB;
28
import javax.ejb.ScheduleExpression;
29
import javax.ejb.Singleton;
30
import javax.ejb.Startup;
31
import javax.ejb.Timeout;
32
import javax.ejb.Timer;
33
import javax.ejb.TimerConfig;
34
import javax.ejb.TransactionAttribute;
35
import javax.ejb.TransactionAttributeType;
36
import javax.inject.Inject;
37
import javax.servlet.http.HttpServletRequest;
38
import java.io.IOException;
39
import java.io.Serializable;
40
import java.net.InetAddress;
41
import java.net.UnknownHostException;
42
import java.util.Calendar;
43
import java.util.Date;
44
import java.util.Iterator;
45
import java.util.List;
46
import java.util.logging.Level;
47
import java.util.logging.Logger;
48

49

50
/**
51
 * This is a largely intact DVN3 implementation.
52
 * original
53
 *
54
 * @author roberttreacy
55
 * ported by
56
 * @author Leonid Andreev
57
 */
58
@Singleton
59
@Startup
60
@DependsOn("StartupFlywayMigrator")
UNCOV
61
public class DataverseTimerServiceBean implements Serializable {
×
62
    private static final Logger logger = Logger.getLogger(DataverseTimerServiceBean.class.getCanonicalName());
1✔
63

64
    @Resource
65
    javax.ejb.TimerService timerService;
66

67
    @EJB
68
    HarvesterServiceBean harvesterService;
69
    @EJB
70
    HarvestingClientDao harvestingClientService;
71
    @EJB
72
    AuthenticationServiceBean authSvc;
73
    @EJB
74
    OAISetServiceBean oaiSetService;
75
    @EJB
76
    SystemConfig systemConfig;
77
    @Inject
78
    SettingsServiceBean settingsService;
79
    @EJB
80
    FileIntegrityChecker fileIntegrityChecker;
81
    @Inject
82
    DatasetCitationsCountUpdater datasetCitationsCountUpdater;
83
    @Inject
84
    FeaturedDataverseServiceBean featuredDataverseServiceBean;
85

86
    @Inject
87
    DatasetDao datasetDao;
88
    
89
    @Inject
90
    IndexServiceBean indexServiceBean;
91

92
    @PostConstruct
93
    public void init() {
94
        logger.info("PostConstruct timer check.");
×
95

UNCOV
96
        if (systemConfig.isReadonlyMode()) {
×
UNCOV
97
            logger.info("Timers not allowed in readonly mode");
×
98
            return;
×
99
        }
100

101
        if (systemConfig.isTimerServer()) {
×
UNCOV
102
            logger.info("I am the dedicated timer server. Initializing mother timer.");
×
103

UNCOV
104
            removeAllTimers();
×
105
            // create mother timer:
UNCOV
106
            createMotherTimer();
×
107
            // And the export timer (there is only one)
108
            createExportTimer();
×
109

110
            createIntegrityCheckTimer();
×
UNCOV
111
            createCitationCountUpdateTimer();
×
UNCOV
112
            createFeaturedDataversesSortingUpdateTimer();
×
113
            
UNCOV
114
            createReindexAfterEmbargoTimer();
×
115

116
        } else {
UNCOV
117
            logger.info("Skipping timer server init (I am not the dedicated timer server)");
×
118
        }
UNCOV
119
    }
×
120

121
    private void createReindexAfterEmbargoTimer() {
UNCOV
122
        String cronExpression = settingsService.getValueForKey(Key.ReindexAfterEmbargoTimerExpression);
×
123

124
        if (StringUtils.isNotBlank(cronExpression)) {
×
125
            ScheduleExpression expression = cronToScheduleExpression(cronExpression);
×
126

127
            TimerConfig timerConfig = new TimerConfig();
×
128
            timerConfig.setPersistent(false);
×
129
            timerConfig.setInfo(new AfterEmbargoReindexTimerInfo());
×
UNCOV
130
            Timer timer = timerService.createCalendarTimer(expression, timerConfig);
×
131
            logger.info("ReindexAfterEmbargoTimerExpression: timer created, initial expiration: " + timer.getNextTimeout());
×
UNCOV
132
        } else {
×
UNCOV
133
            logger.info("ReindexAfterEmbargoTimerExpression is empty. Skipping creation of timer.");
×
134
        }
135
    }
×
136

137
    public void createTimer(Date initialExpiration, long intervalDuration, Serializable info) {
138
        try {
139
            logger.log(Level.INFO, "Creating timer on " + InetAddress.getLocalHost().getCanonicalHostName());
×
140
        } catch (UnknownHostException ex) {
×
UNCOV
141
            Logger.getLogger(DataverseTimerServiceBean.class.getName()).log(Level.SEVERE, null, ex);
×
UNCOV
142
        }
×
UNCOV
143
        timerService.createIntervalTimer(initialExpiration, intervalDuration, new TimerConfig(info, false));
×
UNCOV
144
    }
×
145

146
    /**
147
     * This method is called whenever an EJB Timer goes off.
148
     * Check to see if this is a Harvest Timer, and if it is
149
     * Run the harvest for the given (scheduled) dataverse
150
     *
151
     * @param timer
152
     */
153
    @Timeout
154
    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
155
    public void handleTimeout(Timer timer) {
156
        // We have to put all the code in a try/catch block because
157
        // if an exception is thrown from this method, Glassfish will automatically
158
        // call the method a second time. (The minimum number of re-tries for a Timer method is 1)
UNCOV
159
        if (systemConfig.isReadonlyMode()) {
×
160
            Logger.getLogger(DataverseTimerServiceBean.class.getName()).log(Level.WARNING, null, "handleTimeout() was called in readonly mode - skipping timeout handling");
×
UNCOV
161
            return;
×
162
        }
163

UNCOV
164
        if (!systemConfig.isTimerServer()) {
×
165
            //logger.info("I am not the timer server! - bailing out of handleTimeout()");
166
            Logger.getLogger(DataverseTimerServiceBean.class.getName()).log(Level.WARNING, null, "I am not the timer server! - but handleTimeout() got called. Please investigate!");
×
167
        }
168

169
        try {
UNCOV
170
            logger.log(Level.INFO, "Handling timeout on " + InetAddress.getLocalHost().getCanonicalHostName());
×
171
        } catch (UnknownHostException ex) {
×
172
            Logger.getLogger(DataverseTimerServiceBean.class.getName()).log(Level.SEVERE, null, ex);
×
173
        }
×
174

175
        if (timer.getInfo() instanceof MotherTimerInfo) {
×
176
            logger.info("Behold! I am the Master Timer, king of all timers! I'm here to create all the lesser timers!");
×
177
            removeHarvestTimers();
×
178
            for (HarvestingClient client : harvestingClientService.getAllHarvestingClients()) {
×
UNCOV
179
                createHarvestTimer(client);
×
UNCOV
180
            }
×
181
        } else if (timer.getInfo() instanceof HarvestTimerInfo) {
×
UNCOV
182
            HarvestTimerInfo info = (HarvestTimerInfo) timer.getInfo();
×
183
            try {
184

UNCOV
185
                logger.log(Level.INFO, "running a harvesting client: id=" + info.getHarvestingClientId());
×
186
                // Timer batch jobs are run by the main Admin user.
187
                // TODO: revisit how we retrieve the superuser here.
188
                // Should it be configurable somewhere, which superuser
189
                // runs these jobs? Should there be a central mechanism for obtaining
190
                // the "major", builtin superuser for this Dataverse instance?
191
                // -- L.A. 4.5, Aug. 2016
UNCOV
192
                AuthenticatedUser adminUser = authSvc.getAdminUser(); // getAuthenticatedUser("admin");
×
193
                if (adminUser == null) {
×
194
                    logger.info("Scheduled harvest: failed to locate the admin user! Exiting.");
×
195
                    throw new IOException("Scheduled harvest: failed to locate the admin user");
×
196
                }
197
                logger.info("found admin user " + adminUser.getName());
×
UNCOV
198
                DataverseRequest dataverseRequest = new DataverseRequest(adminUser, (HttpServletRequest) null);
×
UNCOV
199
                harvesterService.doHarvest(dataverseRequest, info.getHarvestingClientId());
×
200

UNCOV
201
            } catch (Throwable e) {
×
202
                // Harvester Service should be handling any error notifications,
203
                // if/when things go wrong.
204
                // (TODO: -- verify this logic; harvesterService may still be able
205
                // to throw an IOException, if it could not run the harvest at all,
206
                // or could not for whatever reason modify the database record...
207
                // in this case we should, probably, log the error and try to send
208
                // a mail notification. -- L.A. 4.4)
209
                //dataverseService.setHarvestResult(info.getHarvestingDataverseId(), harvesterService.HARVEST_RESULT_FAILED);
210
                //mailService.sendHarvestErrorNotification(dataverseService.find().getSystemEmail(), dataverseService.find().getName());
211
                logException(e, logger);
×
212
            }
×
UNCOV
213
        } else if (timer.getInfo() instanceof ExportTimerInfo) {
×
214
            try {
215
                ExportTimerInfo info = (ExportTimerInfo) timer.getInfo();
×
216
                logger.info("Timer Service: Running a scheduled export job.");
×
217

218
                // and update all oai sets:
219
                oaiSetService.exportAllSets();
×
220
            } catch (Throwable e) {
×
UNCOV
221
                logException(e, logger);
×
222
            }
×
223
        } else if (timer.getInfo() instanceof FilesIntegrityCheckTimerInfo) {
×
224
            FilesIntegrityReport report = fileIntegrityChecker.checkFilesIntegrity();
×
225

226
            logger.info(report.getSummaryInfo());
×
UNCOV
227
        } else if (timer.getInfo() instanceof CitationCountUpdateTimerInfo) {
×
UNCOV
228
            datasetCitationsCountUpdater.updateCitationCount();
×
229
        } else if (timer.getInfo() instanceof AfterEmbargoReindexTimerInfo) {
×
UNCOV
230
            reindexAfterEmbargo();
×
UNCOV
231
        } else if (timer.getInfo() instanceof FeaturedDataversesSortingUpdateTimerInfo) {
×
232
            featuredDataverseServiceBean.refreshFeaturedDataversesAutomaticSorting();
×
233
        }
234

235
    }
×
236

237
    private void reindexAfterEmbargo() {
UNCOV
238
        List<Dataset> datasetsAfterEmbargo = datasetDao.findNotIndexedAfterEmbargo();
×
239
        for (Dataset dataset:datasetsAfterEmbargo) {
×
UNCOV
240
            indexServiceBean.indexDataset(dataset, true);
×
241
        }
×
UNCOV
242
    }
×
243

244
    public void removeAllTimers() {
245
        logger.info("Removing ALL existing timers.");
×
246

247
        int i = 0;
×
248

UNCOV
249
        for (Iterator it = timerService.getTimers().iterator(); it.hasNext(); ) {
×
250

251
            Timer timer = (Timer) it.next();
×
252

253
            logger.info("Removing timer " + i + ";");
×
UNCOV
254
            timer.cancel();
×
255

UNCOV
256
            i++;
×
UNCOV
257
        }
×
UNCOV
258
        logger.info("Done!");
×
UNCOV
259
    }
×
260

261
    public void removeHarvestTimers() {
262
        // Remove all the harvest timers, if exist:
263
        //
264
        // (the logging messages below are set to level INFO; it's ok,
265
        // since this code is only called on startup of the application,
266
        // and it may be useful to know what existing timers were encountered).
267

268
        logger.log(Level.INFO, "Removing existing harvest timers..");
×
269

270
        int i = 1;
×
271
        for (Iterator it = timerService.getTimers().iterator(); it.hasNext(); ) {
×
272

UNCOV
273
            Timer timer = (Timer) it.next();
×
UNCOV
274
            logger.log(Level.INFO, "HarvesterService: checking timer " + i);
×
275

276
            if (timer.getInfo() instanceof HarvestTimerInfo) {
×
277
                logger.log(Level.INFO, "HarvesterService: timer " + i + " is a harvesting one; removing.");
×
UNCOV
278
                timer.cancel();
×
279
            }
280

281
            i++;
×
282
        }
×
283
    }
×
284

285
    public void createMotherTimer() {
286
        MotherTimerInfo info = new MotherTimerInfo();
×
287
        Calendar initExpiration = Calendar.getInstance();
×
288
        long intervalDuration = 60 * 60 * 1000; // every hour
×
289
        initExpiration.set(Calendar.MINUTE, 50);
×
UNCOV
290
        initExpiration.set(Calendar.SECOND, 0);
×
291

292
        Date initExpirationDate = initExpiration.getTime();
×
293
        Date currTime = new Date();
×
294
        if (initExpirationDate.before(currTime)) {
×
UNCOV
295
            initExpirationDate.setTime(initExpiration.getTimeInMillis() + intervalDuration);
×
296
        }
297

298
        logger.info("Setting the \"Mother Timer\", initial expiration: " + initExpirationDate);
×
299
        createTimer(initExpirationDate, intervalDuration, info);
×
300
    }
×
301

302
    public void createHarvestTimer(HarvestingClient harvestingClient) {
303

304
        if (harvestingClient.isScheduled()) {
×
305
            long intervalDuration = 0;
×
UNCOV
306
            Calendar initExpiration = Calendar.getInstance();
×
307
            initExpiration.set(Calendar.MINUTE, 0);
×
308
            initExpiration.set(Calendar.SECOND, 0);
×
309
            if (harvestingClient.getSchedulePeriod().equals(HarvestingClient.SCHEDULE_PERIOD_DAILY)) {
×
310
                intervalDuration = 1000 * 60 * 60 * 24;
×
UNCOV
311
                initExpiration.set(Calendar.HOUR_OF_DAY, harvestingClient.getScheduleHourOfDay());
×
312

313
            } else if (harvestingClient.getSchedulePeriod().equals(HarvestingClient.SCHEDULE_PERIOD_WEEKLY)) {
×
314
                intervalDuration = 1000 * 60 * 60 * 24 * 7;
×
UNCOV
315
                initExpiration.set(Calendar.HOUR_OF_DAY, harvestingClient.getScheduleHourOfDay());
×
316
                initExpiration.set(Calendar.DAY_OF_WEEK, harvestingClient.getScheduleDayOfWeek() + 1); //(saved as zero-based array but Calendar is one-based.)
×
317

318
            } else {
319
                logger.log(Level.WARNING, "Could not set timer for harvesting client id=" + harvestingClient.getId() + ", unknown schedule period: " + harvestingClient.getSchedulePeriod());
×
UNCOV
320
                return;
×
321
            }
322
            Date initExpirationDate = initExpiration.getTime();
×
UNCOV
323
            Date currTime = new Date();
×
324
            if (initExpirationDate.before(currTime)) {
×
UNCOV
325
                initExpirationDate.setTime(initExpiration.getTimeInMillis() + intervalDuration);
×
326
            }
UNCOV
327
            logger.log(Level.INFO, "Setting timer for harvesting client " + harvestingClient.getName() + ", initial expiration: " + initExpirationDate);
×
UNCOV
328
            createTimer(initExpirationDate, intervalDuration, new HarvestTimerInfo(harvestingClient.getId()));
×
329
        }
330
    }
×
331

332
    public void removeHarvestTimer(HarvestingClient harvestingClient) {
333
        // Clear dataverse timer, if one exists
334
        try {
335
            logger.log(Level.INFO, "Removing harvest timer on " + InetAddress.getLocalHost().getCanonicalHostName());
×
336
        } catch (UnknownHostException ex) {
×
337
            Logger.getLogger(DataverseTimerServiceBean.class.getName()).log(Level.SEVERE, null, ex);
×
338
        }
×
UNCOV
339
        for (Iterator it = timerService.getTimers().iterator(); it.hasNext(); ) {
×
UNCOV
340
            Timer timer = (Timer) it.next();
×
341
            if (timer.getInfo() instanceof HarvestTimerInfo) {
×
342
                HarvestTimerInfo info = (HarvestTimerInfo) timer.getInfo();
×
UNCOV
343
                if (info.getHarvestingClientId().equals(harvestingClient.getId())) {
×
UNCOV
344
                    timer.cancel();
×
345
                }
346
            }
347
        }
×
348
    }
×
349

350
    public void createExportTimer() {
UNCOV
351
        ExportTimerInfo info = new ExportTimerInfo();
×
UNCOV
352
        Calendar initExpiration = Calendar.getInstance();
×
353
        long intervalDuration = 24 * 60 * 60 * 1000; // every day
×
354
        initExpiration.set(Calendar.MINUTE, 0);
×
355
        initExpiration.set(Calendar.SECOND, 0);
×
356
        initExpiration.set(Calendar.HOUR_OF_DAY, 2); // 2AM, fixed.
×
357

358

359
        Date initExpirationDate = initExpiration.getTime();
×
360
        Date currTime = new Date();
×
361
        if (initExpirationDate.before(currTime)) {
×
UNCOV
362
            initExpirationDate.setTime(initExpiration.getTimeInMillis() + intervalDuration);
×
363
        }
364

UNCOV
365
        logger.info("Setting the Export Timer, initial expiration: " + initExpirationDate);
×
366
        createTimer(initExpirationDate, intervalDuration, info);
×
367
    }
×
368

369
    public void createIntegrityCheckTimer() {
370
        String cronExpression = settingsService.getValueForKey(Key.FilesIntegrityCheckTimerExpression);
×
371

372
        if (StringUtils.isNotBlank(cronExpression)) {
×
UNCOV
373
            ScheduleExpression expression = cronToScheduleExpression(cronExpression);
×
374

UNCOV
375
            TimerConfig timerConfig = new TimerConfig();
×
UNCOV
376
            timerConfig.setInfo(new FilesIntegrityCheckTimerInfo());
×
377
            timerConfig.setPersistent(false);
×
UNCOV
378
            timerService.createCalendarTimer(expression, timerConfig);
×
379
        }
380
    }
×
381

382
    public void createCitationCountUpdateTimer() {
383
        String cronExpression = settingsService.getValueForKey(Key.CitationCountUpdateTimerExpression);
×
384

385
        if (StringUtils.isNotBlank(cronExpression)) {
×
386
            ScheduleExpression expression = cronToScheduleExpression(cronExpression);
×
387

388
            TimerConfig timerConfig = new TimerConfig();
×
UNCOV
389
            timerConfig.setPersistent(false);
×
UNCOV
390
            timerConfig.setInfo(new CitationCountUpdateTimerInfo());
×
391
            Timer timer = timerService.createCalendarTimer(expression, timerConfig);
×
UNCOV
392
            logger.info("CitationCountUpdateTimerExpression: timer created, initial expiration: " + timer.getNextTimeout());
×
UNCOV
393
        } else {
×
UNCOV
394
            logger.info("CitationCountUpdateTimerExpression is empty. Skipping creation of timer.");
×
395
        }
396

UNCOV
397
    }
×
398

399
    public void createFeaturedDataversesSortingUpdateTimer() {
400
        String cronExpression = settingsService.getValueForKey(Key.FeaturedDataversesSortingUpdateTimerExpression);
×
401

402
        if (StringUtils.isNotBlank(cronExpression)) {
×
403
            ScheduleExpression expression = cronToScheduleExpression(cronExpression);
×
404

UNCOV
405
            TimerConfig timerConfig = new TimerConfig();
×
UNCOV
406
            timerConfig.setPersistent(false);
×
UNCOV
407
            timerConfig.setInfo(new FeaturedDataversesSortingUpdateTimerInfo());
×
408
            Timer timer = timerService.createCalendarTimer(expression, timerConfig);
×
409
            logger.info("FeaturedDataversesSortingUpdateTimerExpression: timer created, initial expiration: " + timer.getNextTimeout());
×
UNCOV
410
        } else {
×
411
            logger.info("FeaturedDataversesSortingUpdateTimerExpression is empty. Skipping creation of timer.");
×
412
        }
413

UNCOV
414
    }
×
415

416
    /* Utility methods: */
417
    private ScheduleExpression cronToScheduleExpression(String cronExpression) {
418
        final String[] parts = cronExpression.split(" ");
×
UNCOV
419
        Preconditions.checkArgument(parts.length == 5, "Invalid cron expression {} Expression should have 5 parts", cronExpression);
×
420

421
        return new ScheduleExpression()
×
422
            .minute(parts[0])
×
423
            .hour(parts[1])
×
424
            .dayOfMonth(parts[2])
×
UNCOV
425
            .month(parts[3])
×
UNCOV
426
            .dayOfWeek(parts[4]);
×
427
    }
428

429
    private void logException(Throwable e, Logger logger) {
430

UNCOV
431
        boolean cause = false;
×
UNCOV
432
        String fullMessage = "";
×
433
        do {
UNCOV
434
            String message = e.getClass().getName() + " " + e.getMessage();
×
UNCOV
435
            if (cause) {
×
UNCOV
436
                message = "\nCaused By Exception.................... " + e.getClass().getName() + " " + e.getMessage();
×
437
            }
UNCOV
438
            StackTraceElement[] ste = e.getStackTrace();
×
UNCOV
439
            message += "\nStackTrace: \n";
×
UNCOV
440
            for (int m = 0; m < ste.length; m++) {
×
UNCOV
441
                message += ste[m].toString() + "\n";
×
442
            }
UNCOV
443
            fullMessage += message;
×
UNCOV
444
            cause = true;
×
UNCOV
445
        } while ((e = e.getCause()) != null);
×
UNCOV
446
        logger.severe(fullMessage);
×
UNCOV
447
    }
×
448

449
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc