@Override
    protected void beforeUpdate(ModeledAuthenticatedUser user,
            ModeledConnectionGroup object, ConnectionGroupModel model)
            throws GuacamoleException {

        super.beforeUpdate(user, object, model);
        
        // Name must not be blank
        if (model.getName() == null || model.getName().trim().isEmpty())
            throw new GuacamoleClientException("Connection group names must not be blank.");
        
        // Check whether such a connection group is already present
        ConnectionGroupModel existing = connectionGroupMapper.selectOneByName(model.getParentIdentifier(), model.getName());
        if (existing != null) {

            // If the specified name matches a DIFFERENT existing connection group, the update cannot continue
            if (!existing.getObjectID().equals(model.getObjectID()))
                throw new GuacamoleClientException("The connection group \"" + model.getName() + "\" already exists.");

        }

        // Verify that this connection group's location does not create a cycle
        String relativeParentIdentifier = model.getParentIdentifier();
        while (relativeParentIdentifier != null) {

            // Abort if cycle is detected
            if (relativeParentIdentifier.equals(model.getIdentifier()))
                throw new GuacamoleUnsupportedException("A connection group may not contain itself.");

            // Advance to next parent
            ModeledConnectionGroup relativeParentGroup = retrieveObject(user, relativeParentIdentifier);
            relativeParentIdentifier = relativeParentGroup.getModel().getParentIdentifier();

        } 

    }
Service for creating and tracking tunnels.
extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connectiongroup/ConnectionGroupService.java