Versions Compared


  • This line was added.
  • This line was removed.
  • Formatting was changed.


  • Setting gsh.useLegacy = true in
  • Using a command line argument  ( -forceLegacyGsh)


Escape things in groovysh with single backslash.  e.g.

Code Block
attributeValueDelegate.assignValue(RuleUtils.ruleIfConditionElName(), "\${subject.sourceId != 'g:gsa'}");

API Compability

gsh is now a core part of the Grouper API and so is always compatible with the current release.




addRootStem(extension, displayExtension)

Add top-level stem to the registry

addStem(parent stem name, extension, displayExtension)

Add stem to registry

delStem(stem name)

Delete stem from registry

obliterateStem(stem name, testOnlyBoolean, deleteFromPointInTimeBoolean)    (Grouper v2.0.2+)

Delete stem, and subobjects.

If testonly (true|false), then only
print a report.  This is not supported when deleteFromPointInTime is true.

If deleteFromPointInTime (true|false), then delete from point in time as well.  Otherwise, point in time records are not deleted.

Note that point in time data can only be deleted after the actual objects have been deleted and those deletions have been processed by the changeLogTempToChangeLog job, which runs once a minute by default with the Grouper Daemon.  So when you call obliterateStem(name, false, true), it will first obliterate the actual stem, then sleep and keep checking if the changeLogTempToChangeLog job has completed.  When it completes, it will obliterate from the point in time data.

GrouperSession must be open before calling...

getStemAttr(stem name, attr)

Get value of stem attribute


Find all stems with a matching naming attribute value, returns a Set of stems

setStemAttr(stem name, attr, value)

Set value of stem attribute

StemFinder.findByName(grouperSession, name)

Find one stem by name

StemFinder.findByUuid(grouperSession, uuid)

Find one stem by uuid

Delete stem and subcontents

Code Block
grouperSession = GrouperSession.startRootSession();
stem = StemFinder.findByName(grouperSession, "a");
for(child : stem.getChildGroups(Stem.Scope.SUB)) { System.out.println("deleting: " + child.getName()); child.delete();  }
stemList = new ArrayList(stem.getChildStems(Stem.Scope.SUB));
for(childStem : stemList) { System.out.println("deleting: " + childStem.getName()); childStem.delete(); }

Delete stem and subcontents




addSubject(id, type, name)

Add local subject to registry. You need the jdbc source for this to work. The type parameter describes the type of subject (e.g. "people"), and is required non-null even though there are few useful api methods to query it.

In 2.4.0.api.41+ patch, this will also create the id, name, description, and loginid attribute (unless create.attributes.when.creating.registry.subjects is false)

RegistrySubject.addOrUpdate(grouperSession, id, type, name, nameAttributeValue, loginid, description, email)In 2.4.0.api.41+ patch, add a registry subject like addSubject, but specify the attribute values of name, loginid, etc
e.g. RegistrySubject.addOrUpdate(grouperSession, "someTestSubject", "person", "Some Testsubject", "Name Some Test Subject", "stsub", "Some Testsubject - employee - also alumni", "some@test.subject");
RegistrySubject.find(id, errorOnNotFound)In 2.4.0.api.41+ patch, get a registry subject
e.g. registrySubject = RegistrySubject.find("someTestSubject", false);
registrySubject.delete(grouperSession)In 2.4.0.api.41+ patch, delete a registry subject
e.g. registrySubject.delete(grouperSession);
RegistrySubjectAttribute.addOrUpdate(subjectId, attributeName, attributeValue)In 2.4.0.api.41+ patch, add or update a registry subject attribute
registrySubjectAttribute.delete()In 2.4.0.api.41+ patch, delete an attribute value


Find a subject by id or identifier

findSubject(idOrIdentifier, type)

Find a subject by id or identifier; type is a deprecated parameter that is ignored

findSubject(idOrIdentifier, type, source)

Find a subject by id or identifier for a specific subject source; type is a deprecated parameter that is ignored


Find all Subject sources

grouperSession = GrouperSession.startRootSession();
SubjectFinder.findAll(searchString, source);

Find all subjects in a source by search string

grouperSession = GrouperSession.startRootSession();
SubjectFinder.findByIdAndSource(id, source, exceptionIfNull);
SubjectFinder.findByIdAndSource("12345", "jdbc", true);

