View Javadoc
1   /**
2    * Copyright 2014 Internet2
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  /*
17   * @author mchyzer
18   * $Id: GrouperLoader.java,v 1.15 2009-11-02 03:50:50 mchyzer Exp $
19   */
20  package edu.internet2.middleware.grouper.app.loader;
21  
22  import java.io.File;
23  import java.io.FileWriter;
24  import java.io.IOException;
25  import java.util.Arrays;
26  import java.util.Date;
27  import java.util.GregorianCalendar;
28  import java.util.HashSet;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Properties;
32  import java.util.Set;
33  import java.util.regex.Matcher;
34  
35  import org.apache.commons.lang.StringUtils;
36  import org.apache.commons.lang.exception.ExceptionUtils;
37  import org.apache.commons.logging.Log;
38  import org.apache.ddlutils.PlatformFactory;
39  import org.hibernate.type.StringType;
40  import org.quartz.CronTrigger;
41  import org.quartz.JobBuilder;
42  import org.quartz.JobDetail;
43  import org.quartz.JobKey;
44  import org.quartz.Scheduler;
45  import org.quartz.SchedulerException;
46  import org.quartz.SchedulerFactory;
47  import org.quartz.SimpleScheduleBuilder;
48  import org.quartz.SimpleTrigger;
49  import org.quartz.Trigger;
50  import org.quartz.TriggerBuilder;
51  import org.quartz.TriggerKey;
52  import org.quartz.impl.StdSchedulerFactory;
53  import org.quartz.impl.matchers.GroupMatcher;
54  
55  import edu.internet2.middleware.grouper.Group;
56  import edu.internet2.middleware.grouper.GroupFinder;
57  import edu.internet2.middleware.grouper.GroupTypeFinder;
58  import edu.internet2.middleware.grouper.GrouperSession;
59  import edu.internet2.middleware.grouper.app.loader.db.GrouperLoaderDb;
60  import edu.internet2.middleware.grouper.app.loader.db.Hib3GrouperLoaderLog;
61  import edu.internet2.middleware.grouper.app.loader.ldap.LoaderLdapUtils;
62  import edu.internet2.middleware.grouper.attr.AttributeDef;
63  import edu.internet2.middleware.grouper.attr.AttributeDefName;
64  import edu.internet2.middleware.grouper.attr.assign.AttributeAssign;
65  import edu.internet2.middleware.grouper.attr.finder.AttributeDefFinder;
66  import edu.internet2.middleware.grouper.attr.finder.AttributeDefNameFinder;
67  import edu.internet2.middleware.grouper.audit.GrouperEngineBuiltin;
68  import edu.internet2.middleware.grouper.cfg.GrouperConfig;
69  import edu.internet2.middleware.grouper.cfg.GrouperHibernateConfig;
70  import edu.internet2.middleware.grouper.changeLog.ChangeLogConsumerBase;
71  import edu.internet2.middleware.grouper.client.ClientConfig;
72  import edu.internet2.middleware.grouper.client.ClientConfig.ClientGroupConfigBean;
73  import edu.internet2.middleware.grouper.ddl.GrouperDdlUtils;
74  import edu.internet2.middleware.grouper.hibernate.GrouperContext;
75  import edu.internet2.middleware.grouper.hibernate.GrouperTransaction;
76  import edu.internet2.middleware.grouper.hibernate.GrouperTransactionHandler;
77  import edu.internet2.middleware.grouper.hibernate.GrouperTransactionType;
78  import edu.internet2.middleware.grouper.hibernate.HibUtils;
79  import edu.internet2.middleware.grouper.hibernate.HibernateSession;
80  import edu.internet2.middleware.grouper.instrumentation.InstrumentationThread;
81  import edu.internet2.middleware.grouper.internal.dao.GrouperDAOException;
82  import edu.internet2.middleware.grouper.messaging.MessagingListenerBase;
83  import edu.internet2.middleware.grouper.misc.GrouperCheckConfig;
84  import edu.internet2.middleware.grouper.misc.GrouperStartup;
85  import edu.internet2.middleware.grouper.util.GrouperUtil;
86  import edu.internet2.middleware.morphString.Morph;
87  
88  
89  
90  /**
91   * main class to start the grouper loader
92   */
93  public class GrouperLoader {
94  
95    /**
96     * call this when exiting grouper if not the daemon which should stay running
97     */
98    public static void shutdownIfStarted() {
99      //stop quartz
100     try {
101       for (Scheduler scheduler : GrouperUtil.nonNull(GrouperLoader.schedulerFactory().getAllSchedulers())) {
102         scheduler.shutdown(false);
103       }
104     } catch (Exception e) {
105       throw new RuntimeException("error", e);
106     }
107 
108   }
109   
110   /**
111    * logger 
112    */
113   private static final Log LOG = GrouperUtil.getLog(GrouperLoader.class);
114 
115   /**
116    * @param args
117    */
118   public static void main(String[] args) {
119     
120     //set this and leave it...
121     GrouperContext.createNewDefaultContext(GrouperEngineBuiltin.LOADER, false, true);
122 
123     //printAllSupportDdlUtilsPlatforms();
124     GrouperStartup.startup();
125     GrouperStartup.waitForGrouperStartup();
126 
127     //make sure properties file is there
128     GrouperCheckConfig.checkResource("grouper-loader.properties");
129     
130     //make sure properties are there
131 //    GrouperCheckConfig.checkConfigProperties("grouper-loader.properties", 
132 //        "grouper-loader.example.properties");
133     
134     GrouperCheckConfig.checkGrouperLoaderConfigDbs();
135     GrouperCheckConfig.checkGrouperLoaderConsumers();
136     GrouperCheckConfig.checkGrouperLoaderOtherJobs();
137     
138     scheduleJobs();
139     
140     InstrumentationThread.startThread(GrouperContext.retrieveDefaultContext().getGrouperEngine(), null);
141     
142     GrouperDaemonSchedulerCheck.startDaemonSchedulerCheckThreadIfNeeded();
143     
144     // delay starting the scheduler until the end to make sure things that need to be unscheduled are taken care of first?
145     try {
146       schedulerFactory.getScheduler().start();
147     } catch (SchedulerException e) {
148       throw new RuntimeException(e);
149     }
150   }
151 
152   /**
153    * 
154    * @return the number of changes made
155    */
156   public static int scheduleJobs() {
157     
158     int changesMade = 0;
159     
160     //this will find all schedulable groups, and schedule them
161     changesMade += GrouperLoaderType.scheduleLoads();
162     
163     changesMade += GrouperLoaderType.scheduleAttributeLoads();
164     
165     changesMade += GrouperLoaderType.scheduleLdapLoads();
166 
167     changesMade += scheduleMaintenanceJobs();
168     changesMade += scheduleChangeLogJobs();
169     changesMade += scheduleMessagingListeners();
170     
171     changesMade += scheduleOtherJobs();
172     
173     //this will schedule ESB listener jobs if enabled
174     changesMade += scheduleEsbListenerJobs();
175     
176     if (schedulePspFullSyncJob()) {
177       changesMade++;
178     }
179     
180     return changesMade;
181   }
182 
183   /**
184    * print out all ddlutils platforms
185    */
186   public static void printAllSupportDdlUtilsPlatforms() {
187     String[] platforms = PlatformFactory.getSupportedPlatforms();
188     Arrays.sort(platforms);
189     for (String platform : platforms) {
190       System.out.print(platform + ", ");
191     }
192   }
193   
194   /**
195    * group attribute name of type of the loader, must match one of the enums in GrouperLoaderType.
196    * If there is a query, and it has "group_name" before "from", then defaults to SQL_GROUP_LIST
197    * else defaults to SQL_SIMPLE
198    */
199   public static final String GROUPER_LOADER_TYPE = "grouperLoaderType";
200   
201   /**
202    * grouper loader display name sync type. BASE_FOLDER_NAME or LEVELS
203    */
204   public static final String GROUPER_LOADER_DISPLAY_NAME_SYNC_TYPE = "grouperLoaderDisplayNameSyncType";
205   
206   /**
207    * folder name after which display names need to be synced between source and grouper
208    */
209   public static final String GROUPER_LOADER_DISPLAY_NAME_SYNC_BASE_FOLDER_NAME = "grouperLoaderDisplayNameSyncBaseFolderName";
210   
211   /**
212    * levels starting from the group after which display names need to be synced between source and grouper
213    */
214   public static final String GROUPER_LOADER_DISPLAY_NAME_SYNC_LEVELS = "grouperLoaderDisplayNameSyncLevels";
215 
216   /**
217    * groups to and with to restrict members (e.g. "and" with activeEmployees)
218    */
219   public static final String GROUPER_LOADER_AND_GROUPS = "grouperLoaderAndGroups";
220 
221   /**
222    * If you want the group (if not used from anywhere) or members deleted when 
223    * no longer in loader sql results, list the sql like name, e.g. stem1:stem2:%:%org
224    */
225   public static final String GROUPER_LOADER_GROUPS_LIKE = "grouperLoaderGroupsLike";
226 
227   /**
228    * optional group information for a group list query: e.g. to specify the display name of the
229    * group/stem when it is created
230    */
231   public static final String GROUPER_LOADER_GROUP_QUERY = "grouperLoaderGroupQuery";
232 
233   /**
234    * types to add to loaded groups
235    */
236   public static final String GROUPER_LOADER_GROUP_TYPES = "grouperLoaderGroupTypes";
237 
238   /**
239    * group attribute name of type of schedule, must match one of the enums in GrouperLoaderScheduleType.
240    * defaults to START_TO_START_INTERVAL if grouperLoaderQuartzCron is blank, else defaults to
241    * CRON
242    */
243   public static final String GROUPER_LOADER_SCHEDULE_TYPE = "grouperLoaderScheduleType";
244 
245   /**
246    * group attribute name of query, must have the required columns for the grouperLoaderType
247    */
248   public static final String GROUPER_LOADER_QUERY = "grouperLoaderQuery";
249 
250   /**
251    * group attribute name of quartz cron-like string to describe when the job should run
252    */
253   public static final String GROUPER_LOADER_QUARTZ_CRON = "grouperLoaderQuartzCron";
254 
255   /**
256    * group attribute name of the interval in seconds for a schedule type like START_TO_START_INTERVAL.
257    * defaults to 86400 (1 day)
258    */
259   public static final String GROUPER_LOADER_INTERVAL_SECONDS = "grouperLoaderIntervalSeconds";
260 
261   /**
262    * group attribute name of priority of job, optional, if not there, will be 5.  More is better.
263    * if the threadpool is full, then this priority will help the schedule pick which job should go next
264    */
265   public static final String GROUPER_LOADER_PRIORITY = "grouperLoaderPriority";
266 
267   /**
268    * group attribute name of the db connection where this query comes from.
269    * if the name is "grouper", then it will be the group db name.  defaults to "grouper" for sql type
270    * loaders
271    */
272   public static final String GROUPER_LOADER_DB_NAME = "grouperLoaderDbName";
273   
274   /**
275    * Type of loader, e.g. ATTR_SQL_SIMPLE
276    */
277   public static final String ATTRIBUTE_LOADER_TYPE = "attributeLoaderType";
278   
279   /**
280    * DB name in grouper-loader.properties or default grouper db if blank
281    */
282   public static final String ATTRIBUTE_LOADER_DB_NAME = "attributeLoaderDbName";
283   
284   /**
285    * Type of schedule.  Defaults to CRON if a cron schedule is entered, or START_TO_START_INTERVAL if an interval is entered
286    */
287   public static final String ATTRIBUTE_LOADER_SCHEDULE_TYPE = "attributeLoaderScheduleType";
288   
289   /**
290    * If a CRON schedule type, this is the cron setting string from the quartz product to run a job daily, hourly, weekly, etc.  e.g. daily at 7am: 0 0 7 * * ?
291    */
292   public static final String ATTRIBUTE_LOADER_QUARTZ_CRON = "attributeLoaderQuartzCron";
293 
294   /**
295    * If a START_TO_START_INTERVAL schedule type, this is the number of seconds between runs
296    */
297   public static final String ATTRIBUTE_LOADER_INTERVAL_SECONDS = "attributeLoaderIntervalSeconds";
298   
299   /**
300    * Quartz has a fixed threadpool (max configured in the grouper-loader.properties), and when the max is reached, then jobs are prioritized by this integer.  The higher the better, and the default if not set is 5.
301    */
302   public static final String ATTRIBUTE_LOADER_PRIORITY = "attributeLoaderPriority";
303 
304   /**
305    * If empty, then orphans will be left alone (for attributeDefName and attributeDefNameSets).  If %, then all orphans deleted.  If a SQL like string, then only ones in that like string not in loader will be deleted
306    */
307   public static final String ATTRIBUTE_LOADER_ATTRS_LIKE = "attributeLoaderAttrsLike";
308   
309   /**
310    * SQL query with at least some of the following columns: attr_name, attr_display_name, attr_description
311    */
312   public static final String ATTRIBUTE_LOADER_ATTR_QUERY = "attributeLoaderAttrQuery";
313   
314   /**
315    * SQL query with at least the following columns: if_has_attr_name, then_has_attr_name
316    */
317   public static final String ATTRIBUTE_LOADER_ATTR_SET_QUERY = "attributeLoaderAttrSetQuery";
318   
319   /**
320    * SQL query with at least the following column: action_name
321    */
322   public static final String ATTRIBUTE_LOADER_ACTION_QUERY = "attributeLoaderActionQuery";
323   
324   /**
325    * SQL query with at least the following columns: if_has_action_name, then_has_action_name
326    */
327   public static final String ATTRIBUTE_LOADER_ACTION_SET_QUERY = "attributeLoaderActionSetQuery";
328   
329   /**
330    * True means the group was loaded from loader
331    */
332   public static final String ATTRIBUTE_GROUPER_LOADER_METADATA_LOADED = "grouperLoaderMetadataLoaded";
333   
334   /**
335    * True means the group was loaded from loader
336    * TODO remove in 2.4
337    */
338   @Deprecated
339   public static final String ATTRIBUTE_GROUPER_LOADER_METADATA_LAODED = ATTRIBUTE_GROUPER_LOADER_METADATA_LOADED;
340 
341   /**
342    * Group id which is being populated from the loader
343    */
344   public static final String ATTRIBUTE_GROUPER_LOADER_METADATA_GROUP_ID = "grouperLoaderMetadataGroupId";
345   
346   /**
347    * Millis since 1970 that this group was fully processed
348    */
349   public static final String ATTRIBUTE_GROUPER_LOADER_METADATA_LAST_FULL_MILLIS = "grouperLoaderMetadataLastFullMillisSince1970";
350   
351   /**
352    * Millis since 1970 that this group was incrementally processed
353    */
354   public static final String ATTRIBUTE_GROUPER_LOADER_METADATA_LAST_INCREMENTAL_MILLIS = "grouperLoaderMetadataLastIncrementalMillisSince1970";
355 
356   /**
357    * summary like count of additions, updates and removals
358    */
359   public static final String ATTRIBUTE_GROUPER_LOADER_METADATA_LAST_SUMMARY = "grouperLoaderMetadataLastSummary";
360   
361   /**
362    * name of the loader metadata definition
363    */
364   public static final String LOADER_METADATA_VALUE_DEF = "loaderMetadata";
365 
366   /**
367    * scheduler factory singleton
368    */
369   private static SchedulerFactory schedulerFactory = null;
370 
371   /**
372    * lazy load (and start the scheduler) the scheduler factory
373    * @return the scheduler factory
374    */
375   public static SchedulerFactory schedulerFactory() {
376     if (schedulerFactory == null) {
377       
378       Properties props = new Properties();
379       for (String key : GrouperLoaderConfig.retrieveConfig().propertyNames()) {
380         if (key.startsWith("org.quartz.")) {
381           String value = GrouperLoaderConfig.retrieveConfig().propertyValueString(key);
382           if (key.startsWith("org.quartz.dataSource.myDS") 
383               && !(key.equals("org.quartz.dataSource.myDS.connectionProvider.class") && StringUtils.equals(value, GrouperQuartzConnectionProvider.class.getName()))) {
384             LOG.error("Quartz property filtered since uses Grouper datastore now! '" + key + "', value: '" + value + "'");
385           } else {
386             if (value == null) {
387               value = "";
388             }
389             props.put(key, value);
390           }
391         }
392       }
393       if (!StringUtils.equals("myDS", props.getProperty("org.quartz.jobStore.dataSource"))) {
394         LOG.error("Quartz datasource should be myDS! '" + props.getProperty("org.quartz.jobStore.dataSource") + "'");
395       }
396       if (StringUtils.isBlank(props.getProperty("org.quartz.jobStore.driverDelegateClass"))) {
397         String driverDelegate = GrouperDdlUtils.convertUrlToQuartzDriverDelegateClass();
398         if (!StringUtils.isBlank(driverDelegate)) {
399           props.put("org.quartz.jobStore.driverDelegateClass", driverDelegate);
400         }
401       }
402       try {
403         schedulerFactory = new StdSchedulerFactory(props);
404       } catch (SchedulerException se) {
405         throw new RuntimeException(se);
406       }
407     }
408     return schedulerFactory;
409   }
410   
411   /**
412    * schedule maintenance jobs
413    */
414   public static int scheduleMaintenanceJobs() {
415 
416     int changesMade = 0;
417     
418     if (scheduleLogCleanerJob()) {
419       changesMade++;
420     }
421     
422     if (scheduleDailyReportJob()) {
423       changesMade++;
424     }
425     if (scheduleEnabledDisabledJob()) {
426       changesMade++;
427     }
428     if (scheduleBuiltinMessagingDaemonJob()) {
429       changesMade++;
430     }
431     if (scheduleRulesJob()) {
432       changesMade++;
433     }
434     changesMade += scheduleGroupSyncJobs();
435 
436     return changesMade;
437   }
438 
439   /**
440    * schedule change log jobs
441    */
442   public static int scheduleChangeLogJobs() {
443     int changesMade = 0;
444     if (scheduleChangeLogTempToChangeLogJob()) {
445       changesMade++;
446     }
447     changesMade += scheduleChangeLogConsumers();
448     return changesMade;
449   }
450   
451   /**
452    * schedule maintenance job for moving records from change log to change log temp
453    */
454   public static boolean scheduleChangeLogTempToChangeLogJob() {
455 
456     String cronString = null;
457 
458     //this is a medium priority job
459     int priority = 5;
460 
461     //schedule the log delete job
462     try {
463       Scheduler scheduler = GrouperLoader.schedulerFactory().getScheduler();
464       String triggerName = "triggerChangeLog_grouperChangeLogTempToChangeLog";
465       
466       if (!GrouperLoaderConfig.retrieveConfig().propertyValueBoolean("changeLog.changeLogTempToChangeLog.enable", false)) {
467         LOG.warn("grouper-loader.properties key: changeLog.changeLogTempToChangeLog.enable is not " +
468           "filled in or false so the change log temp to change log daemon will not run");
469         return scheduler.unscheduleJob(TriggerKey.triggerKey(triggerName));
470       }
471       
472       cronString = GrouperLoaderConfig.retrieveConfig().propertyValueString("changeLog.changeLogTempToChangeLog.quartz.cron");
473 
474       if (StringUtils.isBlank(cronString)) {
475         cronString = "50 * * * * ?";
476         
477       }
478       
479       //at this point we have all the attributes and we know the required ones are there, and logged when 
480       //forbidden ones are there
481 
482       //the name of the job must be unique, so use the group name since one job per group (at this point)
483       JobDetail jobDetail = JobBuilder.newJob(GrouperLoaderJob.class)
484         .withIdentity(GrouperLoaderType.GROUPER_CHANGE_LOG_TEMP_TO_CHANGE_LOG)
485         .build();
486 
487       //schedule this job
488       GrouperLoaderScheduleType grouperLoaderScheduleType = GrouperLoaderScheduleType.CRON;
489 
490       Trigger trigger = grouperLoaderScheduleType.createTrigger(triggerName, priority, cronString, null);
491 
492       return scheduleJobIfNeeded(jobDetail, trigger);
493 
494 
495     } catch (Exception e) {
496       String errorMessage = "Could not schedule job: '" + GrouperLoaderType.GROUPER_CHANGE_LOG_TEMP_TO_CHANGE_LOG + "'";
497       LOG.error(errorMessage, e);
498       errorMessage += "\n" + ExceptionUtils.getFullStackTrace(e);
499       try {
500         //lets enter a log entry so it shows up as error in the db
501         Hib3GrouperLoaderLogib3GrouperLoaderLog.html#Hib3GrouperLoaderLog">Hib3GrouperLoaderLog hib3GrouploaderLog = new Hib3GrouperLoaderLog();
502         hib3GrouploaderLog.setHost(GrouperUtil.hostname());
503         hib3GrouploaderLog.setJobMessage(errorMessage);
504         hib3GrouploaderLog.setJobName(GrouperLoaderType.GROUPER_CHANGE_LOG_TEMP_TO_CHANGE_LOG);
505         hib3GrouploaderLog.setJobSchedulePriority(priority);
506         hib3GrouploaderLog.setJobScheduleQuartzCron(cronString);
507         hib3GrouploaderLog.setJobScheduleType(GrouperLoaderScheduleType.CRON.name());
508         hib3GrouploaderLog.setJobType(GrouperLoaderType.CHANGE_LOG.name());
509         hib3GrouploaderLog.setStatus(GrouperLoaderStatus.CONFIG_ERROR.name());
510         hib3GrouploaderLog.store();
511         
512       } catch (Exception e2) {
513         LOG.error("Problem logging to loader db log", e2);
514       }
515     }
516     return false;
517   }
518 
519   /**
520    * schedule change log consumer jobs
521    */
522   public static int scheduleChangeLogConsumers() {
523 
524     int changesMade = 0;
525     
526     //changeLog.consumer.ldappc.class = 
527     //changeLog.consumer.ldappc.quartz.cron
528     
529     //make sure sequences are ok
530     Map<String, String> consumerMap = GrouperLoaderConfig.retrieveConfig().propertiesMap( 
531         GrouperCheckConfig.grouperLoaderConsumerPattern);
532     
533     Set<String> changeLogJobNames = new HashSet<String>();
534     
535     int index = 0;
536     
537     while (consumerMap.size() > 0) {
538       
539       //get one
540       String consumerKey = consumerMap.keySet().iterator().next();
541       //get the database name
542       Matcher matcher = GrouperCheckConfig.grouperLoaderConsumerPattern.matcher(consumerKey);
543       matcher.matches();
544       String consumerName = matcher.group(1);
545       boolean missingOne = false;
546       //now find all 4 required keys
547       String classKey = "changeLog.consumer." + consumerName + ".class";
548       if (!consumerMap.containsKey(classKey)) {
549         String error = "cannot find grouper-loader.properties key: " + classKey; 
550         System.out.println("Grouper error: " + error);
551         LOG.error(error);
552         missingOne = true;
553       }
554       String cronKey = "changeLog.consumer." + consumerName + ".quartzCron";
555 
556       //check the classname
557       Class<?> theClass = null;
558       String className = consumerMap.get(classKey);
559       String cronString = consumerMap.get(cronKey);
560       
561       String jobName = GrouperLoaderType.GROUPER_CHANGE_LOG_CONSUMER_PREFIX + consumerName;
562       changeLogJobNames.add(jobName);
563       
564       //this is a medium priority job
565       int priority = 5;
566 
567       try {
568         if (missingOne) {
569           throw new RuntimeException("Cant find config param" );
570         }
571         
572         theClass = GrouperUtil.forName(className);
573         if (!ChangeLogConsumerBase.class.isAssignableFrom(theClass)) {
574           throw new RuntimeException("not a subclass of ChangeLogConsumerBase");
575         }
576 
577         //default to every minute on the minute, though add a couple of seconds for each one
578         //        if (StringUtils.isBlank(cronString) || StringUtils.equals("0 * * * * ?", cronString)) {
579         //        dont change the crons that are explicitly set... only blank ones
580         if (StringUtils.isBlank(cronString)) {
581           cronString = ((index * 2) % 60) + " * * * * ?";
582         }
583         //at this point we have all the attributes and we know the required ones are there, and logged when 
584         //forbidden ones are there
585 
586         //the name of the job must be unique, so use the group name since one job per group (at this point)
587         JobDetail jobDetail = JobBuilder.newJob(GrouperLoaderJob.class)
588           .withIdentity(jobName)
589           .build();
590         
591         //schedule this job
592         GrouperLoaderScheduleType grouperLoaderScheduleType = GrouperLoaderScheduleType.CRON;
593 
594         Trigger trigger = grouperLoaderScheduleType.createTrigger("triggerChangeLog_" + jobName, priority, cronString, null);
595 
596         if (scheduleJobIfNeeded(jobDetail, trigger)) {
597           changesMade++;
598         }
599 
600       } catch (Exception e) {
601 
602         String errorMessage = "Could not schedule job: '" + jobName + "'";
603         LOG.error(errorMessage, e);
604         errorMessage += "\n" + ExceptionUtils.getFullStackTrace(e);
605         try {
606           //lets enter a log entry so it shows up as error in the db
607           Hib3GrouperLoaderLogib3GrouperLoaderLog.html#Hib3GrouperLoaderLog">Hib3GrouperLoaderLog hib3GrouploaderLog = new Hib3GrouperLoaderLog();
608           hib3GrouploaderLog.setHost(GrouperUtil.hostname());
609           hib3GrouploaderLog.setJobMessage(errorMessage);
610           hib3GrouploaderLog.setJobName(jobName);
611           hib3GrouploaderLog.setJobSchedulePriority(priority);
612           hib3GrouploaderLog.setJobScheduleQuartzCron(cronString);
613           hib3GrouploaderLog.setJobScheduleType(GrouperLoaderScheduleType.CRON.name());
614           hib3GrouploaderLog.setJobType(GrouperLoaderType.CHANGE_LOG.name());
615           hib3GrouploaderLog.setStatus(GrouperLoaderStatus.CONFIG_ERROR.name());
616           hib3GrouploaderLog.store();
617           
618         } catch (Exception e2) {
619           LOG.error("Problem logging to loader db log", e2);
620         }
621 
622       }
623       
624       consumerMap.remove(classKey);
625       consumerMap.remove(cronKey);
626       index++;
627     }
628     
629     // check to see if anything should be unscheduled.
630     try {
631       Scheduler scheduler = GrouperLoader.schedulerFactory().getScheduler();
632 
633       for (JobKey jobKey : scheduler.getJobKeys(GroupMatcher.jobGroupEquals("DEFAULT"))) {
634         
635         String jobName = jobKey.getName();
636         
637         if (jobName.startsWith(GrouperLoaderType.GROUPER_CHANGE_LOG_CONSUMER_PREFIX) && !changeLogJobNames.contains(jobName)) {
638           try {
639             String triggerName = "triggerChangeLog_" + jobName;
640             if (scheduler.unscheduleJob(TriggerKey.triggerKey(triggerName))) {
641               changesMade++;
642             }
643           } catch (Exception e) {
644             String errorMessage = "Could not unschedule job: '" + jobName + "'";
645             LOG.error(errorMessage, e);
646             errorMessage += "\n" + ExceptionUtils.getFullStackTrace(e);
647             try {
648               //lets enter a log entry so it shows up as error in the db
649               Hib3GrouperLoaderLogib3GrouperLoaderLog.html#Hib3GrouperLoaderLog">Hib3GrouperLoaderLog hib3GrouploaderLog = new Hib3GrouperLoaderLog();
650               hib3GrouploaderLog.setHost(GrouperUtil.hostname());
651               hib3GrouploaderLog.setJobMessage(errorMessage);
652               hib3GrouploaderLog.setJobName(jobName);
653               hib3GrouploaderLog.setJobType(GrouperLoaderType.CHANGE_LOG.name());
654               hib3GrouploaderLog.setStatus(GrouperLoaderStatus.CONFIG_ERROR.name());
655               hib3GrouploaderLog.store();
656               
657             } catch (Exception e2) {
658               LOG.error("Problem logging to loader db log", e2);
659             }
660           }
661         }
662       }
663     } catch (Exception e) {
664       
665       String errorMessage = "Could not query change log jobs to see if any should be unscheduled.";
666       LOG.error(errorMessage, e);
667       errorMessage += "\n" + ExceptionUtils.getFullStackTrace(e);
668       try {
669         //lets enter a log entry so it shows up as error in the db
670         Hib3GrouperLoaderLogib3GrouperLoaderLog.html#Hib3GrouperLoaderLog">Hib3GrouperLoaderLog hib3GrouploaderLog = new Hib3GrouperLoaderLog();
671         hib3GrouploaderLog.setHost(GrouperUtil.hostname());
672         hib3GrouploaderLog.setJobMessage(errorMessage);
673         hib3GrouploaderLog.setJobType(GrouperLoaderType.CHANGE_LOG.name());
674         hib3GrouploaderLog.setStatus(GrouperLoaderStatus.CONFIG_ERROR.name());
675         hib3GrouploaderLog.store();
676         
677       } catch (Exception e2) {
678         LOG.error("Problem logging to loader db log", e2);
679       }
680     }
681     return changesMade;
682   }
683   
684   /**
685    * schedule messaging listener jobs
686    */
687   public static int scheduleMessagingListeners() {
688 
689     int changesMade = 0;
690     
691     //#messaging.listener.messagingListener.class = edu.internet2.middleware.grouper.messaging.MessagingListener
692     //#messaging.listener.messagingListener.quartzCron = 0 * * * * ?
693 
694     //make sure sequences are ok
695     Map<String, String> listenerMap = GrouperLoaderConfig.retrieveConfig().propertiesMap( 
696         GrouperCheckConfig.messagingListenerConsumerPattern);
697 
698     Set<String> messagingListenerJobNames = new HashSet<String>();
699     
700     int index = 0;
701     
702     for (String listenerKey : listenerMap.keySet()) {
703       
704       //get the database name
705       Matcher matcher = GrouperCheckConfig.messagingListenerConsumerPattern.matcher(listenerKey);
706       matcher.matches();
707       String listenerName = matcher.group(1);
708       boolean missingOne = false;
709 
710       //now find required keys
711       String classKey = "messaging.listener." + listenerName + ".class";
712             
713       if (!GrouperLoaderConfig.retrieveConfig().containsKey(classKey)) {
714         String error = "cannot find grouper-loader.properties key: " + classKey; 
715         System.out.println("Grouper error: " + error);
716         LOG.error(error);
717         missingOne = true;
718       }
719       String cronKey = "messaging.listener." + listenerName + ".quartzCron";
720 
721       //check the classname
722       Class<?> theClass = null;
723       String className = GrouperLoaderConfig.retrieveConfig().propertyValueString(classKey);
724       String cronString = GrouperLoaderConfig.retrieveConfig().propertyValueString(cronKey);
725       
726       String jobName = GrouperLoaderType.GROUPER_MESSAGING_LISTENER_PREFIX + listenerName;
727       
728       //could be dupes here
729       if (messagingListenerJobNames.contains(jobName)) {
730         continue;
731       }
732 
733       messagingListenerJobNames.add(jobName);
734       
735       //this is a medium priority job
736       int priority = 5;
737 
738       try {
739         if (missingOne) {
740           throw new RuntimeException("Cant find config param" );
741         }
742         
743         theClass = GrouperUtil.forName(className);
744         if (!MessagingListenerBase.class.isAssignableFrom(theClass)) {
745           throw new RuntimeException("not a subclass of MessagingListenerBase");
746         }
747 
748         //default to every minute on the minute, though add a couple of seconds for each one
749         //        if (StringUtils.isBlank(cronString) || StringUtils.equals("0 * * * * ?", cronString)) {
750         //        dont change the crons that are explicitly set... only blank ones
751         if (StringUtils.isBlank(cronString)) {
752           cronString = ((index * 2) % 60) + " * * * * ?";
753         }
754         //at this point we have all the attributes and we know the required ones are there, and logged when 
755         //forbidden ones are there
756 
757         //the name of the job must be unique, so use the group name since one job per group (at this point)
758         JobDetail jobDetail = JobBuilder.newJob(GrouperLoaderJob.class)
759           .withIdentity(jobName)
760           .build();
761         
762         //schedule this job
763         GrouperLoaderScheduleType grouperLoaderScheduleType = GrouperLoaderScheduleType.CRON;
764 
765         Trigger trigger = grouperLoaderScheduleType.createTrigger("triggerMessaging_" + jobName, priority, cronString, null);
766 
767         if (scheduleJobIfNeeded(jobDetail, trigger)) {
768           changesMade++;
769         }
770 
771       } catch (Exception e) {
772 
773         String errorMessage = "Could not schedule job: '" + jobName + "'";
774         LOG.error(errorMessage, e);
775         errorMessage += "\n" + ExceptionUtils.getFullStackTrace(e);
776         try {
777           //lets enter a log entry so it shows up as error in the db
778           Hib3GrouperLoaderLogib3GrouperLoaderLog.html#Hib3GrouperLoaderLog">Hib3GrouperLoaderLog hib3GrouploaderLog = new Hib3GrouperLoaderLog();
779           hib3GrouploaderLog.setHost(GrouperUtil.hostname());
780           hib3GrouploaderLog.setJobMessage(errorMessage);
781           hib3GrouploaderLog.setJobName(jobName);
782           hib3GrouploaderLog.setJobSchedulePriority(priority);
783           hib3GrouploaderLog.setJobScheduleQuartzCron(cronString);
784           hib3GrouploaderLog.setJobScheduleType(GrouperLoaderScheduleType.CRON.name());
785           hib3GrouploaderLog.setJobType(GrouperLoaderType.MESSAGE_LISTENER.name());
786           hib3GrouploaderLog.setStatus(GrouperLoaderStatus.CONFIG_ERROR.name());
787           hib3GrouploaderLog.store();
788           
789         } catch (Exception e2) {
790           LOG.error("Problem logging to loader db log", e2);
791         }
792 
793       }
794       
795       index++;
796     }
797     
798     // check to see if anything should be unscheduled.
799     try {
800       Scheduler scheduler = GrouperLoader.schedulerFactory().getScheduler();
801 
802       for (JobKey jobKey : scheduler.getJobKeys(GroupMatcher.jobGroupEquals("DEFAULT"))) {
803         
804         String jobName = jobKey.getName();
805         
806         if (jobName.startsWith(GrouperLoaderType.GROUPER_MESSAGING_LISTENER_PREFIX) && !messagingListenerJobNames.contains(jobName)) {
807           try {
808             String triggerName = "triggerMessaging_" + jobName;
809             if (scheduler.unscheduleJob(TriggerKey.triggerKey(triggerName))) {
810               changesMade++;
811             }
812           } catch (Exception e) {
813             String errorMessage = "Could not unschedule job: '" + jobName + "'";
814             LOG.error(errorMessage, e);
815             errorMessage += "\n" + ExceptionUtils.getFullStackTrace(e);
816             try {
817               //lets enter a log entry so it shows up as error in the db
818               Hib3GrouperLoaderLogib3GrouperLoaderLog.html#Hib3GrouperLoaderLog">Hib3GrouperLoaderLog hib3GrouploaderLog = new Hib3GrouperLoaderLog();
819               hib3GrouploaderLog.setHost(GrouperUtil.hostname());
820               hib3GrouploaderLog.setJobMessage(errorMessage);
821               hib3GrouploaderLog.setJobName(jobName);
822               hib3GrouploaderLog.setJobType(GrouperLoaderType.MESSAGE_LISTENER.name());
823               hib3GrouploaderLog.setStatus(GrouperLoaderStatus.CONFIG_ERROR.name());
824               hib3GrouploaderLog.store();
825               
826             } catch (Exception e2) {
827               LOG.error("Problem logging to loader db log", e2);
828             }
829           }
830         }
831       }
832     } catch (Exception e) {
833       
834       String errorMessage = "Could not query change log jobs to see if any should be unscheduled.";
835       LOG.error(errorMessage, e);
836       errorMessage += "\n" + ExceptionUtils.getFullStackTrace(e);
837       try {
838         //lets enter a log entry so it shows up as error in the db
839         Hib3GrouperLoaderLogib3GrouperLoaderLog.html#Hib3GrouperLoaderLog">Hib3GrouperLoaderLog hib3GrouploaderLog = new Hib3GrouperLoaderLog();
840         hib3GrouploaderLog.setHost(GrouperUtil.hostname());
841         hib3GrouploaderLog.setJobMessage(errorMessage);
842         hib3GrouploaderLog.setJobType(GrouperLoaderType.MESSAGE_LISTENER.name());
843         hib3GrouploaderLog.setStatus(GrouperLoaderStatus.CONFIG_ERROR.name());
844         hib3GrouploaderLog.store();
845         
846       } catch (Exception e2) {
847         LOG.error("Problem logging to loader db log", e2);
848       }
849     }
850     return changesMade;
851   }
852   
853   /**
854    * schedule other jobs
855    */
856   public static int scheduleOtherJobs() {
857     
858     //otherJob.duo.class = 
859     //otherJob.duo.quartzCron = 
860     //otherJob.duo.priority = 
861 
862     //make sure sequences are ok
863     Map<String, String> otherJobMap = GrouperLoaderConfig.retrieveConfig().propertiesMap( 
864         GrouperCheckConfig.grouperLoaderOtherJobPattern);
865     
866     Set<String> otherJobNames = new HashSet<String>();
867         
868     int changesMade = 0;
869     
870     while (otherJobMap.size() > 0) {
871             
872       //get one
873       String consumerKey = otherJobMap.keySet().iterator().next();
874 
875       Matcher matcher = GrouperCheckConfig.grouperLoaderOtherJobPattern.matcher(consumerKey);
876       matcher.matches();
877       String otherJobName = matcher.group(1);
878       boolean missingOne = false;
879       //now find all required keys
880       String classKey = "otherJob." + otherJobName + ".class";
881       if (!otherJobMap.containsKey(classKey)) {
882         String error = "cannot find grouper-loader.properties key: " + classKey; 
883         System.out.println("Grouper error: " + error);
884         LOG.error(error);
885         missingOne = true;
886       }
887       
888       String cronKey = "otherJob." + otherJobName + ".quartzCron";
889       if (!otherJobMap.containsKey(cronKey)) {
890         String error = "cannot find grouper-loader.properties key: " + cronKey; 
891         System.out.println("Grouper error: " + error);
892         LOG.error(error);
893         missingOne = true;
894       }
895       
896       String priorityKey = "otherJob." + otherJobName + ".priority";
897       
898       //check the classname
899       String className = otherJobMap.get(classKey);
900       String cronString = otherJobMap.get(cronKey);
901       int priority = GrouperUtil.intValue(otherJobMap.get(priorityKey), 5);
902       
903       if (StringUtils.isBlank(cronString)) {
904         LOG.warn("grouper-loader.properties key: " + cronKey + " is blank so disabling job " + otherJobName + ".");
905         otherJobMap.remove(classKey);
906         otherJobMap.remove(cronKey);
907         otherJobMap.remove(priorityKey);
908         continue;
909       }
910       
911       String jobName = GrouperLoaderType.GROUPER_OTHER_JOB_PREFIX + otherJobName;
912       otherJobNames.add(jobName);
913 
914       try {
915         if (missingOne) {
916           throw new RuntimeException("Cant find config param" );
917         }
918         
919         GrouperUtil.forName(className); // just confirm that it resolves
920 
921         //at this point we have all the attributes and we know the required ones are there, and logged when 
922         //forbidden ones are there
923 
924         JobDetail jobDetail = JobBuilder.newJob(GrouperDaemonJob.class)
925           .withIdentity(jobName)
926           .build();
927         
928         //schedule this job
929         GrouperLoaderScheduleType grouperLoaderScheduleType = GrouperLoaderScheduleType.CRON;
930 
931         Trigger trigger = grouperLoaderScheduleType.createTrigger("triggerOtherJob_" + jobName, priority, cronString, null);
932 
933         if (scheduleJobIfNeeded(jobDetail, trigger)) {
934           changesMade++;
935         }
936       } catch (Exception e) {
937 
938         String errorMessage = "Could not schedule job: '" + jobName + "'";
939         LOG.error(errorMessage, e);
940         errorMessage += "\n" + ExceptionUtils.getFullStackTrace(e);
941         try {
942           //lets enter a log entry so it shows up as error in the db
943           Hib3GrouperLoaderLogib3GrouperLoaderLog.html#Hib3GrouperLoaderLog">Hib3GrouperLoaderLog hib3GrouploaderLog = new Hib3GrouperLoaderLog();
944           hib3GrouploaderLog.setHost(GrouperUtil.hostname());
945           hib3GrouploaderLog.setJobMessage(errorMessage);
946           hib3GrouploaderLog.setJobName(jobName);
947           hib3GrouploaderLog.setJobSchedulePriority(priority);
948           hib3GrouploaderLog.setJobScheduleQuartzCron(cronString);
949           hib3GrouploaderLog.setJobScheduleType(GrouperLoaderScheduleType.CRON.name());
950           hib3GrouploaderLog.setJobType("OTHER_JOB");
951           hib3GrouploaderLog.setStatus(GrouperLoaderStatus.CONFIG_ERROR.name());
952           hib3GrouploaderLog.store();
953           
954         } catch (Exception e2) {
955           LOG.error("Problem logging to loader db log", e2);
956         }
957 
958       }
959       
960       otherJobMap.remove(classKey);
961       otherJobMap.remove(cronKey);
962       otherJobMap.remove(priorityKey);
963     }
964     
965     // check to see if anything should be unscheduled.
966     try {
967       Scheduler scheduler = GrouperLoader.schedulerFactory().getScheduler();
968 
969       for (JobKey jobKey : scheduler.getJobKeys(GroupMatcher.jobGroupEquals("DEFAULT"))) {
970         
971         String jobName = jobKey.getName();
972         
973         if (jobName.startsWith(GrouperLoaderType.GROUPER_OTHER_JOB_PREFIX) && !otherJobNames.contains(jobName)) {
974           try {
975             String triggerName = "triggerOtherJob_" + jobName;
976             if (scheduler.unscheduleJob(TriggerKey.triggerKey(triggerName))) {
977               changesMade++;
978             }
979           } catch (Exception e) {
980             String errorMessage = "Could not unschedule job: '" + jobName + "'";
981             LOG.error(errorMessage, e);
982             errorMessage += "\n" + ExceptionUtils.getFullStackTrace(e);
983             try {
984               //lets enter a log entry so it shows up as error in the db
985               Hib3GrouperLoaderLogib3GrouperLoaderLog.html#Hib3GrouperLoaderLog">Hib3GrouperLoaderLog hib3GrouploaderLog = new Hib3GrouperLoaderLog();
986               hib3GrouploaderLog.setHost(GrouperUtil.hostname());
987               hib3GrouploaderLog.setJobMessage(errorMessage);
988               hib3GrouploaderLog.setJobName(jobName);
989               hib3GrouploaderLog.setJobType("OTHER_JOB");
990               hib3GrouploaderLog.setStatus(GrouperLoaderStatus.CONFIG_ERROR.name());
991               hib3GrouploaderLog.store();
992               
993             } catch (Exception e2) {
994               LOG.error("Problem logging to loader db log", e2);
995             }
996           }
997         }
998       }
999     } catch (Exception e) {
1000       
1001       String errorMessage = "Could not query other jobs to see if any should be unscheduled.";
1002       LOG.error(errorMessage, e);
1003       errorMessage += "\n" + ExceptionUtils.getFullStackTrace(e);
1004       try {
1005         //lets enter a log entry so it shows up as error in the db
1006         Hib3GrouperLoaderLogib3GrouperLoaderLog.html#Hib3GrouperLoaderLog">Hib3GrouperLoaderLog hib3GrouploaderLog = new Hib3GrouperLoaderLog();
1007         hib3GrouploaderLog.setHost(GrouperUtil.hostname());
1008         hib3GrouploaderLog.setJobMessage(errorMessage);
1009         hib3GrouploaderLog.setJobType("OTHER_JOB");
1010         hib3GrouploaderLog.setStatus(GrouperLoaderStatus.CONFIG_ERROR.name());
1011         hib3GrouploaderLog.store();
1012         
1013       } catch (Exception e2) {
1014         LOG.error("Problem logging to loader db log", e2);
1015       }
1016     }
1017     return changesMade;
1018   }
1019 
1020 
1021   /**
1022    * schedule maintenance job
1023    */
1024   public static boolean scheduleDailyReportJob() {
1025 
1026     String cronString = null;
1027 
1028     //this is a low priority job
1029     int priority = 1;
1030 
1031     //schedule the job
1032     try {
1033       Scheduler scheduler = GrouperLoader.schedulerFactory().getScheduler();
1034       String triggerName = "triggerMaintenance_grouperReport";
1035       
1036       cronString = GrouperLoaderConfig.retrieveConfig().propertyValueString("daily.report.quartz.cron");
1037 
1038       if (StringUtils.isBlank(cronString)) {
1039         LOG.warn("grouper-loader.properties key: daily.report.quartz.cron is not " +
1040         		"filled in so the daily report will not run");
1041         return scheduler.unscheduleJob(TriggerKey.triggerKey(triggerName));
1042 
1043       }
1044       
1045       //at this point we have all the attributes and we know the required ones are there, and logged when 
1046       //forbidden ones are there
1047 
1048       //the name of the job must be unique, so use the group name since one job per group (at this point)
1049       JobDetail jobDetail = JobBuilder.newJob(GrouperLoaderJob.class)
1050         .withIdentity(GrouperLoaderType.GROUPER_REPORT)
1051         .build();
1052 
1053       //schedule this job daily at 6am
1054       GrouperLoaderScheduleType grouperLoaderScheduleType = GrouperLoaderScheduleType.CRON;
1055 
1056       Trigger trigger = grouperLoaderScheduleType.createTrigger(triggerName, priority, cronString, null);
1057 
1058       return scheduleJobIfNeeded(jobDetail, trigger);
1059 
1060     } catch (Exception e) {
1061       String errorMessage = "Could not schedule job: '" + GrouperLoaderType.GROUPER_REPORT + "'";
1062       LOG.error(errorMessage, e);
1063       errorMessage += "\n" + ExceptionUtils.getFullStackTrace(e);
1064       try {
1065         //lets enter a log entry so it shows up as error in the db
1066         Hib3GrouperLoaderLogib3GrouperLoaderLog.html#Hib3GrouperLoaderLog">Hib3GrouperLoaderLog hib3GrouploaderLog = new Hib3GrouperLoaderLog();
1067         hib3GrouploaderLog.setHost(GrouperUtil.hostname());
1068         hib3GrouploaderLog.setJobMessage(errorMessage);
1069         hib3GrouploaderLog.setJobName(GrouperLoaderType.GROUPER_REPORT);
1070         hib3GrouploaderLog.setJobSchedulePriority(priority);
1071         hib3GrouploaderLog.setJobScheduleQuartzCron(cronString);
1072         hib3GrouploaderLog.setJobScheduleType(GrouperLoaderScheduleType.CRON.name());
1073         hib3GrouploaderLog.setJobType(GrouperLoaderType.MAINTENANCE.name());
1074         hib3GrouploaderLog.setStatus(GrouperLoaderStatus.CONFIG_ERROR.name());
1075         hib3GrouploaderLog.store();
1076         
1077       } catch (Exception e2) {
1078         LOG.error("Problem logging to loader db log", e2);
1079       }
1080     }
1081     return false;
1082   }
1083 
1084   /**
1085    * schedule rules job
1086    */
1087   public static boolean scheduleRulesJob() {
1088 
1089     String cronString = null;
1090 
1091     //this is a low priority job
1092     int priority = 1;
1093 
1094     //schedule the job
1095     try {
1096       boolean unscheduleAndReturn = false;
1097       
1098       Scheduler scheduler = GrouperLoader.schedulerFactory().getScheduler();
1099       String triggerName = "triggerMaintenance_rules";
1100 
1101       if (!GrouperConfig.retrieveConfig().propertyValueBoolean("rules.enable", true)) {
1102         LOG.warn("grouper.properties key: rules.enable is false " +
1103           "so the rules engine/daemon will not run");
1104         unscheduleAndReturn = true;
1105       }
1106 
1107       cronString = GrouperLoaderConfig.retrieveConfig().propertyValueString("rules.quartz.cron");
1108 
1109       if (StringUtils.isBlank(cronString)) {
1110         LOG.warn("grouper-loader.properties key: rules.quartz.cron is not " +
1111             "filled in so the rules daemon will not run");
1112         unscheduleAndReturn = true;
1113       }
1114       
1115       if (unscheduleAndReturn) {
1116         return scheduler.unscheduleJob(TriggerKey.triggerKey(triggerName));
1117       }
1118       
1119       //at this point we have all the attributes and we know the required ones are there, and logged when 
1120       //forbidden ones are there
1121 
1122       //the name of the job must be unique, so use the group name since one job per group (at this point)
1123       JobDetail jobDetail = JobBuilder.newJob(GrouperLoaderJob.class)
1124         .withIdentity(GrouperLoaderType.GROUPER_RULES)
1125         .build();
1126 
1127       //schedule this job daily at 6am
1128       GrouperLoaderScheduleType grouperLoaderScheduleType = GrouperLoaderScheduleType.CRON;
1129 
1130       Trigger trigger = grouperLoaderScheduleType.createTrigger(triggerName, priority, cronString, null);
1131 
1132       return scheduleJobIfNeeded(jobDetail, trigger);
1133 
1134 
1135     } catch (Exception e) {
1136       String errorMessage = "Could not schedule job: '" + GrouperLoaderType.GROUPER_RULES + "'";
1137       LOG.error(errorMessage, e);
1138       errorMessage += "\n" + ExceptionUtils.getFullStackTrace(e);
1139       try {
1140         //lets enter a log entry so it shows up as error in the db
1141         Hib3GrouperLoaderLogib3GrouperLoaderLog.html#Hib3GrouperLoaderLog">Hib3GrouperLoaderLog hib3GrouploaderLog = new Hib3GrouperLoaderLog();
1142         hib3GrouploaderLog.setHost(GrouperUtil.hostname());
1143         hib3GrouploaderLog.setJobMessage(errorMessage);
1144         hib3GrouploaderLog.setJobName(GrouperLoaderType.GROUPER_RULES);
1145         hib3GrouploaderLog.setJobSchedulePriority(priority);
1146         hib3GrouploaderLog.setJobScheduleQuartzCron(cronString);
1147         hib3GrouploaderLog.setJobScheduleType(GrouperLoaderScheduleType.CRON.name());
1148         hib3GrouploaderLog.setJobType(GrouperLoaderType.MAINTENANCE.name());
1149         hib3GrouploaderLog.setStatus(GrouperLoaderStatus.CONFIG_ERROR.name());
1150         hib3GrouploaderLog.store();
1151         
1152       } catch (Exception e2) {
1153         LOG.error("Problem logging to loader db log", e2);
1154       }
1155     }
1156     return false;
1157   }
1158 
1159 
1160   
1161   /**
1162    * schedule enabled/disabled job
1163    */
1164   public static boolean scheduleEnabledDisabledJob() {
1165 
1166     String cronString = null;
1167 
1168     //this is a low priority job
1169     int priority = 1;
1170 
1171     //schedule the job
1172     try {
1173       Scheduler scheduler = GrouperLoader.schedulerFactory().getScheduler();
1174       String triggerName = "triggerMaintenance_enabledDisabled";
1175       
1176       cronString = GrouperLoaderConfig.retrieveConfig().propertyValueString("changeLog.enabledDisabled.quartz.cron");
1177 
1178       if (StringUtils.isBlank(cronString)) {
1179         LOG.warn("grouper-loader.properties key: changeLog.enabledDisabled.quartz.cron is not " +
1180             "filled in so the enabled/disabled daemon will not run");
1181         return scheduler.unscheduleJob(TriggerKey.triggerKey(triggerName));
1182 
1183       }
1184       
1185       //at this point we have all the attributes and we know the required ones are there, and logged when 
1186       //forbidden ones are there
1187 
1188       //the name of the job must be unique, so use the group name since one job per group (at this point)
1189       JobDetail jobDetail = JobBuilder.newJob(GrouperLoaderJob.class)
1190         .withIdentity(GrouperLoaderType.GROUPER_ENABLED_DISABLED)
1191         .build();
1192 
1193       //schedule this job daily at 6am
1194       GrouperLoaderScheduleType grouperLoaderScheduleType = GrouperLoaderScheduleType.CRON;
1195 
1196       Trigger trigger = grouperLoaderScheduleType.createTrigger(triggerName, priority, cronString, null);
1197 
1198       return scheduleJobIfNeeded(jobDetail, trigger);
1199 
1200 
1201     } catch (Exception e) {
1202       String errorMessage = "Could not schedule job: '" + GrouperLoaderType.GROUPER_ENABLED_DISABLED + "'";
1203       LOG.error(errorMessage, e);
1204       errorMessage += "\n" + ExceptionUtils.getFullStackTrace(e);
1205       try {
1206         //lets enter a log entry so it shows up as error in the db
1207         Hib3GrouperLoaderLogib3GrouperLoaderLog.html#Hib3GrouperLoaderLog">Hib3GrouperLoaderLog hib3GrouploaderLog = new Hib3GrouperLoaderLog();
1208         hib3GrouploaderLog.setHost(GrouperUtil.hostname());
1209         hib3GrouploaderLog.setJobMessage(errorMessage);
1210         hib3GrouploaderLog.setJobName(GrouperLoaderType.GROUPER_ENABLED_DISABLED);
1211         hib3GrouploaderLog.setJobSchedulePriority(priority);
1212         hib3GrouploaderLog.setJobScheduleQuartzCron(cronString);
1213         hib3GrouploaderLog.setJobScheduleType(GrouperLoaderScheduleType.CRON.name());
1214         hib3GrouploaderLog.setJobType(GrouperLoaderType.MAINTENANCE.name());
1215         hib3GrouploaderLog.setStatus(GrouperLoaderStatus.CONFIG_ERROR.name());
1216         hib3GrouploaderLog.store();
1217         
1218       } catch (Exception e2) {
1219         LOG.error("Problem logging to loader db log", e2);
1220       }
1221     }
1222     return false;
1223   }
1224   
1225   /**
1226    * schedule enabled/disabled job
1227    */
1228   public static boolean scheduleBuiltinMessagingDaemonJob() {
1229 
1230     String cronString = null;
1231 
1232     //this is a low priority job
1233     int priority = 1;
1234 
1235     //schedule the job
1236     try {
1237       Scheduler scheduler = GrouperLoader.schedulerFactory().getScheduler();
1238       String triggerName = "triggerMaintenance_Messaging";
1239       
1240       cronString = GrouperLoaderConfig.retrieveConfig().propertyValueString("changeLog.builtinMessagingDaemon.quartz.cron");
1241 
1242       if (StringUtils.isBlank(cronString)) {
1243         LOG.warn("grouper-loader.properties key: changeLog.builtinMessagingDaemon.quartz.cron is not " +
1244             "filled in so the builtin messaging daemon will not run");
1245         return scheduler.unscheduleJob(TriggerKey.triggerKey(triggerName));
1246 
1247       }
1248       
1249       //at this point we have all the attributes and we know the required ones are there, and logged when 
1250       //forbidden ones are there
1251 
1252       //the name of the job must be unique, so use the group name since one job per group (at this point)
1253       JobDetail jobDetail = JobBuilder.newJob(GrouperLoaderJob.class)
1254         .withIdentity(GrouperLoaderType.GROUPER_BUILTIN_MESSAGING_DAEMON)
1255         .build();
1256 
1257       //schedule this job daily at 6am
1258       GrouperLoaderScheduleType grouperLoaderScheduleType = GrouperLoaderScheduleType.CRON;
1259 
1260       Trigger trigger = grouperLoaderScheduleType.createTrigger(triggerName, priority, cronString, null);
1261 
1262       return scheduleJobIfNeeded(jobDetail, trigger);
1263 
1264 
1265     } catch (Exception e) {
1266       String errorMessage = "Could not schedule job: '" + GrouperLoaderType.GROUPER_BUILTIN_MESSAGING_DAEMON + "'";
1267       LOG.error(errorMessage, e);
1268       errorMessage += "\n" + ExceptionUtils.getFullStackTrace(e);
1269       try {
1270         //lets enter a log entry so it shows up as error in the db
1271         Hib3GrouperLoaderLogib3GrouperLoaderLog.html#Hib3GrouperLoaderLog">Hib3GrouperLoaderLog hib3GrouploaderLog = new Hib3GrouperLoaderLog();
1272         hib3GrouploaderLog.setHost(GrouperUtil.hostname());
1273         hib3GrouploaderLog.setJobMessage(errorMessage);
1274         hib3GrouploaderLog.setJobName(GrouperLoaderType.GROUPER_BUILTIN_MESSAGING_DAEMON);
1275         hib3GrouploaderLog.setJobSchedulePriority(priority);
1276         hib3GrouploaderLog.setJobScheduleQuartzCron(cronString);
1277         hib3GrouploaderLog.setJobScheduleType(GrouperLoaderScheduleType.CRON.name());
1278         hib3GrouploaderLog.setJobType(GrouperLoaderType.MAINTENANCE.name());
1279         hib3GrouploaderLog.setStatus(GrouperLoaderStatus.CONFIG_ERROR.name());
1280         hib3GrouploaderLog.store();
1281         
1282       } catch (Exception e2) {
1283         LOG.error("Problem logging to loader db log", e2);
1284       }
1285     }
1286     return false;
1287   }
1288   
1289   /**
1290    * schedule external subject subj calc fields
1291    */
1292   public static void scheduleExternalSubjCalcFieldsJob() {
1293 
1294     String cronString = null;
1295 
1296     //this is a low priority job
1297     int priority = 1;
1298 
1299     //schedule the job
1300     try {
1301       Scheduler scheduler = GrouperLoader.schedulerFactory().getScheduler();
1302       String triggerName = "triggerMaintenance_externalSubjCalcFields";
1303 
1304       cronString = GrouperLoaderConfig.retrieveConfig().propertyValueString("externalSubjects.calc.fields.cron");
1305 
1306       if (StringUtils.isBlank(cronString)) {
1307         LOG.info("grouper.properties key: externalSubjects.calc.fields.cron is not " +
1308             "filled in so the external subject calc fields daemon will not run");
1309         scheduler.unscheduleJob(TriggerKey.triggerKey(triggerName));
1310 
1311         return;
1312       }
1313       
1314       //at this point we have all the attributes and we know the required ones are there, and logged when 
1315       //forbidden ones are there
1316 
1317       //the name of the job must be unique, so use the group name since one job per group (at this point)
1318       JobDetail jobDetail = JobBuilder.newJob(GrouperLoaderJob.class)
1319         .withIdentity(GrouperLoaderType.GROUPER_EXTERNAL_SUBJ_CALC_FIELDS)
1320         .build();
1321 
1322       //schedule this job daily at 6am
1323       GrouperLoaderScheduleType grouperLoaderScheduleType = GrouperLoaderScheduleType.CRON;
1324 
1325       Trigger trigger = grouperLoaderScheduleType.createTrigger(triggerName, priority, cronString, null);
1326 
1327       scheduleJobIfNeeded(jobDetail, trigger);
1328 
1329 
1330     } catch (Exception e) {
1331       String errorMessage = "Could not schedule job: '" + GrouperLoaderType.GROUPER_EXTERNAL_SUBJ_CALC_FIELDS + "'";
1332       LOG.error(errorMessage, e);
1333       errorMessage += "\n" + ExceptionUtils.getFullStackTrace(e);
1334       try {
1335         //lets enter a log entry so it shows up as error in the db
1336         Hib3GrouperLoaderLogib3GrouperLoaderLog.html#Hib3GrouperLoaderLog">Hib3GrouperLoaderLog hib3GrouploaderLog = new Hib3GrouperLoaderLog();
1337         hib3GrouploaderLog.setHost(GrouperUtil.hostname());
1338         hib3GrouploaderLog.setJobMessage(errorMessage);
1339         hib3GrouploaderLog.setJobName(GrouperLoaderType.GROUPER_EXTERNAL_SUBJ_CALC_FIELDS);
1340         hib3GrouploaderLog.setJobSchedulePriority(priority);
1341         hib3GrouploaderLog.setJobScheduleQuartzCron(cronString);
1342         hib3GrouploaderLog.setJobScheduleType(GrouperLoaderScheduleType.CRON.name());
1343         hib3GrouploaderLog.setJobType(GrouperLoaderType.MAINTENANCE.name());
1344         hib3GrouploaderLog.setStatus(GrouperLoaderStatus.CONFIG_ERROR.name());
1345         hib3GrouploaderLog.store();
1346         
1347       } catch (Exception e2) {
1348         LOG.error("Problem logging to loader db log", e2);
1349       }
1350     }
1351 
1352   }
1353 
1354   /**
1355    * schedule maintenance job
1356    * @return true if made change
1357    */
1358   public static boolean scheduleLogCleanerJob() {
1359     
1360     //schedule daily anytime
1361     //6am daily: "0 0 6 * * ?"
1362     //every minute for testing: "0 * * * * ?"
1363     String cronString = GrouperLoaderConfig.retrieveConfig().propertyValueString("changeLog.cleanLogs.quartz.cron", "0 0 6 * * ?");
1364     
1365     //this is a low priority job
1366     int priority = 1;
1367 
1368     //schedule the log delete job
1369     try {
1370 
1371       //at this point we have all the attributes and we know the required ones are there, and logged when 
1372       //forbidden ones are there
1373 
1374       //the name of the job must be unique, so use the group name since one job per group (at this point)
1375       JobDetail jobDetail = JobBuilder.newJob(GrouperLoaderJob.class)
1376         .withIdentity(GrouperLoaderType.MAINTENANCE_CLEAN_LOGS)
1377         .build();
1378       
1379       //schedule this job daily at 6am
1380       GrouperLoaderScheduleType grouperLoaderScheduleType = GrouperLoaderScheduleType.CRON;
1381       
1382       Trigger trigger = grouperLoaderScheduleType.createTrigger("triggerMaintenance_cleanLogs", priority, cronString, null);
1383 
1384       return scheduleJobIfNeeded(jobDetail, trigger);
1385 
1386       
1387     } catch (Exception e) {
1388       String errorMessage = "Could not schedule job: '" + GrouperLoaderType.MAINTENANCE_CLEAN_LOGS + "'";
1389       LOG.error(errorMessage, e);
1390       errorMessage += "\n" + ExceptionUtils.getFullStackTrace(e);
1391       try {
1392         //lets enter a log entry so it shows up as error in the db
1393         Hib3GrouperLoaderLogib3GrouperLoaderLog.html#Hib3GrouperLoaderLog">Hib3GrouperLoaderLog hib3GrouploaderLog = new Hib3GrouperLoaderLog();
1394         hib3GrouploaderLog.setHost(GrouperUtil.hostname());
1395         hib3GrouploaderLog.setJobMessage(errorMessage);
1396         hib3GrouploaderLog.setJobName(GrouperLoaderType.MAINTENANCE_CLEAN_LOGS);
1397         hib3GrouploaderLog.setJobSchedulePriority(priority);
1398         hib3GrouploaderLog.setJobScheduleQuartzCron(cronString);
1399         hib3GrouploaderLog.setJobScheduleType(GrouperLoaderScheduleType.CRON.name());
1400         hib3GrouploaderLog.setJobType(GrouperLoaderType.MAINTENANCE.name());
1401         hib3GrouploaderLog.setStatus(GrouperLoaderStatus.CONFIG_ERROR.name());
1402         hib3GrouploaderLog.store();
1403         
1404       } catch (Exception e2) {
1405         LOG.error("Problem logging to loader db log", e2);
1406       }
1407     }
1408     return false;
1409   }
1410 
1411   /**
1412    * 
1413    */
1414   public static int scheduleEsbListenerJobs() {
1415 
1416     int changesMade = 0;
1417     
1418     int priority = 1;
1419     GregorianCalendar cal = new GregorianCalendar();
1420     cal.add(GregorianCalendar.SECOND, 5);
1421     Date runTime = cal.getTime();
1422     
1423     String triggerNameHttpListener = GrouperLoaderType.GROUPER_ESB_HTTP_LISTENER + "_trigger";
1424     String triggerNameXmmpListener = GrouperLoaderType.GROUPER_ESB_XMMP_LISTENER + "_trigger";
1425 
1426     // String cronString = "15 55 13 * * ?";
1427     //cronString = cal.getTime().getSeconds() + " " + cal.getTime().getMinutes() + " " + cal.getTime().getHours() + " * * ?"; 
1428     //System.out.println(cronString);
1429     boolean runEsbHttpListener = GrouperLoaderConfig.retrieveConfig().propertyValueBoolean(
1430         "esb.listeners.http.enable", false);
1431 
1432     try {
1433       Scheduler scheduler = GrouperLoader.schedulerFactory().getScheduler();
1434 
1435       if (runEsbHttpListener) {
1436         LOG.info("Starting experimental HTTP(S) listener");
1437         String port = GrouperLoaderConfig.retrieveConfig().propertyValueString("esb.listeners.http.port",
1438             "8080");
1439         String bindAddress = GrouperLoaderConfig.retrieveConfig().propertyValueString(
1440             "esb.listeners.http.bindaddress", "127.0.0.1");
1441         String authConfigFile = GrouperLoaderConfig.retrieveConfig().propertyValueString(
1442             "esb.listeners.http.authConfigFile", "");
1443         String sslKeystore = GrouperLoaderConfig.retrieveConfig().propertyValueString(
1444             "esb.listeners.http.ssl.keystore", "");
1445         String sslKeyPassword = GrouperLoaderConfig.retrieveConfig().propertyValueString(
1446             "esb.listeners.http.ssl.keyPassword", "");
1447         String sslTrustStore = GrouperLoaderConfig.retrieveConfig().propertyValueString(
1448             "esb.listeners.http.ssl.trustStore", "");
1449         String sslTrustPassword = GrouperLoaderConfig.retrieveConfig().propertyValueString(
1450             "esb.listeners.http.ssl.trustPassword", "");
1451         String sslPassword = GrouperLoaderConfig.retrieveConfig().propertyValueString(
1452             "esb.listeners.http.ssl.password", "");
1453         //at this point we have all the attributes and we know the required ones are there, and logged when 
1454         //forbidden ones are there
1455 
1456         //the name of the job must be unique, so use the group name since one job per group (at this point)
1457         JobDetail jobDetail = JobBuilder.newJob(GrouperUtil.forName("edu.internet2.middleware.grouper.esb.listener.EsbHttpServer"))
1458           .withIdentity(GrouperLoaderType.GROUPER_ESB_HTTP_LISTENER)
1459           .usingJobData("port", port)
1460           .usingJobData("bindAddress", bindAddress)
1461           .usingJobData("authConfigFile", authConfigFile)
1462           .usingJobData("keystore", sslKeystore)
1463           .usingJobData("keyPassword", sslKeyPassword)
1464           .usingJobData("trustStore", sslTrustStore)
1465           .usingJobData("trustPassword", sslTrustPassword)
1466           .usingJobData("keystore", sslKeystore)
1467           .usingJobData("password", sslPassword)
1468           .build();
1469 
1470         //schedule this job to run in 5 seconds
1471         Trigger trg = TriggerBuilder.newTrigger()
1472           .withIdentity(triggerNameHttpListener)
1473           .startAt(runTime)
1474           .withPriority(priority)
1475           .build();
1476 
1477         if (scheduleJobIfNeeded(jobDetail, trg)) {
1478           changesMade++;
1479         }
1480       } else {
1481         LOG.info("Not starting experimental HTTP(S) listener");
1482        
1483         scheduler.unscheduleJob(TriggerKey.triggerKey(triggerNameHttpListener));
1484       }
1485     } catch (Exception e) {
1486       String errorMessage = "Could not schedule job: '"
1487           + GrouperLoaderType.GROUPER_ESB_HTTP_LISTENER + "'";
1488       LOG.error(errorMessage, e);
1489       errorMessage += "\n" + ExceptionUtils.getFullStackTrace(e);
1490       try {
1491         //lets enter a log entry so it shows up as error in the db
1492         Hib3GrouperLoaderLogib3GrouperLoaderLog.html#Hib3GrouperLoaderLog">Hib3GrouperLoaderLog hib3GrouploaderLog = new Hib3GrouperLoaderLog();
1493         hib3GrouploaderLog.setHost(GrouperUtil.hostname());
1494         hib3GrouploaderLog.setJobMessage(errorMessage);
1495         hib3GrouploaderLog.setJobName(GrouperLoaderType.GROUPER_ESB_HTTP_LISTENER);
1496         hib3GrouploaderLog.setJobSchedulePriority(priority);
1497         hib3GrouploaderLog.setJobScheduleQuartzCron("5 seconds from now");
1498         hib3GrouploaderLog.setJobScheduleType(GrouperLoaderScheduleType.CRON.name());
1499         hib3GrouploaderLog.setJobType(GrouperLoaderType.GROUPER_ESB_HTTP_LISTENER);
1500         hib3GrouploaderLog.setStatus(GrouperLoaderStatus.CONFIG_ERROR.name());
1501         hib3GrouploaderLog.store();
1502 
1503       } catch (Exception e2) {
1504         LOG.error("Problem logging to loader db log", e2);
1505       }
1506     }
1507 
1508     boolean runEsbHXmppListener = GrouperLoaderConfig.retrieveConfig().propertyValueBoolean(
1509         "esb.listeners.xmpp.enable", false);
1510     try {
1511       Scheduler scheduler = GrouperLoader.schedulerFactory().getScheduler();
1512       boolean unschedule = false;
1513       if (runEsbHXmppListener) {
1514         LOG.info("Starting experimental XMPP listener");
1515 
1516         String server = GrouperLoaderConfig.retrieveConfig().propertyValueString(
1517             "esb.listeners.xmpp.server", "");
1518         if (server.equals("")) {
1519           LOG.warn("XMPP server must be configured in grouper-loader.properties");
1520         }
1521         String port = GrouperLoaderConfig.retrieveConfig().propertyValueString("esb.listeners.xmpp.port",
1522             "5222");
1523         String username = GrouperLoaderConfig.retrieveConfig().propertyValueString(
1524             "esb.listeners.xmpp.username", "");
1525         if (username.equals("")) {
1526           LOG.warn("XMPP username must be configured in grouper-loader.properties");
1527         }
1528         String password = GrouperLoaderConfig.retrieveConfig().propertyValueString(
1529             "esb.listeners.xmpp.password", "");
1530         if (password.equals("")) {
1531           LOG.warn("XMPP password must be configured in grouper-loader.properties");
1532         }
1533         String sendername = GrouperLoaderConfig.retrieveConfig().propertyValueString(
1534             "esb.listeners.xmpp.sendername", "");
1535         String resource = GrouperLoaderConfig.retrieveConfig().propertyValueString(
1536             "esb.listeners.xmpp.resource", "GrouperListener");
1537         if (server.equals("")) {
1538           LOG.warn("XMPP sendername must be configured in grouper-loader.properties");
1539         }
1540         if (!(server.equals("")) & !(username.equals("")) && !(password.equals(""))
1541             && !(sendername.equals(""))) {
1542 
1543           //the name of the job must be unique, so use the group name since one job per group (at this point)
1544           JobDetail jobDetail = JobBuilder.newJob(GrouperUtil.forName("edu.internet2.middleware.grouper.esb.listener.EsbXmppListener"))
1545             .withIdentity(GrouperLoaderType.GROUPER_ESB_XMMP_LISTENER)
1546             .usingJobData("port", port)
1547             .usingJobData("server", server)
1548             .usingJobData("username", username)
1549             .usingJobData("password", password)
1550             .usingJobData("sendername", sendername)
1551             .usingJobData("resource", resource)
1552             .build();
1553           
1554           Trigger trg = TriggerBuilder.newTrigger()
1555             .withIdentity(triggerNameXmmpListener)
1556             .startAt(runTime)
1557             .withPriority(priority)
1558             .build();
1559 
1560           if (scheduleJobIfNeeded(jobDetail, trg)) {
1561             changesMade++;
1562           }
1563         } else {
1564           unschedule = true;
1565         }
1566       } else {
1567         LOG.info("Not starting experimental XMPP listener");
1568         unschedule = true;
1569       }
1570       
1571       if (unschedule) {
1572         scheduler.unscheduleJob(TriggerKey.triggerKey(triggerNameXmmpListener));
1573       }
1574       
1575     } catch (Exception e) {
1576       String errorMessage = "Could not schedule job: '"
1577           + GrouperLoaderType.GROUPER_ESB_XMMP_LISTENER + "'";
1578       LOG.error(errorMessage, e);
1579       errorMessage += "\n" + ExceptionUtils.getFullStackTrace(e);
1580       try {
1581         //lets enter a log entry so it shows up as error in the db
1582         Hib3GrouperLoaderLogib3GrouperLoaderLog.html#Hib3GrouperLoaderLog">Hib3GrouperLoaderLog hib3GrouploaderLog = new Hib3GrouperLoaderLog();
1583         hib3GrouploaderLog.setHost(GrouperUtil.hostname());
1584         hib3GrouploaderLog.setJobMessage(errorMessage);
1585         hib3GrouploaderLog.setJobName(GrouperLoaderType.GROUPER_ESB_XMMP_LISTENER);
1586         hib3GrouploaderLog.setJobSchedulePriority(priority);
1587         hib3GrouploaderLog.setJobScheduleQuartzCron("5 seconds from now");
1588         hib3GrouploaderLog.setJobScheduleType(GrouperLoaderScheduleType.CRON.name());
1589         hib3GrouploaderLog.setJobType(GrouperLoaderType.GROUPER_ESB_XMMP_LISTENER);
1590         hib3GrouploaderLog.setStatus(GrouperLoaderStatus.CONFIG_ERROR.name());
1591         hib3GrouploaderLog.store();
1592 
1593       } catch (Exception e2) {
1594         LOG.error("Problem logging to loader db log", e2);
1595       }
1596     }
1597     return changesMade;
1598   }
1599   
1600   /**
1601    * @param group
1602    * @param grouperSession
1603    * @return status
1604    */
1605   public static String runJobOnceForGroup(GrouperSession grouperSession, Group group) {
1606     return runJobOnceForGroup(grouperSession, group, false);
1607   }
1608 
1609   /**
1610    * @param group
1611    * @param grouperSession
1612    * @param runOnDaemon
1613    * @return status
1614    */
1615   public static String runJobOnceForGroup(GrouperSession grouperSession, Group group, boolean runOnDaemon) {
1616     
1617     boolean loggerInitted = GrouperLoaderLogger.initializeThreadLocalMap("overallLog");
1618 
1619     try {
1620   
1621       @SuppressWarnings("deprecation")
1622       boolean isSqlLoader = group.hasType(GroupTypeFinder.find("grouperLoader", false));
1623       boolean isLdapLoader = false;
1624       
1625       String grouperLoaderTypeString = null;
1626         
1627       if (!isSqlLoader) {
1628         AttributeDefName grouperLoaderLdapTypeAttributeDefName = AttributeDefNameFinder.findByName(LoaderLdapUtils.grouperLoaderLdapName(), false);
1629         AttributeAssign attributeAssign = grouperLoaderLdapTypeAttributeDefName == null ? null : 
1630           group.getAttributeDelegate().retrieveAssignment(
1631             null, grouperLoaderLdapTypeAttributeDefName, false, false);
1632         if (attributeAssign != null) {
1633           grouperLoaderTypeString = attributeAssign.getAttributeValueDelegate().retrieveValueString(LoaderLdapUtils.grouperLoaderLdapTypeName());
1634           isLdapLoader = true;
1635         }
1636       } else {
1637         grouperLoaderTypeString = GrouperLoaderType.attributeValueOrDefaultOrNull(group, GROUPER_LOADER_TYPE);
1638         if (!StringUtils.isBlank(grouperLoaderTypeString)) {
1639           isSqlLoader = true;
1640         }
1641       }
1642       
1643       if (StringUtils.isBlank(grouperLoaderTypeString)) {
1644         
1645         throw new RuntimeException("Cant find grouper loader type of group: " + group.getName());
1646       }
1647       
1648       GrouperLoaderType grouperLoaderType = GrouperLoaderType.valueOfIgnoreCase(grouperLoaderTypeString, true);
1649       String jobName = grouperLoaderType.name() + "__" + group.getName() + "__" + group.getUuid();
1650       
1651       if (runOnDaemon) {
1652         return runOnceByJobName(grouperSession, jobName, true);
1653       }
1654       
1655       Hib3GrouperLoaderLog3GrouperLoaderLog.html#Hib3GrouperLoaderLog">Hib3GrouperLoaderLog hib3GrouperLoaderLog = new Hib3GrouperLoaderLog();
1656       hib3GrouperLoaderLog.setJobScheduleType("MANUAL_FROM_GSH");
1657       hib3GrouperLoaderLog.setJobName(jobName);
1658       hib3GrouperLoaderLog.setJobType(grouperLoaderTypeString);
1659       
1660       if (isSqlLoader) {
1661         GrouperLoaderJob.runJob(hib3GrouperLoaderLog, group, grouperSession);
1662       }
1663       
1664       if (isLdapLoader) {
1665         GrouperLoaderJob.runJobLdap(hib3GrouperLoaderLog, group, grouperSession);
1666       }
1667       
1668       String status = "SUBJECT_PROBLEMS".equals(hib3GrouperLoaderLog.getStatus()) ? "with subject problems" :
1669         "successfully";
1670       
1671       return "loader " + (isDryRun() ? "dry " : "") + "ran " + status + ", " + (isDryRun() ? "would have " : "") + "inserted " + hib3GrouperLoaderLog.getInsertCount()
1672         + " memberships, " + (isDryRun() ? "would have " : "") + "deleted " + hib3GrouperLoaderLog.getDeleteCount() + " memberships, total membership count: "
1673         + hib3GrouperLoaderLog.getTotalCount() + ", unresolvable subjects: " + hib3GrouperLoaderLog.getUnresolvableSubjectCount();
1674     } catch (Exception e) {
1675       throw new RuntimeException(e);
1676     } finally {
1677       if (loggerInitted) {
1678         GrouperLoaderLogger.doTheLogging("overallLog");
1679       }
1680     }      
1681   }
1682   
1683   /**
1684    * @param grouperSession
1685    * @param jobName
1686    * @return status
1687    */
1688   public static String runOnceByJobName(GrouperSession grouperSession, String jobName) {
1689     return runOnceByJobName(grouperSession, jobName, false);
1690   }
1691   
1692   
1693   /**
1694    * @param grouperSession
1695    * @param jobName
1696    * @param runOnDaemon
1697    * @return status
1698    */
1699   public static String runOnceByJobName(GrouperSession grouperSession, String jobName, boolean runOnDaemon) {
1700     try {
1701       
1702       if (runOnDaemon) {
1703         if (isDryRun()) {
1704           throw new RuntimeException("Dry run not supported if running on daemon.");
1705         }
1706         
1707         if (!isJobEnabled(jobName)) {
1708           throw new RuntimeException("Job " + jobName + " is not enabled.");
1709         }
1710   
1711         try {
1712           Scheduler scheduler = GrouperLoader.schedulerFactory().getScheduler();
1713           JobKey jobKey = new JobKey(jobName);
1714           scheduler.triggerJob(jobKey);
1715         } catch (SchedulerException e) {
1716           throw new RuntimeException(e);
1717         }
1718         
1719         return "Job successfully scheduled on daemon";
1720       }
1721   
1722       GrouperLoaderType grouperLoaderType = GrouperLoaderType.typeForThisName(jobName);
1723       if (grouperLoaderType.equals(GrouperLoaderType.SQL_SIMPLE) || grouperLoaderType.equals(GrouperLoaderType.SQL_GROUP_LIST)) {
1724         
1725         int uuidIndexStart = jobName.lastIndexOf("__");
1726       
1727         String grouperLoaderGroupUuid = jobName.substring(uuidIndexStart+2, jobName.length());
1728         Group group = GroupFinder.findByUuid(grouperSession, grouperLoaderGroupUuid, true);
1729         return runJobOnceForGroup(grouperSession, group);
1730       } else if (grouperLoaderType.equals(GrouperLoaderType.ATTR_SQL_SIMPLE)) {
1731         int uuidIndexStart = jobName.lastIndexOf("__");
1732         
1733         String grouperLoaderAttributeDefUuid = jobName.substring(uuidIndexStart+2, jobName.length());
1734         AttributeDef attributeDef = AttributeDefFinder.findById(grouperLoaderAttributeDefUuid, true);
1735         return runJobOnceForAttributeDef(grouperSession, attributeDef);
1736         
1737       }
1738       Hib3GrouperLoaderLog3GrouperLoaderLog.html#Hib3GrouperLoaderLog">Hib3GrouperLoaderLog hib3GrouperLoaderLog = new Hib3GrouperLoaderLog();
1739       hib3GrouperLoaderLog.setJobScheduleType("MANUAL_FROM_GSH");
1740       hib3GrouperLoaderLog.setJobName(jobName);
1741       GrouperLoaderJob.runJob(hib3GrouperLoaderLog, (Group)null, grouperSession);
1742       
1743       return "loader ran successfully: " + hib3GrouperLoaderLog.getJobMessage();
1744     } catch (Exception e) {
1745       throw new RuntimeException(e);
1746     }
1747   }
1748   
1749   /**
1750    * @param attributeDef
1751    * @param grouperSession
1752    * @return status
1753    */
1754   public static Hib3GrouperLoaderLog _internal_runJobOnceForAttributeDef(GrouperSession grouperSession, AttributeDef attributeDef) {
1755     try {
1756       Hib3GrouperLoaderLog3GrouperLoaderLog.html#Hib3GrouperLoaderLog">Hib3GrouperLoaderLog hib3GrouperLoaderLog = new Hib3GrouperLoaderLog();
1757       hib3GrouperLoaderLog.setJobScheduleType("MANUAL_FROM_GSH");
1758       
1759       if (!attributeDef.getAttributeDelegate().hasAttributeByName(GrouperCheckConfig.attributeLoaderStemName() + ":attributeLoader")) {
1760         throw new RuntimeException("Cant find attributeLoader type of attributeDef: " + attributeDef.getName());
1761       }
1762       String grouperLoaderTypeString = attributeDef.getAttributeValueDelegate()
1763         .retrieveValueString(GrouperCheckConfig.attributeLoaderStemName() + ":attributeLoaderType");
1764 
1765       GrouperLoaderType grouperLoaderType = GrouperLoaderType.valueOfIgnoreCase(grouperLoaderTypeString, true);
1766       
1767       hib3GrouperLoaderLog.setJobName(grouperLoaderType.name() + "__" + attributeDef.getName() + "__" + attributeDef.getUuid());
1768       hib3GrouperLoaderLog.setJobType(grouperLoaderTypeString);
1769   
1770       GrouperLoaderJob.runJobAttrDef(hib3GrouperLoaderLog, attributeDef, grouperSession);
1771       
1772       return hib3GrouperLoaderLog;
1773     } catch (Exception e) {
1774       throw new RuntimeException(e);
1775     }
1776 
1777   }
1778   /**
1779    * @param attributeDef
1780    * @param grouperSession
1781    * @return status
1782    */
1783   public static String runJobOnceForAttributeDef(GrouperSession grouperSession, AttributeDef attributeDef) {
1784 
1785     Hib3GrouperLoaderLog hib3GrouperLoaderLog = _internal_runJobOnceForAttributeDef(grouperSession, attributeDef);
1786     
1787     return "loader " + (isDryRun() ? "dry " : "") + "ran successfully, " + (isDryRun() ? "would have " : "") + "inserted " + hib3GrouperLoaderLog.getInsertCount()
1788       + " attrDefNames, " + (isDryRun() ? "would have " : "") + "deleted " + hib3GrouperLoaderLog.getDeleteCount() + " records, total record count: "
1789       + hib3GrouperLoaderLog.getTotalCount();
1790   }
1791 
1792   /**
1793    * schedule rules job
1794    */
1795   public static int scheduleGroupSyncJobs() {
1796   
1797     Map<String, ClientGroupConfigBean> clientGroupConfigBeanCache = ClientConfig.clientGroupConfigBeanCache();
1798     
1799     Set<String> groupSyncJobNames = new HashSet<String>();
1800     
1801     int changesMade = 0;
1802     
1803     //loop through all of them configured
1804     for (String localGroupName : clientGroupConfigBeanCache.keySet()) {
1805       
1806       ClientGroupConfigBean clientGroupConfigBean = clientGroupConfigBeanCache.get(localGroupName);
1807       
1808       if (StringUtils.isBlank(clientGroupConfigBean.getLocalGroupName())) {
1809         LOG.error("Why is local group name blank? " + clientGroupConfigBean.getConfigId());
1810       }
1811       
1812       String jobName = GrouperLoaderType.GROUPER_GROUP_SYNC + "__" + clientGroupConfigBean.getLocalGroupName();
1813       groupSyncJobNames.add(jobName);
1814 
1815       //schedule the job
1816       try {
1817       
1818         //at this point we have all the attributes and we know the required ones are there, and logged when 
1819         //forbidden ones are there
1820     
1821         //the name of the job must be unique, so use the group name since one job per group (at this point)
1822         JobDetail jobDetail = JobBuilder.newJob(GrouperLoaderJob.class)
1823           .withIdentity(jobName)
1824           .build();
1825     
1826         //schedule this job daily at 6am
1827         GrouperLoaderScheduleType grouperLoaderScheduleType = GrouperLoaderScheduleType.CRON;
1828     
1829         Trigger trigger = grouperLoaderScheduleType.createTrigger("trigger_" + jobName, Trigger.DEFAULT_PRIORITY, clientGroupConfigBean.getCron(), null);
1830         
1831         if (scheduleJobIfNeeded(jobDetail, trigger)) {
1832           changesMade++;
1833         }
1834     
1835     
1836       } catch (Exception e) {
1837         
1838         //log and continue so we can schedule the other ones...
1839         
1840         String errorMessage = "Could not schedule job: " + jobName;
1841         LOG.error(errorMessage, e);
1842         errorMessage += "\n" + ExceptionUtils.getFullStackTrace(e);
1843         try {
1844           //lets enter a log entry so it shows up as error in the db
1845           Hib3GrouperLoaderLogib3GrouperLoaderLog.html#Hib3GrouperLoaderLog">Hib3GrouperLoaderLog hib3GrouploaderLog = new Hib3GrouperLoaderLog();
1846           hib3GrouploaderLog.setHost(GrouperUtil.hostname());
1847           hib3GrouploaderLog.setJobMessage(errorMessage);
1848           hib3GrouploaderLog.setJobName(GrouperLoaderType.GROUPER_GROUP_SYNC);
1849           hib3GrouploaderLog.setJobScheduleQuartzCron(clientGroupConfigBean.getCron());
1850           hib3GrouploaderLog.setJobScheduleType(GrouperLoaderScheduleType.CRON.name());
1851           hib3GrouploaderLog.setJobType(GrouperLoaderType.MAINTENANCE.name());
1852           hib3GrouploaderLog.setStatus(GrouperLoaderStatus.CONFIG_ERROR.name());
1853           hib3GrouploaderLog.store();
1854           
1855         } catch (Exception e2) {
1856           LOG.error("Problem logging to loader db log", e2);
1857         }
1858       }
1859       
1860       
1861     }
1862     
1863     // check to see if anything should be unscheduled.
1864     try {
1865       Scheduler scheduler = GrouperLoader.schedulerFactory().getScheduler();
1866 
1867       for (JobKey jobKey : scheduler.getJobKeys(GroupMatcher.jobGroupEquals("DEFAULT"))) {
1868         
1869         String jobName = jobKey.getName();
1870         
1871         if (jobName.startsWith(GrouperLoaderType.GROUPER_GROUP_SYNC + "__") && !groupSyncJobNames.contains(jobName)) {
1872           try {
1873             String triggerName = "trigger_" + jobName;
1874             if (scheduler.unscheduleJob(TriggerKey.triggerKey(triggerName))) {
1875               changesMade++;
1876             }
1877           } catch (Exception e) {
1878             String errorMessage = "Could not unschedule job: '" + jobName + "'";
1879             LOG.error(errorMessage, e);
1880             errorMessage += "\n" + ExceptionUtils.getFullStackTrace(e);
1881             try {
1882               //lets enter a log entry so it shows up as error in the db
1883               Hib3GrouperLoaderLogib3GrouperLoaderLog.html#Hib3GrouperLoaderLog">Hib3GrouperLoaderLog hib3GrouploaderLog = new Hib3GrouperLoaderLog();
1884               hib3GrouploaderLog.setHost(GrouperUtil.hostname());
1885               hib3GrouploaderLog.setJobMessage(errorMessage);
1886               hib3GrouploaderLog.setJobName(jobName);
1887               hib3GrouploaderLog.setJobType(GrouperLoaderType.MAINTENANCE.name());
1888               hib3GrouploaderLog.setStatus(GrouperLoaderStatus.CONFIG_ERROR.name());
1889               hib3GrouploaderLog.store();
1890               
1891             } catch (Exception e2) {
1892               LOG.error("Problem logging to loader db log", e2);
1893             }
1894           }
1895         }
1896       }
1897     } catch (Exception e) {
1898       
1899       String errorMessage = "Could not query group sync jobs to see if any should be unscheduled.";
1900       LOG.error(errorMessage, e);
1901       errorMessage += "\n" + ExceptionUtils.getFullStackTrace(e);
1902       try {
1903         //lets enter a log entry so it shows up as error in the db
1904         Hib3GrouperLoaderLogib3GrouperLoaderLog.html#Hib3GrouperLoaderLog">Hib3GrouperLoaderLog hib3GrouploaderLog = new Hib3GrouperLoaderLog();
1905         hib3GrouploaderLog.setHost(GrouperUtil.hostname());
1906         hib3GrouploaderLog.setJobMessage(errorMessage);
1907         hib3GrouploaderLog.setJobType(GrouperLoaderType.MAINTENANCE.name());
1908         hib3GrouploaderLog.setStatus(GrouperLoaderStatus.CONFIG_ERROR.name());
1909         hib3GrouploaderLog.store();
1910         
1911       } catch (Exception e2) {
1912         LOG.error("Problem logging to loader db log", e2);
1913       }
1914     }
1915     return changesMade;
1916   }
1917 
1918   /**
1919    * schedule psp full sync job
1920    */
1921   public static boolean schedulePspFullSyncJob() {
1922   
1923     String cronString = null;
1924     
1925     //this is a medium priority job
1926     int priority = 5;
1927   
1928     //schedule the job
1929     try {
1930       Scheduler scheduler = GrouperLoader.schedulerFactory().getScheduler();
1931 
1932       cronString = GrouperLoaderConfig.retrieveConfig().propertyValueString("changeLog.psp.fullSync.quartzCron");
1933      
1934       String triggerName = "trigger_" + GrouperLoaderType.PSP_FULL_SYNC.name();
1935       
1936       boolean unscheduleAndReturn = false;
1937       
1938       if (StringUtils.isEmpty(cronString)) {
1939         LOG.warn("Full synchronization provisioning jobs are not scheduled. To schedule full synchronization jobs, " +
1940                  "set grouper-loader.properties key 'changeLog.psp.fullSync.quartzCron' to a cron expression.");
1941         unscheduleAndReturn = true;
1942       } else if (StringUtils.isEmpty(GrouperLoaderConfig.retrieveConfig().propertyValueString("changeLog.psp.fullSync.class"))) {
1943         LOG.warn("Unable to run a full synchronization provisioning job. " +
1944             "Set grouper-loader.properties key 'changeLog.psp.fullSync.class' to the name of the class providing a fullSync() method.");
1945         unscheduleAndReturn = true;
1946       }
1947       
1948       if (unscheduleAndReturn) {
1949         return scheduler.unscheduleJob(TriggerKey.triggerKey(triggerName));
1950       }
1951       
1952       LOG.info("Scheduling " + GrouperLoaderType.PSP_FULL_SYNC.name());
1953         
1954       //at this point we have all the attributes and we know the required ones are there, and logged when 
1955       //forbidden ones are there
1956 
1957       //the name of the job must be unique
1958       JobDetail jobDetail = JobBuilder.newJob(GrouperLoaderJob.class)
1959         .withIdentity(GrouperLoaderType.PSP_FULL_SYNC.name())
1960         .build();
1961   
1962       //schedule this job
1963       GrouperLoaderScheduleType grouperLoaderScheduleType = GrouperLoaderScheduleType.CRON;
1964   
1965       Trigger trigger = grouperLoaderScheduleType.createTrigger(triggerName, priority, cronString, null);
1966   
1967       return scheduleJobIfNeeded(jobDetail, trigger);
1968   
1969     } catch (Exception e) {
1970       String errorMessage = "Could not schedule job: '" + GrouperLoaderType.PSP_FULL_SYNC.name() + "'";
1971       LOG.error(errorMessage, e);
1972       errorMessage += "\n" + ExceptionUtils.getFullStackTrace(e);
1973       try {
1974         //lets enter a log entry so it shows up as error in the db
1975         Hib3GrouperLoaderLogib3GrouperLoaderLog.html#Hib3GrouperLoaderLog">Hib3GrouperLoaderLog hib3GrouploaderLog = new Hib3GrouperLoaderLog();
1976         hib3GrouploaderLog.setHost(GrouperUtil.hostname());
1977         hib3GrouploaderLog.setJobMessage(errorMessage);
1978         hib3GrouploaderLog.setJobName(GrouperLoaderType.PSP_FULL_SYNC.name());
1979         hib3GrouploaderLog.setJobSchedulePriority(priority);
1980         hib3GrouploaderLog.setJobScheduleQuartzCron(cronString);
1981         hib3GrouploaderLog.setJobScheduleType(GrouperLoaderScheduleType.CRON.name());
1982         hib3GrouploaderLog.setJobType(GrouperLoaderType.PSP_FULL_SYNC.name());
1983         hib3GrouploaderLog.setStatus(GrouperLoaderStatus.CONFIG_ERROR.name());
1984         hib3GrouploaderLog.store();
1985         
1986       } catch (Exception e2) {
1987         LOG.error("Problem logging to loader db log", e2);
1988       }
1989     }
1990     return false;
1991   }
1992   
1993    /**
1994    * if there is a threadlocal, then we are in dry run mode
1995    */
1996 
1997   private static ThreadLocal<GrouperLoaderDryRunBean> threadLocalGrouperLoaderDryRun = new ThreadLocal<GrouperLoaderDryRunBean>();
1998   
1999   
2000   
2001   
2002   /**
2003    * @return the threadLocalGrouperLoaderDryRun
2004    */
2005   public static GrouperLoaderDryRunBean internal_retrieveThreadLocalGrouperLoaderDryRun() {
2006     return threadLocalGrouperLoaderDryRun.get();
2007   }
2008 
2009   
2010   /**
2011    * @param theThreadLocalGrouperLoaderDryRun the threadLocalGrouperLoaderDryRun to set
2012    */
2013   public static void internal_assignThreadLocalGrouperLoaderDryRun(
2014       GrouperLoaderDryRunBean theThreadLocalGrouperLoaderDryRun) {
2015     if (theThreadLocalGrouperLoaderDryRun == null) {
2016       threadLocalGrouperLoaderDryRun.remove();
2017     } else {
2018       threadLocalGrouperLoaderDryRun.set(theThreadLocalGrouperLoaderDryRun);
2019     }
2020   }
2021 
2022   /**
2023    * bean holds where the logging goes, and if there, then it means we are in dry run mode
2024    *
2025    */
2026   public static class GrouperLoaderDryRunBean {
2027     
2028     /**
2029      * filewriter for output
2030      */
2031     private FileWriter fileWriter;
2032     
2033     /**
2034      * file
2035      */
2036     private File file;
2037 
2038     /**
2039      * construct
2040      * @param fileName
2041      */
2042     public GrouperLoaderDryRunBean(String fileName) {
2043       if (!StringUtils.isBlank(fileName)) {
2044         this.file = new File(fileName); 
2045         try {
2046           this.fileWriter = new FileWriter(this.file);
2047         } catch (IOException ioe) {
2048           throw new RuntimeException("Cant open file: " + fileName, ioe);
2049         }
2050       }
2051     }
2052     
2053     /**
2054      * finish everything up
2055      * @param success
2056      */
2057     public void finish(boolean success) {
2058       if (this.fileWriter != null) {
2059         try {
2060           this.fileWriter.close();
2061         } catch (IOException ioe) {
2062           throw new RuntimeException("Problem closing file: " + this.file.getAbsolutePath(), ioe);
2063         }
2064         System.out.println("Wrote dry run to file: " + this.file.getAbsolutePath() + ", succcess? " + success);
2065       }
2066     }
2067     
2068     /**
2069      * write a line, it shouldnt end in newline
2070      * @param line
2071      */
2072     public void writeLine(String line) {
2073       if (this.fileWriter != null) {
2074         try {
2075           this.fileWriter.write(line);
2076           this.fileWriter.write("\n");
2077         } catch (IOException ioe) {
2078           throw new RuntimeException("Problem writing to file: " + this.file.getAbsolutePath(), ioe);
2079         }
2080       } else {
2081         System.out.println(line);
2082       }
2083     }
2084     
2085   }
2086   
2087   /**
2088    * @param group
2089    * @param grouperSession
2090    * @param fileName is the file where output should go
2091    * @return status
2092    */
2093   public static String dryRunJobOnceForGroup(final GrouperSession grouperSession, final Group group, String fileName) {
2094     
2095     //put grouepr in readonly mode
2096     HibernateSession.threadLocalReadonlyAssign();
2097 
2098     try {
2099       threadLocalGrouperLoaderDryRun.set(new GrouperLoaderDryRunBean(fileName));
2100       
2101       boolean success = false;
2102       try {
2103       
2104         String result = (String)GrouperTransaction.callbackGrouperTransaction(GrouperTransactionType.READONLY_NEW, new GrouperTransactionHandler() {
2105           
2106           /**
2107            * 
2108            */
2109           @Override
2110           public Object callback(GrouperTransaction grouperTransaction)
2111               throws GrouperDAOException {
2112             
2113             return runJobOnceForGroup(grouperSession, group);
2114             
2115           }
2116         });
2117         
2118         success = true;
2119         return result;
2120         
2121       } finally {
2122         GrouperLoaderDryRunBean grouperLoaderDryRunBean = threadLocalGrouperLoaderDryRun.get();
2123         if (grouperLoaderDryRunBean != null) {
2124           try {
2125             grouperLoaderDryRunBean.finish(success);
2126           } finally {
2127             threadLocalGrouperLoaderDryRun.remove();
2128           }
2129         }
2130       }
2131     } finally {
2132       //no longer in readonly mode
2133       HibernateSession.threadLocalReadonlyClear();
2134     }
2135   }
2136   
2137   /**
2138    * 
2139    * @return true if dry run
2140    */
2141   public static boolean isDryRun() {
2142     return threadLocalGrouperLoaderDryRun.get() != null;
2143   }
2144   
2145   /**
2146    * 
2147    * @param line
2148    */
2149   public static void dryRunWriteLine(String line) {
2150     GrouperLoaderDryRunBean grouperLoaderDryRunBean = threadLocalGrouperLoaderDryRun.get();
2151     if (grouperLoaderDryRunBean != null) {
2152       grouperLoaderDryRunBean.writeLine(line);
2153     }
2154   }
2155   
2156   /**
2157    * Schedule job if new or something has changed
2158    * @param jobDetail
2159    * @param trigger
2160    * @return true if needed an update
2161    * @throws SchedulerException 
2162    */
2163   public static boolean scheduleJobIfNeeded(JobDetail jobDetail, Trigger trigger) throws SchedulerException {
2164     Scheduler scheduler = GrouperLoader.schedulerFactory().getScheduler();
2165     
2166     boolean scheduleJob = false;
2167     boolean triggerTypeChanging = false;
2168     
2169     JobDetail oldJobDetail = scheduler.getJobDetail(jobDetail.getKey());
2170     Trigger oldTrigger = scheduler.getTrigger(trigger.getKey());
2171     Trigger.TriggerState oldTriggerState = scheduler.getTriggerState(trigger.getKey());
2172 
2173     if (oldJobDetail == null || oldTrigger == null) {
2174       scheduleJob = true;
2175     }
2176     
2177     if (!scheduleJob) {
2178 
2179       if (oldTrigger instanceof SimpleTrigger && trigger instanceof SimpleTrigger) {
2180         if (((SimpleTrigger)oldTrigger).getRepeatInterval() != ((SimpleTrigger)trigger).getRepeatInterval()) {
2181           scheduleJob = true;
2182         }
2183       } else if (oldTrigger instanceof CronTrigger && trigger instanceof CronTrigger) {
2184         if (!((CronTrigger)oldTrigger).getCronExpression().equals(((CronTrigger)trigger).getCronExpression())) {
2185           scheduleJob = true;
2186         }
2187       } else {
2188         triggerTypeChanging = true;
2189         scheduleJob = true;
2190       }
2191     }
2192     
2193     if (!scheduleJob && oldJobDetail.getJobClass() != jobDetail.getJobClass()) {
2194       scheduleJob = true;
2195     }
2196     
2197     if (!scheduleJob && oldTrigger.getPriority() != trigger.getPriority()) {
2198       scheduleJob = true;
2199     }
2200     
2201     if (!scheduleJob) {
2202       if (oldJobDetail.getJobDataMap() == null && jobDetail.getJobDataMap() == null) {
2203         // ok
2204       } else if (oldJobDetail.getJobDataMap() == null || jobDetail.getJobDataMap() == null) {
2205         scheduleJob = true;
2206       } else if (!oldJobDetail.getJobDataMap().equals(jobDetail.getJobDataMap())) {
2207         scheduleJob = true;
2208       }
2209     }
2210     
2211     if (scheduleJob) {
2212       // apparently running this for a job that already exists when bringing the daemon up on a different host may fire some
2213       // jobs concurrently with jobs on another host.  so only updating if there's something to update.
2214       
2215       if (triggerTypeChanging) {
2216         // if this isn't done, then the schedule in quartz gets messed up..
2217         scheduler.unscheduleJob(oldTrigger.getKey());
2218       }
2219       
2220       scheduler.scheduleJob(jobDetail, GrouperUtil.toSet(trigger), true);
2221       if (oldTriggerState == Trigger.TriggerState.PAUSED) {
2222         // old trigger was disabled so disable this one too.  need a better way..
2223         scheduler.pauseTrigger(trigger.getKey());
2224       }
2225       
2226       LOG.info("Scheduled quartz job: " + jobDetail.getKey().getName());
2227       return true;
2228     }
2229     
2230     return false;
2231   }
2232   
2233   /**
2234    * @param jobName
2235    * @return true if the job appears to currently be running
2236    */
2237   public static boolean isJobRunning(String jobName) {
2238     
2239     long assumeJobKilledIfNoUpdateInMillis = 1000L * GrouperConfig.retrieveConfig().propertyValueInt("loader.assumeJobKilledIfNoUpdateInSeconds", 300);
2240     Long count = HibernateSession.byHqlStatic()
2241         .createQuery("select count(*) from Hib3GrouperLoaderLog where jobName = :jobName and status = 'STARTED' and lastUpdated > :lastUpdated")
2242         .setString("jobName", jobName)
2243         .setTimestamp("lastUpdated", new Date(System.currentTimeMillis() - assumeJobKilledIfNoUpdateInMillis))
2244         .uniqueResult(Long.class);
2245     
2246     return count > 0;
2247   }
2248   
2249   /**
2250    * @param jobName 
2251    * @return date or null if not running
2252    */
2253   public static Date internal_getJobStartTimeIfRunning(String jobName) {
2254     
2255     // all of our jobs should have the DisallowConcurrentExecution annotation so this should only return 1 at most
2256     List<String> firedTimes = HibernateSession.bySqlStatic().listSelect(String.class, "select fired_time from grouper_QZ_FIRED_TRIGGERS where state='EXECUTING' and job_name = ? and fired_time is not null", 
2257         GrouperUtil.toListObject(jobName), HibUtils.listType(StringType.INSTANCE));
2258     if (firedTimes != null && firedTimes.size() > 0 && firedTimes.get(0) != null) {
2259       return new Date(Long.parseLong(firedTimes.get(0)));
2260     }
2261     
2262     return null;
2263   }
2264   
2265   /**
2266    * @param jobName
2267    * @return true is job is enabled
2268    */
2269   public static boolean isJobEnabled(String jobName) {
2270 
2271     try {
2272       Scheduler scheduler = GrouperLoader.schedulerFactory().getScheduler();
2273       
2274       List<? extends Trigger> triggers = scheduler.getTriggersOfJob(new JobKey(jobName));
2275   
2276       for (Trigger trigger : triggers) {
2277         
2278         Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
2279         if (triggerState == Trigger.TriggerState.COMPLETE) {
2280           // looks like this trigger is done so skip it
2281           continue;
2282         }
2283         
2284         if (triggerState != Trigger.TriggerState.PAUSED) {
2285           return true;
2286         }
2287       }
2288      
2289       return false;
2290     } catch (SchedulerException e) {
2291       throw new RuntimeException(e);
2292     }
2293   }
2294 }