1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package edu.internet2.middleware.grouper.app.gsh;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.lang.reflect.Method;
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Map;
31
32 import jline.TerminalFactory;
33
34 import org.apache.commons.lang.exception.ExceptionUtils;
35 import org.apache.commons.lang3.ArrayUtils;
36 import org.apache.commons.lang3.StringUtils;
37 import org.apache.commons.logging.Log;
38 import org.apache.commons.logging.LogFactory;
39 import org.codehaus.groovy.control.CompilerConfiguration;
40 import org.codehaus.groovy.tools.shell.ExitNotification;
41 import org.codehaus.groovy.tools.shell.Groovysh;
42 import org.codehaus.groovy.tools.shell.IO;
43 import org.codehaus.groovy.tools.shell.util.Logger;
44
45 import bsh.Interpreter;
46 import edu.internet2.middleware.grouper.GrouperSession;
47 import edu.internet2.middleware.grouper.SubjectFinder;
48 import edu.internet2.middleware.grouper.app.gsh.jline.WindowsTerminal;
49 import edu.internet2.middleware.grouper.app.gsh.template.GshTemplateReturnException;
50 import edu.internet2.middleware.grouper.app.loader.GrouperLoader;
51 import edu.internet2.middleware.grouper.audit.GrouperEngineBuiltin;
52 import edu.internet2.middleware.grouper.cfg.GrouperConfig;
53 import edu.internet2.middleware.grouper.hibernate.GrouperContext;
54 import edu.internet2.middleware.grouper.hibernate.HibernateSession;
55 import edu.internet2.middleware.grouper.hooks.beans.GrouperContextTypeBuiltIn;
56 import edu.internet2.middleware.grouper.misc.GrouperStartup;
57 import edu.internet2.middleware.grouper.util.GrouperUtil;
58 import edu.internet2.middleware.grouper.xml.export.XmlExportGsh;
59 import edu.internet2.middleware.grouper.xml.importXml.XmlImportGsh;
60
61
62
63
64
65
66
67
68 public class GrouperShell {
69
70
71 static boolean exitOnFailure = false;
72
73 private static Map<String, String> mainLookups = new HashMap<String, String>();
74
75 static {
76 mainLookups.put("-xmlimportold",
77 "edu.internet2.middleware.grouper.xml.XmlImporter");
78
79 mainLookups.put("-xmlexportold",
80 "edu.internet2.middleware.grouper.xml.XmlExporter");
81
82 mainLookups.put("-xmlimport",
83 XmlImportGsh.class.getName());
84
85 mainLookups.put("-xmlexport",
86 XmlExportGsh.class.getName());
87
88 mainLookups.put("-test",
89 "edu.internet2.middleware.grouper.AllTests");
90
91 mainLookups.put("-loader",
92 "edu.internet2.middleware.grouper.app.loader.GrouperLoader");
93
94 mainLookups.put("-usdu",
95 "edu.internet2.middleware.grouper.app.usdu.USDU");
96
97 mainLookups.put("-registry",
98 "edu.internet2.middleware.grouper.registry.RegistryInitializeSchema");
99
100 mainLookups.put("-findbadmemberships",
101 "edu.internet2.middleware.grouper.misc.FindBadMemberships");
102
103 mainLookups.put("-ldappc",
104 "edu.internet2.middleware.ldappc.Ldappc");
105
106 mainLookups.put("-ldappcng",
107 "edu.internet2.middleware.ldappc.spml.PSPCLI");
108
109 mainLookups.put("-psp",
110 "edu.internet2.middleware.psp.PspCLI");
111
112 mainLookups.put("-pspngattributestoprovisioningattributes",
113 "edu.internet2.middleware.grouper.app.provisioning.PspngToNewProvisioningAttributeConversion");
114
115 }
116
117
118 protected static final String NAME = "gsh";
119
120
121
122 private static final String GSH_DEBUG = "GSH_DEBUG";
123 private static final String GSH_DEVEL = "GSH_DEVEL";
124 private static final String GSH_HISTORY = "_GSH_HISTORY";
125 private static final String GSH_OURS = "_GSH_OURS";
126 private static final String GSH_SESSION = "_GSH_GROUPER_SESSION";
127 private static final String GSH_TIMER = "GSH_TIMER";
128
129
130
131 private Interpreter interpreter = null;
132 private CommandReader r = null;
133
134
135 public static boolean runFromGsh = false;
136
137
138 private static boolean runFromGshInteractive = false;
139
140 private static ThreadLocal<String> groovyPreloadString = new ThreadLocal<String>();
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159 public static void main(String args[]) {
160
161 @SuppressWarnings("unused")
162 GrouperContext grouperContext = GrouperContext.createNewDefaultContext(
163 GrouperEngineBuiltin.GSH, false, true);
164
165 boolean wasSpecialCase = handleSpecialCase(args);
166 if(wasSpecialCase) {
167 return;
168 }
169 runFromGsh = true;
170
171 GrouperStartup.runFromMain = true;
172 GrouperStartup.startup();
173 GrouperStartup.waitForGrouperStartup();
174
175
176 Log bshLogger = LogFactory.getLog("bsh");
177 if (bshLogger.isTraceEnabled()) {
178 Interpreter.TRACE = true;
179 }
180 if (bshLogger.isDebugEnabled()) {
181 Interpreter.DEBUG = true;
182 }
183 exitOnFailure = true;
184 try {
185 grouperShellHelper(args, null);
186 }
187 catch (GrouperShellException eGS) {
188 eGS.printStackTrace();
189 LOG.error("GSH is exiting: " + eGS.getMessage(), eGS);
190 System.exit(1);
191 } catch (UnsatisfiedLinkError e) {
192 if (e.getMessage() != null && e.getMessage().contains("jansi")) {
193 System.err.println("\n\n\nUnable to start GSH. Your tmpdir " + System.getProperty("java.io.tmpdir") + " may have the noexec flag set. If so, set this environment variable: export GSH_JVMARGS=\"-Dlibrary.jansi.path=/some/other/temp/path/with/exec\"\n\n\n");
194 }
195
196 throw new RuntimeException(e);
197 }
198 System.exit(0);
199 }
200
201
202
203
204
205
206
207 private static boolean handleSpecialCase(String[] args) {
208 if(args == null || args.length==0) {
209 return false;
210 }
211 if("-h".equalsIgnoreCase(args[0])||"-help".equalsIgnoreCase(args[0])) {
212 System.out.println(_getUsage());
213 return true;
214 }
215
216 if("-nocheck".equalsIgnoreCase(args[0])) {
217 GrouperStartup.ignoreCheckConfig = true;
218 return false;
219 }
220 boolean isLoader = StringUtils.equals("-loader", args[0].toLowerCase());
221 String mainClass = mainLookups.get(args[0].toLowerCase());
222 if(mainClass==null) {
223 return false;
224 }
225 String[] mainArgs = new String[args.length -1];
226 for(int i=1;i<args.length;i++) {
227 mainArgs[i-1] = args[i];
228 }
229
230 try {
231 Class claz = Class.forName(mainClass);
232
233 Method method = claz.getMethod("main", String[].class);
234 method.invoke(null, (Object)mainArgs);
235 }catch(Exception e) {
236 if (ExceptionUtils.getFullStackTrace(e).contains("PSPCLI")) {
237 String error = "Make sure you have run 'ant dist' in ldappcng, and 'ant ldappcng' in grouper to copy the libs over";
238 LOG.fatal(error);
239 System.err.println(error);
240 }
241 if(e instanceof RuntimeException) {
242 throw (RuntimeException)e;
243 }
244 throw new RuntimeException(e);
245 } finally {
246 if (!isLoader) {
247 try {
248 GrouperLoader.shutdownIfStarted();
249 } catch (Exception e) {
250 LOG.error("error shutting down loader", e);
251 }
252 }
253 }
254
255 return true;
256 }
257
258
259
260
261
262
263
264 static void grouperShellHelper(String args[], InputStream inputStreamParam) throws GrouperShellException {
265
266 System.out.println("Type help() for instructions");
267
268 GrouperContextTypeBuiltIn.setDefaultContext(GrouperContextTypeBuiltIn.GSH);
269
270 boolean forceLegacyGsh = false;
271 if (args != null && args.length > 0 && args[0].equalsIgnoreCase("-forceLegacyGsh")) {
272 forceLegacyGsh = true;
273 args = ArrayUtils.remove(args, 0);
274 }
275
276 boolean lightweightProfile = false;
277 if (args != null && args.length > 0 && args[0].equalsIgnoreCase("-lightWeightProfile")) {
278 lightweightProfile = true;
279 args = ArrayUtils.remove(args, 0);
280 }
281
282 if (forceLegacyGsh || GrouperConfig.retrieveConfig().propertyValueBoolean("gsh.useLegacy", false)) {
283 new GrouperShell( new ShellCommandReader(args, inputStreamParam )).run();
284 } else {
285
286 TerminalFactory.registerFlavor(TerminalFactory.Flavor.WINDOWS, WindowsTerminal.class);
287
288 StringBuilder body = new StringBuilder();
289
290 if (lightweightProfile) {
291 if (args != null && args.length > 0 && !args[0].equalsIgnoreCase("-check") && !args[0].equals("-runarg")) {
292 body.append(":load '" + GrouperUtil.fileFromResourceName("groovysh_lightWeightWithFile.profile").getAbsolutePath() + "'");
293 } else {
294 body.append(":load '" + GrouperUtil.fileFromResourceName("groovysh_lightWeight.profile").getAbsolutePath() + "'");
295 }
296 } else {
297 body.append(":load '" + GrouperUtil.fileFromResourceName("groovysh.profile").getAbsolutePath() + "'");
298 }
299 if (args != null && args.length > 0 && !args[0].equalsIgnoreCase("-check")) {
300
301 if (args[0].equals("-main")) {
302 if (args.length == 1) {
303 throw new RuntimeException("When passing -main, pass at least one more argument, the class to run");
304 }
305
306
307 body.append("\n" + args[1] + ".main(");
308
309 for(int i = 2; i < args.length; i++) {
310 body.append("\"" + args[i] + "\"");
311 if ((i + 1) < args.length) {
312 body.append(", ");
313 }
314 }
315
316 body.append(")");
317 } else if (args[0].equals("-runarg")) {
318 if (args.length == 1) {
319 throw new RuntimeException("When passing -runarg, pass one other argument, the gsh command to run");
320 }
321
322 String commands = args[1];
323
324 commands = commands.replace("\\n", "\n");
325 body.append("\n" + commands);
326 } else {
327 body.append("\n" + ":gshFileLoad '" + args[0] + "'");
328 }
329
330 body.append("\n:exit");
331 } else if (inputStreamParam != null) {
332 throw new RuntimeException("Unexpected (for now at least)");
333 } else {
334 runFromGshInteractive = true;
335 }
336
337 groovyPreloadString.set(body.toString());
338
339
340 boolean exitOnError = !GrouperShell.runFromGshInteractive && GrouperConfig.retrieveConfig().propertyValueBoolean("gsh.exitOnNonInteractiveError", false);
341
342 org.codehaus.groovy.tools.shell.Main.setTerminalType(TerminalFactory.AUTO, false);
343 IO io = new IO();
344 CompilerConfiguration compilerConfiguration = new CompilerConfiguration();
345 Logger.io = io;
346 compilerConfiguration.setParameters(false);
347
348 final Groovysh shell = new GrouperGroovysh(io, compilerConfiguration, exitOnError);
349
350 Runtime.getRuntime().addShutdownHook(new Thread() {
351 public void run() {
352 try {
353 if (shell.getHistory() != null) {
354 shell.getHistory().flush();
355 }
356 } catch (IOException e) {
357 System.err.println("Error flushing GSH history: " + ExceptionUtils.getFullStackTrace(e));
358 }
359 }
360 });
361
362 int code = 0;
363 try {
364 code = shell.run(body.toString());
365 } catch (GshTemplateReturnException e) {
366 }
367
368 System.exit(code);
369 }
370 }
371
372
373
374
375
376 public static void handleFileLoadError(String command, Throwable t) {
377 if (t instanceof GshTemplateReturnException) {
378 return;
379 }
380
381
382
383 boolean exitOnNonInteractiveError = !GrouperShell.runFromGshInteractive && GrouperConfig.retrieveConfig().propertyValueBoolean("gsh.exitOnNonInteractiveError", false);
384 String error = "Error while running command (" + command + ")";
385 if (exitOnNonInteractiveError) {
386
387 LOG.error("Error in command '" + command + "'", t);
388 throw new RuntimeException(error, t);
389 }
390
391 System.err.println(error + ": " + ExceptionUtils.getFullStackTrace(t));
392 }
393
394
395
396
397 public static String getGroovyPreloadString() {
398 return groovyPreloadString.get();
399 }
400
401
402
403
404 protected GrouperShell(CommandReader r)
405 throws GrouperShellException
406 {
407 this.interpreter = r.getInterpreter();
408 this.r = r;
409 }
410
411
412
413
414
415 protected static void error(Interpreter i, Exception e) {
416 error(i, e, e.getMessage());
417 }
418
419
420 protected static void error(Interpreter interpreter, Exception e, String msg) {
421 interpreter.error(msg);
422 LOG.error("Error in GSH: " + msg, e);
423 if (isDebug(interpreter)) {
424 e.printStackTrace();
425 }
426 if (ShellHelper.closeOpenTransactions(interpreter, e)) {
427 ShellHelper.exitDueToOpenTransaction(interpreter);
428 }
429 }
430
431
432 protected static Object get(Interpreter i, String key)
433 throws bsh.EvalError
434 {
435 return i.get(key);
436 }
437
438
439 protected static List getHistory(Interpreter i)
440 throws bsh.EvalError
441 {
442 List history = (ArrayList) GrouperShell.get(i, GSH_HISTORY);
443 if (history == null) {
444 history = new ArrayList();
445 }
446 return history;
447 }
448
449
450 protected static GrouperSession getSession(Interpreter i)
451 throws GrouperShellException
452 {
453 try {
454
455 GrouperSession s = null;
456
457 if (GrouperConfig.retrieveConfig().propertyValueBoolean("grouper.gsh.useStaticGrouperSessionFirst", true)) {
458 s = GrouperSession.staticGrouperSession(false);
459 }
460 if (s==null) {
461 s = (GrouperSession) GrouperShell.get(i, GSH_SESSION);
462 }
463 if (s != null && s.getSubjectDb() == null) {
464 s = null;
465 GrouperShell.set(i, GSH_SESSION, s);
466 }
467 if (s == null) {
468 s = GrouperSession.staticGrouperSession(false);
469
470 if (s == null) {
471 s = GrouperSession.start(
472 SubjectFinder.findRootSubject()
473 );
474 }
475 GrouperShell.set(i, GSH_SESSION, s);
476 }
477 return s;
478 }
479 catch (Exception e) {
480 if (i != null) {
481 i.error(e.getMessage());
482 }
483 throw new GrouperShellException(e);
484 }
485 }
486
487
488 protected static boolean isDebug(Interpreter i) {
489 return _isTrue(i, GSH_DEBUG);
490 }
491
492
493
494 protected static boolean isOurCommand(Interpreter i) {
495 return _isTrue(i, GSH_OURS);
496 }
497
498
499
500 protected static boolean isTimed(Interpreter i) {
501 return _isTrue(i, GSH_TIMER);
502 }
503
504
505 protected static void set(Interpreter i, String key, Object obj)
506 throws bsh.EvalError
507 {
508 i.set(key, obj);
509 }
510
511
512 protected static boolean isDevel(Interpreter i) {
513 return _isTrue(i, GSH_DEVEL);
514 }
515
516
517 protected static void setHistory(Interpreter i, int cnt, String cmd)
518 throws bsh.EvalError
519 {
520 List history = GrouperShell.getHistory(i);
521 history.add(cnt, cmd);
522 GrouperShell.set(i, GSH_HISTORY, history);
523 }
524
525
526 public static void setOurCommand(Interpreter i, boolean b) {
527 try {
528 GrouperShell.set(i, GSH_OURS, Boolean.valueOf(b));
529 }
530 catch (bsh.EvalError eBEE) {
531 i.error(eBEE.getMessage());
532 }
533 }
534
535
536
537
538
539 protected void run()
540 throws GrouperShellException
541 {
542 String cmd = new String();
543 try {
544 this.interpreter.eval( "importCommands(\"edu.internet2.middleware.grouper\")");
545 this.interpreter.eval( "importCommands(\"edu.internet2.middleware.grouper.app.gsh\")");
546 this.interpreter.eval( "importCommands(\"edu.internet2.middleware.grouper.app.misc\")");
547 this.interpreter.eval( "importCommands(\"edu.internet2.middleware.grouper.privs\")");
548
549 this.interpreter.eval( "importCommands(\"edu.internet2.middleware.subject\")");
550 this.interpreter.eval( "importCommands(\"edu.internet2.middleware.subject.provider\")");
551 this.interpreter.eval( "import edu.internet2.middleware.grouper.*;");
552 this.interpreter.eval( "import edu.internet2.middleware.grouper.authentication.*;");
553 this.interpreter.eval( "import edu.internet2.middleware.grouper.app.loader.ldap.*;");
554 this.interpreter.eval( "import edu.internet2.middleware.grouper.attr.*;");
555 this.interpreter.eval( "import edu.internet2.middleware.grouper.attr.assign.*;");
556 this.interpreter.eval( "import edu.internet2.middleware.grouper.attr.finder.*;");
557 this.interpreter.eval( "import edu.internet2.middleware.grouper.attr.value.*;");
558 this.interpreter.eval( "import edu.internet2.middleware.grouper.audit.*;");
559 this.interpreter.eval( "import edu.internet2.middleware.grouper.client.*;");
560 this.interpreter.eval( "import edu.internet2.middleware.grouper.entity.*;");
561 this.interpreter.eval( "import edu.internet2.middleware.grouper.externalSubjects.*;");
562 this.interpreter.eval( "import edu.internet2.middleware.grouper.group.*;");
563 this.interpreter.eval( "import edu.internet2.middleware.grouper.ldap.*;");
564 this.interpreter.eval( "import edu.internet2.middleware.grouper.app.loader.*;");
565 this.interpreter.eval( "import edu.internet2.middleware.grouper.xml.*;");
566 this.interpreter.eval( "import edu.internet2.middleware.grouper.registry.*;");
567 this.interpreter.eval( "import edu.internet2.middleware.grouper.app.usdu.*;");
568 this.interpreter.eval( "import edu.internet2.middleware.grouper.app.provisioning.*;");
569 this.interpreter.eval( "import edu.internet2.middleware.grouper.app.gsh.*;");
570 this.interpreter.eval( "import edu.internet2.middleware.grouper.app.misc.*;");
571 this.interpreter.eval( "import edu.internet2.middleware.grouper.privs.*;");
572 this.interpreter.eval( "import edu.internet2.middleware.grouper.rules.*;");
573 this.interpreter.eval( "import edu.internet2.middleware.grouper.misc.*;");
574 this.interpreter.eval( "import edu.internet2.middleware.grouper.hibernate.*;");
575 this.interpreter.eval( "import edu.internet2.middleware.grouper.permissions.*;");
576 this.interpreter.eval( "import edu.internet2.middleware.grouper.util.*;");
577 this.interpreter.eval( "import edu.internet2.middleware.grouper.xml.export.*;");
578 this.interpreter.eval( "import edu.internet2.middleware.subject.*;");
579 this.interpreter.eval( "import edu.internet2.middleware.subject.provider.*;");
580 this.interpreter.eval( "import edu.internet2.middleware.grouper.userData.*;");
581 this.interpreter.eval( "import edu.internet2.middleware.grouper.messaging.*;");
582
583 }
584 catch (bsh.EvalError eBBB) {
585 throw new GrouperShellException(GshErrorMessages.I_IMPORT + eBBB.getMessage(), eBBB);
586 }
587 while ( ( cmd = r.getNext() ) != null) {
588 if ( this._isComment(cmd) ) {
589 continue;
590 }
591 if ( this._isTimeToExit(cmd) ) {
592 int txSize = HibernateSession._internal_staticSessions().size();
593 boolean hasTransactions = txSize>0;
594 if (hasTransactions) {
595 String error = "Exiting in the middle of " + txSize + " open transactions, they will be rolled back and closed";
596 this.interpreter.println(error);
597 LOG.error(error);
598 HibernateSession._internal_closeAllHibernateSessions(new RuntimeException());
599 }
600 this._stopSession();
601 if (hasTransactions) {
602 ShellHelper.exitDueToOpenTransaction(this.interpreter);
603 }
604 break;
605 }
606
607 cmd = ShellHelper.eval(interpreter, cmd);
608 }
609 }
610
611
612 private static final Log LOG = GrouperUtil.getLog(GrouperShell.class);
613
614
615
616
617
618
619
620 private static boolean _isTrue(Interpreter i, String var) {
621 boolean rv = false;
622 try {
623 Object obj = GrouperShell.get(i, var);
624 if (
625 (obj != null)
626 && (obj instanceof Boolean)
627 && (Boolean.TRUE.equals( obj ))
628 )
629 {
630 rv = true;
631 }
632 }
633 catch (bsh.EvalError eBEE) {
634 i.error(eBEE.getMessage());
635 }
636 return rv;
637 }
638
639
640
641
642
643
644 private boolean _isComment(String cmd) {
645 if ( cmd.startsWith("#") || cmd.startsWith("//") ) {
646 return true;
647 }
648 return false;
649 }
650
651
652
653 private boolean _isTimeToExit(String cmd) {
654 if ( cmd.equals("exit") || cmd.equals("quit") ) {
655 return true;
656 }
657 return false;
658 }
659
660
661 private void _stopSession()
662 throws GrouperShellException
663 {
664 try {
665
666
667 GrouperSession../../../../../edu/internet2/middleware/grouper/GrouperSession.html#GrouperSession">GrouperSession s = (GrouperSession) GrouperShell.get(this.interpreter, GSH_SESSION);
668 if (s != null) {
669 s.stop();
670 this.interpreter.unset(GSH_SESSION);
671 }
672 }
673 catch (Exception e) {
674 if (interpreter != null) {
675 this.interpreter.error(e.getMessage());
676 }
677 throw new GrouperShellException(e);
678 }
679 }
680
681 private static String _getUsage() {
682 return "Usage:" + GrouperConfig.NL
683 + "args: -h, Prints this message" + GrouperConfig.NL
684 + "args: <filename>, Execute commands in specified file" + GrouperConfig.NL
685 + "no args: Enters an interactive shell" + GrouperConfig.NL
686 + "args: -lightWeightProfile" + GrouperConfig.NL
687 + " Use alternate init script (classes/groovysh_lightWeight.profile)" + GrouperConfig.NL
688 + " which has less imports and may improve startup performance" + GrouperConfig.NL
689 + "args: -nocheck, Skips startup check and enters an " + GrouperConfig.NL
690 + " interactive shell" + GrouperConfig.NL
691 + "args: -runarg <command> Run command (use \\\\n to separate commands)" + GrouperConfig.NL
692 + "args: -main <class> [args...] " + GrouperConfig.NL
693 + " class, Full class name (must have main method)" + GrouperConfig.NL
694 + " args, args as required by main method of class" + GrouperConfig.NL
695 + "args: -initEnv [<configDir>]" + GrouperConfig.NL
696 + " On Windows sets GROUPER_HOME and adds GROUPER_HOME/bin to path" + GrouperConfig.NL
697 + " For *nix 'source gsh.sh' for the same result" + GrouperConfig.NL
698 + " configDir optionally adds an alternative conf directory than" + GrouperConfig.NL
699 + " GROUPER_HOME/conf to the classpath" + GrouperConfig.NL
700 + "args: (-xmlimport | -xmlexport | -loader | -test | -registry | -usdu |" + GrouperConfig.NL
701 + " -findbadmemberships | -ldappc | pspngAttributesToProvisioningAttributes) "
702 + " Enter option to get additional usage for that " + GrouperConfig.NL
703 + " option " + GrouperConfig.NL
704
705 + " -xmlimport, Invokes XmlImporter*" + GrouperConfig.NL
706 + " *XML format has changed in v1.6. To import" + GrouperConfig.NL
707 + " the original XML format use -xmlimportold" + GrouperConfig.NL
708 + " -xmlexport, Invokes XmlExporter" + GrouperConfig.NL
709 + " -loader, Invokes GrouperLoader" + GrouperConfig.NL
710 + " -registry, Manipulate the Grouper schema and install" + GrouperConfig.NL
711 + " bootstrap data" + GrouperConfig.NL
712 + " -test, Run JUnit tests" + GrouperConfig.NL
713
714 + " -usdu, Invoke USDU - Unresolvable Subject Deletion " + GrouperConfig.NL
715 + " Utility" + GrouperConfig.NL
716 + " -pspngAttributesToProvisioningAttributes Copies pspng attributes to provisioning" + GrouperConfig.NL
717
718 + " -findbadmemberships, Check for membership data inconsistencies " + GrouperConfig.NL
719 + " -ldappc, Run the grouper ldap provisioning connector to send data to ldap " + GrouperConfig.NL
720 ;
721 }
722
723 }
724