Find a subject by id in a certain source

grouperSession = GrouperSession.startRootSession();
SubjectFinder.findByIdentifierAndSource(identifier, source, exceptionIfNull);
SubjectFinder.findByIdentifierAndSource("jsmith", "jdbc", true);

Find a subject by identifier in a certain source

grouperSession = GrouperSession.startRootSession();
SubjectFinder.findByIdOrIdentifierAndSource(idOrIdentifier, source, exceptionIfNull);
SubjectFinder.findByIdOrIdentifierAndSource("jsmith", "jdbc", true);

Find a subject by id or identifier in a certain source

add test subjects to registry (e.g. test.subject.0 through 9)

grouperSession = GrouperSession.startRootSession();
new RegistryReset()._addSubjects();

Edit subject (in this case name)

RegistrySubject registrySubject = GrouperDAOFactory.getFactory().getRegistrySubject().find("user1a", "person", true);
registrySubject.setName("New name");

add a subject application principal with attributes (GSH)

Code Block
String principal = "someApp";
String email = null;

GrouperSession grouperSession = GrouperSession.startRootSession();

addSubject(principal, "application", principal);
HibernateSession.bySqlStatic().executeSql("insert into subjectattribute (subjectId, name, value, searchValue) values (?, ?, ?, ?)", GrouperUtil.toListObject(new Object[]{principal, "description", principal, principal.toLowerCase()}));
if (email != null){ HibernateSession.bySqlStatic().executeSql("insert into subjectattribute (subjectId, name, value, searchValue) values (?, ?, ?, ?)", GrouperUtil.toListObject(new Object[]{principal, "email", email, email.toLowerCase()}));}
HibernateSession.bySqlStatic().executeSql("insert into subjectattribute (subjectId, name, value, searchValue) values (?, ?, ?, ?)", GrouperUtil.toListObject(new Object[]{principal, "loginid", principal, principal}));
HibernateSession.bySqlStatic().executeSql("insert into subjectattribute (subjectId, name, value, searchValue) values (?, ?, ?, ?)", GrouperUtil.toListObject(new Object[]{principal, "name", principal, principal}));

remove a subject with attributes (GSH)

Code Block
String principal = "someApp";
String email = null;

GrouperSession grouperSession = GrouperSession.startRootSession();

HibernateSession.bySqlStatic().executeSql("delete from subjectattribute where subjectId = ?", GrouperUtil.toListObject(new Object[]{principal}));
HibernateSession.bySqlStatic().executeSql("delete from subject where subjectId = ?", GrouperUtil.toListObject(new Object[]{principal}));

subject api diagnostics (temporarily)

Code Block
gsh.exitOnSubjectCheckConfigProblem = false


Code Block
new edu.internet2.middleware.grouper.grouperUi.serviceLogic.SubjectSourceDiagnostics().assignSourceId("SMUPerson_DEV").assignSubjectId("empl1").assignSubjectIdentifier("").assignSearchString("em").subjectSourceDiagnosticsFromGsh()
SUCCESS: Found subject by id in 37ms: 'empl1'
         with SubjectFinder.findByIdAndSource("empl1", "SMUPerson_DEV", false)
SUCCESS: Subject id in returned subject matches the subject id searched for: 'empl1'
WARNING: No subject found by identifier in 14ms: ''
         with SubjectFinder.findByIdentifierAndSource("", "SMUPerson_DEV", false)





Execute each line of a sql file, just like ant would.  This can run the files generated by registryInitializeSchema()


Executes a single sql statement


Terminate shell


Display usage information


Pretty print results.


Terminate shell


Return version information





print help information


print the list of nested transactions


start a transaction, or make sure one is already started


commit a transaction


rollback a transaction


end a transaction
    Note if it was read/write, and not committed or rolled back, this will commit and end


You can schedule daemon jobs (and in UI) in v2.5.23+

Code Block


Above, it describes how you can kick off the loader in daemon mode.  You can also execute one job with:


You can run the loader as a linux service

Jobs not firing in daemon

  1. Stop all daemons
  2. Run these sqls from a file: fixLoaderScheduler.sql

    Code Block

    Code Block -registry -runsqlfile fixLoaderScheduler.sql

  3. Restart daemons

It took a couple of hours to catch up on a few days of changes, but it seems to be back to normal. Thanks again, guys!

GrouperShell Variables (BeanShell only)


Code Block
# (1) Print tab-separated summary of all group members, and flags for direct, indirect, or both
# Depending on the results, you could use the data to create a scrutinized list of Ids to delete, then import it and delete in a loop

me = SubjectFinder.findByIdentifierAndSource("my-username", "pid", true);
session = GrouperSession.start(me);
// OR: session = GrouperSession.startRootSession(True)

group = GroupFinder.findByName(session, "tmp:my:group", true);

effectiveMembers = group.getEffectiveMembers();
immediateMembers = group.getImmediateMembers();

System.out.println(String.join("\t", "id", "name", "Effective", "Immediate"));

for (Member m: group.getMembers()) {
    System.out.print(m.getSubject().getId() + "\t" + m.getSubject().getName() + "\t");
    System.out.print(effectiveMembers.contains(m).toString() + "\t");
    System.out.println(immediateMembers.contains(m).toString() + "\t");

# (2) Get the immediate and effective members for a specific source ("pid" in this example), intersect them to find the redundant ones
# This has a dryRun flag, so you can test first

sources = new HashSet<Source>()

effectiveUsers = group.getEffectiveMembers(Group.getDefaultList(), sources, null)
immediateUsers = group.getImmediateMembers(Group.getDefaultList(), sources, null)

# use retainAll() to find the intersection; i.e., users both as effective and immediate member

System.out.println("There are " + immediateUsers.size() + " users having both direct + indirect memberships");

dryRun = true

for (Member m: immediateUsers) {
    if (dryRun) {
        System.out.println("Ok to delete " + m.getSubject().getId());
    } else {
        System.out.println("Deleting " + m.getSubject().getId());
        group.deleteMember(m, false);

# (3) Get the groups this subject is a member of. Note that a group is a kind of subject, and has a toSubject() method to convert it.

import edu.internet2.middleware.grouper.membership.MembershipSubjectContainer

GrouperSession grouperSession = GrouperSession.startRootSession();

Group group = GroupFinder.findByName(grouperSession, "test:testGroup", true);
Subject subject = g.toSubject();

Set<MembershipSubjectContainer> msc = new MembershipFinder().addSubject(subject).findMembershipResult().getMembershipSubjectContainers();

for (MembershipSubjectContainer membershipSubjectContainer : msc) { println(membershipSubjectContainer.getGroupOwner().getName());}

//Note there are a few other options for the search. Add these to the MembershipFinder method chain before calling findMembershipResult():
//  - search immediate, effective, etc. (needs to import MembershipType)
import edu.internet2.middleware.grouper.membership.MembershipType
membershipFinder.assignMembershipType(MembershipType.IMMEDIATE) // options are IMMEDIATE|NONIMMEDIATE|EFFECTIVE|COMPOSITE
//  - retrieve specific groups based on pattern
//  - Enabled status -- true means enabled only, false, means disabled only, and null means all
// For other methods, refer to the Javadoc at httphttps://software.internet2.github.ioedu/grouper/doc/master/grouper-parent/apidocs/index.html?edu/internet2/middleware/grouper/MembershipFinder.html


Code Block
gcDbAccess = new edu.internet2.middleware.grouperClient.jdbc.GcDbAccess();
// "loaderConnection" is the string used in the ( Example: db.warehouse.url --> "warehouse")
gcDbAccess.connectionName("loaderConnection").sql("select count(1) from test1").select(int.class);

Code Block
List results = new edu.internet2.middleware.grouperClient.jdbc.GcDbAccess().sql("select name, id from grouper_groups").selectList(Object[].class);
for (Object[] row : results) { System.out.println(row[0] + ", " + row[1]);}

See the WIKI for running the Grouper Report manually


Code Block
gcDbAccess = new edu.internet2.middleware.grouperClient.jdbc.GcDbAccess();
gcDbAccess.connectionName("warehouse").sql("select count(1) from grouper_groups").select(int.class);

Set password using Grouper built-in authentication

Code Block
new GrouperPasswordSave().assignApplication(GrouperPassword.Application.UI).assignUsername("username").assignPassword("password").save();