View Javadoc
1   package edu.internet2.middleware.grouper.app.remedyV2.digitalMarketplace;
2   
3   import java.util.ArrayList;
4   import java.util.HashMap;
5   import java.util.LinkedHashMap;
6   import java.util.List;
7   import java.util.Map;
8   import java.util.Set;
9   import java.util.TreeMap;
10  
11  import org.apache.commons.lang.StringUtils;
12  
13  import com.fasterxml.jackson.databind.JsonNode;
14  import com.fasterxml.jackson.databind.ObjectMapper;
15  import com.fasterxml.jackson.databind.node.ArrayNode;
16  import com.fasterxml.jackson.databind.node.ObjectNode;
17  
18  import edu.internet2.middleware.grouper.cfg.GrouperConfig;
19  import edu.internet2.middleware.grouper.util.GrouperHttpClient;
20  import edu.internet2.middleware.grouper.util.GrouperHttpMethod;
21  import edu.internet2.middleware.grouper.util.GrouperUtil;
22  import edu.internet2.middleware.grouperClient.util.ExpirableCache;
23  import edu.internet2.middleware.grouperClient.util.GrouperClientConfig;
24  import edu.internet2.middleware.grouperClient.util.GrouperClientUtils;
25  import org.apache.commons.lang3.exception.ExceptionUtils;
26  
27  public class GrouperDigitalMarketplaceApiCommands {
28    
29    /**
30     * @param digitalMarketplaceExternalSystemConfigId
31     * @return remedy login id to user never null
32     */
33    public static Map<String, GrouperDigitalMarketplaceUser> retrieveDigitalMarketplaceUsers(String digitalMarketplaceExternalSystemConfigId) {
34      
35      Map<String, Object> debugMap = new LinkedHashMap<String, Object>();
36  
37      debugMap.put("method", "retrieveDigitalMarketplaceUsers");
38  
39      long startTime = System.nanoTime();
40  
41      Map<String, GrouperDigitalMarketplaceUser> results = new LinkedHashMap<String, GrouperDigitalMarketplaceUser>();
42      
43      try {
44  
45        int pageSize = 2000;
46        
47        for (int i=0;i<6000;i++) {
48        
49          Map<String, GrouperDigitalMarketplaceUser> localResults = retrieveDigitalMarketplaceUsersHelper(digitalMarketplaceExternalSystemConfigId, pageSize, 0+results.size());
50          
51          if (localResults.size() == 0) {
52            break;
53          }
54          
55          results.putAll(localResults);
56          
57        }
58  
59        // jsonObject.getString("totalSize")
60        debugMap.put("size", GrouperClientUtils.length(results));
61  
62        return results;
63      } catch (RuntimeException re) {
64        debugMap.put("exception", GrouperClientUtils.getFullStackTrace(re));
65        throw re;
66      } finally {
67        GrouperDigitalMarketplaceLog.marketplaceLog(debugMap, startTime);
68      }
69      
70      
71    }
72  
73    /**
74     * @param digitalMarketplaceExternalSystemConfigId
75     * @param pageSize 
76     * @param startIndex 
77     * @return remedy login id to user never null
78     */
79    private static Map<String, GrouperDigitalMarketplaceUser> retrieveDigitalMarketplaceUsersHelper(
80        String digitalMarketplaceExternalSystemConfigId,
81        int pageSize, int startIndex) {
82  
83      Map<String, Object> debugMap = new LinkedHashMap<String, Object>();
84      
85      debugMap.put("method", "retrieveDigitalMarketplaceUsersHelper");
86  
87      long startTime = System.nanoTime();
88  
89      try {
90      
91        Map<String, String> paramMap = new HashMap<String, String>();
92    
93        paramMap.put("dataPageType", "com.bmc.arsys.rx.application.user.datapage.UserDataPageQuery");
94        paramMap.put("pageSize", "" + pageSize);
95        paramMap.put("startIndex", "" + startIndex);
96        
97        JsonNode jsonObject = executeGetMethod(digitalMarketplaceExternalSystemConfigId, debugMap, "/api/rx/application/datapage", paramMap);
98        
99        Map<String, GrouperDigitalMarketplaceUser> results = convertMarketplaceUsersFromJson(jsonObject);
100       debugMap.put("totalSize", GrouperUtil.jsonJacksonGetInteger(jsonObject, "totalSize")); 
101       
102       List<String> ids = new ArrayList<String>(results.keySet());
103 
104       if (ids.size() > 0) {
105         debugMap.put("first", ids.get(0));
106         debugMap.put("last", ids.get(ids.size()-1));
107       }      
108   
109       return results;
110     } catch (RuntimeException re) {
111       debugMap.put("exception", GrouperClientUtils.getFullStackTrace(re));
112       throw re;
113     } finally {
114       GrouperDigitalMarketplaceLog.marketplaceLog(debugMap, startTime);
115     }
116 
117   }
118 
119   /**
120    * @param digitalMarketplaceExternalSystemConfigId
121    * @param loginid
122    * @return the user based on loginid
123    */
124   public static GrouperDigitalMarketplaceUser retrieveDigitalMarketplaceUser(String digitalMarketplaceExternalSystemConfigId, String loginid) {
125     
126     Map<String, Object> debugMap = new LinkedHashMap<String, Object>();
127 
128     debugMap.put("method", "retrieveDigitalMarketplaceUser");
129     debugMap.put("loginid", loginid);
130 
131     long startTime = System.nanoTime();
132 
133     try {
134   
135       Map<String, String> paramMap = new HashMap<String, String>();
136 
137       //doesnt work since the url shouldnt be encoded
138       paramMap.put("dataPageType", "com.bmc.arsys.rx.application.user.datapage.UserDataPageQuery");
139       paramMap.put("pageSize", "1");
140       paramMap.put("startIndex", "0");
141       paramMap.put("loginName", loginid);
142 
143       JsonNode jsonObject = executeGetMethod(digitalMarketplaceExternalSystemConfigId, debugMap, "/api/rx/application/datapage", paramMap);
144 
145       Map<String, GrouperDigitalMarketplaceUser> results = convertMarketplaceUsersFromJson(jsonObject);
146 
147       debugMap.put("size", GrouperClientUtils.length(results));
148 
149       if (GrouperClientUtils.length(results) == 0) {
150         
151         return null;
152         
153       }
154       if (GrouperClientUtils.length(results) == 1) {
155         return results.values().iterator().next();
156       }
157       throw new RuntimeException("Found multiple results for loginid '" + loginid + "', results: " + GrouperClientUtils.length(results));
158       
159     } catch (RuntimeException re) {
160       debugMap.put("exception", GrouperClientUtils.getFullStackTrace(re));
161       throw re;
162     } finally {
163       GrouperDigitalMarketplaceLog.marketplaceLog(debugMap, startTime);
164     }
165     
166   }
167   
168   /**
169    * @param digitalMarketplaceExternalSystemConfigId
170    * @param groupName
171    * @return the group based on group name
172    */
173   public static GrouperDigitalMarketplaceGroup retrieveDigitalMarketplaceGroup(String digitalMarketplaceExternalSystemConfigId, String groupName) {
174     
175     Map<String, GrouperDigitalMarketplaceGroup> results = new TreeMap<String, GrouperDigitalMarketplaceGroup>();
176     
177     Map<String, Object> debugMap = new LinkedHashMap<String, Object>();
178 
179     debugMap.put("method", "retrieveDigitalMarketplaceGroup");
180     debugMap.put("groupName", groupName);
181 
182     long startTime = System.nanoTime();
183 
184     try {
185   
186       Map<String, String> paramMap = new HashMap<String, String>();
187 
188       //doesnt work since the url shouldnt be encoded
189       paramMap.put("dataPageType", "com.bmc.arsys.rx.application.group.datapage.GroupDataPageQuery");
190       paramMap.put("pageSize", "1");
191       paramMap.put("startIndex", "0");
192       paramMap.put("groupName", groupName);
193 
194       JsonNode jsonObject = executeGetMethod(digitalMarketplaceExternalSystemConfigId, debugMap, "/api/rx/application/datapage", paramMap);
195 
196       JsonNode jsonObjectEntries = jsonObject.get("data");
197       for (int i=0; i < jsonObjectEntries.size(); i++) {
198         
199         JsonNode jsonObjectGroup = jsonObjectEntries.get(i);
200         
201         GrouperDigitalMarketplaceGroupouperDigitalMarketplaceGroup.html#GrouperDigitalMarketplaceGroup">GrouperDigitalMarketplaceGroup grouperDigitalMarketplaceGroup = new GrouperDigitalMarketplaceGroup();
202   
203         {
204           String groupName1 = GrouperUtil.jsonJacksonGetString(jsonObjectGroup, "groupName");
205           grouperDigitalMarketplaceGroup.setGroupName(groupName1);
206         }
207         
208         {
209           String comments = GrouperUtil.jsonJacksonGetString(jsonObjectGroup, "comments");
210           grouperDigitalMarketplaceGroup.setComments(comments);
211         }
212         
213         {
214           String groupType = GrouperUtil.jsonJacksonGetString(jsonObjectGroup, "groupType");
215           grouperDigitalMarketplaceGroup.setGroupType(groupType);
216         }
217 
218         {
219           String longGroupName = GrouperUtil.jsonJacksonGetString(jsonObjectGroup, "longGroupName");
220           grouperDigitalMarketplaceGroup.setLongGroupName(longGroupName);
221         }
222 
223         {
224           // note: this might be blank: com.bmc.arsys.rx.services.group.domain.RegularGroup
225           if (jsonObjectGroup.has("resourceType")) {
226             String resourceType = GrouperUtil.jsonJacksonGetString(jsonObjectGroup, "resourceType");
227             grouperDigitalMarketplaceGroup.setResourceType(resourceType);
228           }
229         }
230 
231         results.put(grouperDigitalMarketplaceGroup.getGroupName(), grouperDigitalMarketplaceGroup);
232       }
233       
234       debugMap.put("size", GrouperClientUtils.length(results));
235 
236       if (GrouperClientUtils.length(results) == 0) {
237         
238         return null;
239         
240       }
241       if (GrouperClientUtils.length(results) == 1) {
242         return results.values().iterator().next();
243       }
244       throw new RuntimeException("Found multiple results for groupName '" + groupName + "', results: " + GrouperClientUtils.length(results));
245       
246     } catch (RuntimeException re) {
247       debugMap.put("exception", GrouperClientUtils.getFullStackTrace(re));
248       throw re;
249     } finally {
250       GrouperDigitalMarketplaceLog.marketplaceLog(debugMap, startTime);
251     }
252     
253   }
254 
255   /**
256    * execute a GET method
257    * @param digitalMarketplaceExternalSystemConfigId
258    * @param debugMap
259    * @param path
260    * @param paramMap
261    * @return the json object
262    */
263   private static JsonNode executeGetMethod(String digitalMarketplaceExternalSystemConfigId, Map<String, Object> debugMap, String path, Map<String, String> paramMap) {
264   
265     GrouperHttpClientuperHttpClient.html#GrouperHttpClient">GrouperHttpClient grouperHttpClient = new GrouperHttpClient();
266     
267     String jwtToken = retrieveJwtToken(digitalMarketplaceExternalSystemConfigId, debugMap);
268   
269     String fullUrl = calculateUrl(digitalMarketplaceExternalSystemConfigId, path, paramMap);
270     grouperHttpClient.assignUrl(fullUrl);
271     grouperHttpClient.assignGrouperHttpMethod(GrouperHttpMethod.get);
272     
273     grouperHttpClient.addHeader("authorization", "AR-JWT " + jwtToken);
274     
275     int responseCodeInt = -1;
276     String body = null;
277     long startTime = System.nanoTime();
278     try {
279       grouperHttpClient.executeRequest();
280       responseCodeInt = grouperHttpClient.getResponseCode();
281       
282       try {
283         body = grouperHttpClient.getResponseBody();
284       } catch (Exception e) {
285         debugMap.put("getResponseBody", ExceptionUtils.getStackTrace(e));
286       }
287       
288     } catch (Exception e) {
289       throw new RuntimeException("error in authn", e);
290     } finally {
291       debugMap.put("getMillis", ((System.nanoTime() - startTime) / 1000000) + "ms");
292     }
293     
294     if (responseCodeInt != 200) {
295       throw new RuntimeException("get didnt return 200, it returned: " + responseCodeInt + ", " + body);
296     }
297   
298     // hmmm, no body
299     if (GrouperClientUtils.isBlank(body)) {
300       return null;
301     }
302     
303     JsonNode rootNode = GrouperUtil.jsonJacksonNode(body);
304 
305     return rootNode;
306   }
307 
308   /**
309    * cache tokens
310    */
311   private static ExpirableCache<Boolean, String> retrieveJwtTokenCache = new ExpirableCache<Boolean, String>(5);
312 
313   /**
314    * millis since 1970 that the user was retrieved last for membership update
315    */
316   private static ExpirableCache<String, Long> lastRetrieveUserByLoginNameForMshipUpdate = new ExpirableCache<String, Long>(5);
317 
318   /**
319    * get the login token
320    * @param digitalMarketplaceExternalSystemConfigId
321    * @param debugMap
322    * @return the login token
323    */
324   private static String retrieveJwtToken(String digitalMarketplaceExternalSystemConfigId, Map<String, Object> debugMap) {
325     
326     String jwtToken = retrieveJwtTokenCache.get(Boolean.TRUE);
327 
328     if (GrouperClientUtils.isBlank(jwtToken)) {
329       
330       synchronized (retrieveJwtTokenCache) {
331         jwtToken = retrieveJwtTokenCache.get(Boolean.TRUE);
332         if (GrouperClientUtils.isBlank(jwtToken)) {
333           
334           String username = GrouperConfig.retrieveConfig().propertyValueStringRequired("grouper.remedyDigitalMarketplaceConnector."+digitalMarketplaceExternalSystemConfigId+".username");
335           
336           String password = GrouperConfig.retrieveConfig().propertyValueStringRequired("grouper.remedyDigitalMarketplaceConnector."+digitalMarketplaceExternalSystemConfigId+".password");
337       
338           //login and get a token
339           String loginUrl = GrouperConfig.retrieveConfig().propertyValueStringRequired("grouper.remedyDigitalMarketplaceConnector."+digitalMarketplaceExternalSystemConfigId+".tokenUrl");
340         
341           //login and get a token
342           String url = GrouperConfig.retrieveConfig().propertyValueStringRequired("grouper.remedyDigitalMarketplaceConnector."+digitalMarketplaceExternalSystemConfigId+".url");
343         
344           url = GrouperClientUtils.stripEnd(url, "/");
345           
346 //          String loginUrl = url + "/api/myit-sb/users/login";
347           
348           
349           GrouperHttpClientuperHttpClient.html#GrouperHttpClient">GrouperHttpClient grouperHttpClient = new GrouperHttpClient();
350           
351           //URL e.g. http://localhost:8093/grouper-ws/servicesRest/v1_3_000/...
352           //NOTE: aStem:aGroup urlencoded substitutes %3A for a colon
353           grouperHttpClient.assignUrl(loginUrl);
354           grouperHttpClient.assignGrouperHttpMethod(GrouperHttpMethod.post);
355         
356           //no keep alive so response is easier to indent for tests
357           grouperHttpClient.addHeader("Connection", "close");
358           grouperHttpClient.addHeader("Content-Type", "application/json");
359           grouperHttpClient.addHeader("X-Requested-By", username);
360           
361           ObjectNode jsonObject = GrouperUtil.jsonJacksonNode();
362           
363           jsonObject.put("id", username);
364           jsonObject.put("password", password);
365           
366           String postBody = jsonObject.toString();
367 
368           grouperHttpClient.assignBody(postBody);
369 
370           int responseCodeInt = -1;
371 
372           long startTime = System.nanoTime();
373           try {
374             grouperHttpClient.executeRequest();
375             responseCodeInt = grouperHttpClient.getResponseCode();
376             
377             try {
378               jwtToken = grouperHttpClient.getResponseBody();
379             } catch (Exception e) {
380               debugMap.put("authnGetResponseAsStringException", ExceptionUtils.getStackTrace(e));
381             }
382             
383           } catch (Exception e) {
384             throw new RuntimeException("error in authn", e);
385           } finally {
386             debugMap.put("authnMillis", ((System.nanoTime() - startTime) / 1000000) + "ms");
387           }
388           
389           if (responseCodeInt != 200) {
390             debugMap.put("authnResponseCodeInt", responseCodeInt);
391             // note jwt token in this case is not valid and is an error message
392             throw new RuntimeException("authn didnt return 200, it returned: " + responseCodeInt + ", " + jwtToken);
393           }
394           
395           retrieveJwtTokenCache.put(Boolean.TRUE, jwtToken);
396       
397         }
398       }
399     }
400     return jwtToken;
401   }
402 
403   /**
404    * @param digitalMarketplaceExternalSystemConfigId
405    * @param path
406    * @param paramMap
407    * @return the url
408    */
409   private static String calculateUrl(String digitalMarketplaceExternalSystemConfigId,  String path, Map<String, String> paramMap) {
410     
411     String url = GrouperConfig.retrieveConfig().propertyValueStringRequired("grouper.remedyDigitalMarketplaceConnector."+digitalMarketplaceExternalSystemConfigId+".url");
412     
413     url = GrouperClientUtils.stripEnd(url, "/");
414     
415     StringBuilder fullUrlBuilder = new StringBuilder(url).append(path);
416   
417     if (GrouperClientUtils.length(paramMap) > 0) {
418       GrouperClientUtils.length(paramMap);
419       int index = 0;
420       
421       for (String keyname : GrouperClientUtils.nonNull(paramMap).keySet()) {
422         
423         if (index == 0) {
424           fullUrlBuilder.append("?");
425         } else {
426           fullUrlBuilder.append("&");
427         }
428         
429         fullUrlBuilder.append(keyname).append("=").append(paramMap.get(keyname));
430         
431         index++;
432       }
433     }
434     
435     String fullUrl = fullUrlBuilder.toString();
436     return fullUrl;
437   }
438 
439   /**
440    * @param jsonObject
441    * @return the map from netId to user object
442    */
443   private static Map<String, GrouperDigitalMarketplaceUser> convertMarketplaceUsersFromJson(JsonNode jsonObject) {
444     Map<String, GrouperDigitalMarketplaceUser> results = new TreeMap<String, GrouperDigitalMarketplaceUser>();
445   
446     //    { 
447     //      "totalSize":44027,
448     //      "data":[ 
449     //         { 
450     //            "fullName":"Hannah Admin",
451     //            "loginName":"hannah_admin",
452     //            "password":"**************************",
453     //            "userId":"AGGADGJWHI4IZAPC012HPB3UI41XTN",
454     //            "emailAddress":"",
455     //            "groups":[ 
456     //               "Administrator",
457     //               "sbe-catalog-admins",
458     //               "University of Pennsylvania",
459     //               "General Access",
460     //               "sbe-internal-suppliers"
461     //            ],
462     //            "computedGroups":[ 
463     //               "CMDB SC Admin Group",
464     //               "CMDB SC User Group",
465     //               "sbe-public-computed",
466     //               "sbe-internal-suppliers-computed",
467     //               "sbe-catalog-admins-computed",
468     //               "CMDB Data Change Group",
469     //               "CMDB Data View Group",
470     //               "CMDB Console User Group",
471     //               "CMDB Console Admin Group",
472     //               "CMDB RE User Group",
473     //               "CMDB Definitions Viewer Group",
474     //               "CMDB Definitions Admin Group",
475     //               "AI Computed Group"
476     //            ], ...
477     //         }, 
478 
479     JsonNode jsonObjectEntries = jsonObject.get("data");
480     for (int i=0; i < jsonObjectEntries.size(); i++) {
481       
482       JsonNode jsonObjectUser = jsonObjectEntries.get(i);
483       
484       GrouperDigitalMarketplaceUserGrouperDigitalMarketplaceUser.html#GrouperDigitalMarketplaceUser">GrouperDigitalMarketplaceUser grouperDigitalMarketplaceUser = new GrouperDigitalMarketplaceUser();
485 
486       // store this so we can post it back later...
487       grouperDigitalMarketplaceUser.setJsonObject(jsonObjectUser);
488       
489       {
490         String userId = GrouperUtil.jsonJacksonGetString(jsonObjectUser, "userId");
491   
492         // not sure why this would happen
493         if (GrouperClientUtils.isBlank(userId)) {
494           continue;
495         }
496         
497         grouperDigitalMarketplaceUser.setUserId(userId);
498       }
499       
500       {
501         String loginName = GrouperUtil.jsonJacksonGetString(jsonObjectUser, "loginName");
502         // not sure why this would happen
503         if (GrouperClientUtils.isBlank(loginName)) {
504           continue;
505         }
506 
507         grouperDigitalMarketplaceUser.setLoginName(loginName);
508         
509         results.put(loginName, grouperDigitalMarketplaceUser);
510       }
511       
512       {
513         JsonNode groupsArray = jsonObjectUser.get("groups");
514         for (int j=0; j < groupsArray.size(); j++) {
515           JsonNode jsonNode = groupsArray.get(j);
516           String groupExtension = jsonNode.asText();
517           grouperDigitalMarketplaceUser.getGroups().add(groupExtension);
518         }
519       }
520       
521     }
522     return results;
523   }
524 
525   /**
526    * @param digitalMarketplaceExternalSystemConfigId
527    * @return the name of group extension mapped to group
528    */
529   public static Map<String, GrouperDigitalMarketplaceGroup> retrieveDigitalMarketplaceGroups(String digitalMarketplaceExternalSystemConfigId) {
530     
531     Map<String, Object> debugMap = new LinkedHashMap<String, Object>();
532   
533     debugMap.put("method", "retrieveDigitalMarketplaceGroups");
534   
535     long startTime = System.nanoTime();
536   
537     try {
538   
539       Map<String, GrouperDigitalMarketplaceGroup> results = new TreeMap<String, GrouperDigitalMarketplaceGroup>();
540       
541       Map<String, String> paramMap = new HashMap<String, String>();
542       paramMap.put("dataPageType", "com.bmc.arsys.rx.application.group.datapage.GroupDataPageQuery");
543       paramMap.put("pageSize", "-1");
544       paramMap.put("startIndex", "0");
545       
546       //doesnt work since the url shouldnt be encoded
547       
548       JsonNode jsonObject = executeGetMethod(digitalMarketplaceExternalSystemConfigId, debugMap, "/api/rx/application/datapage", paramMap);
549       
550       //  { 
551       //    "totalSize":555,
552       //    "data":[ 
553       //       { 
554       //          "groupName":"Administrator",
555       //          "groupId":1,
556       //          "longGroupName":"superuser to the AR System",
557       //          "groupType":"Change",
558       //          "parentGroup":null,
559       //          "status":"Current",
560       //          "comments":"Members have full and unlimited access to AR System, and thus can perform all operations\non all objects and data.",
561       //          "createdBy":"AR System",
562       //          "tags":null,
563       //          "permittedGroupsBySecurityLabels":{ 
564       //  
565       //          },
566       //          "permittedUsersBySecurityLabels":{ 
567       //  
568       //          },
569       //          "floatingLicenses":0,
570       //          "applicationFloatingLicenses":null,
571       //          "groupCategory":"com.bmc.arsys.rx.services.group.domain.RegularGroup"
572       //       }
573       //    ]
574       // } 
575       
576       JsonNode jsonObjectEntries = jsonObject.get("data");
577       for (int i=0; i < jsonObjectEntries.size(); i++) {
578         
579         JsonNode jsonObjectGroup = jsonObjectEntries.get(i);
580         
581         GrouperDigitalMarketplaceGroupouperDigitalMarketplaceGroup.html#GrouperDigitalMarketplaceGroup">GrouperDigitalMarketplaceGroup grouperDigitalMarketplaceGroup = new GrouperDigitalMarketplaceGroup();
582   
583         {
584           String groupName = GrouperUtil.jsonJacksonGetString(jsonObjectGroup, "groupName");
585           grouperDigitalMarketplaceGroup.setGroupName(groupName);
586         }
587         
588         {
589           String comments = GrouperUtil.jsonJacksonGetString(jsonObjectGroup, "comments");
590           grouperDigitalMarketplaceGroup.setComments(comments);
591         }
592         
593         {
594           String groupType = GrouperUtil.jsonJacksonGetString(jsonObjectGroup, "groupType");
595           grouperDigitalMarketplaceGroup.setGroupType(groupType);
596         }
597 
598         {
599           String longGroupName = GrouperUtil.jsonJacksonGetString(jsonObjectGroup, "longGroupName");
600           grouperDigitalMarketplaceGroup.setLongGroupName(longGroupName);
601         }
602 
603         {
604           // note: this might be blank: com.bmc.arsys.rx.services.group.domain.RegularGroup
605           if (jsonObjectGroup.has("resourceType")) {
606             String resourceType = GrouperUtil.jsonJacksonGetString(jsonObjectGroup, "resourceType");
607             grouperDigitalMarketplaceGroup.setResourceType(resourceType);
608           }
609         }
610 
611         results.put(grouperDigitalMarketplaceGroup.getGroupName(), grouperDigitalMarketplaceGroup);
612       }
613       
614       
615       debugMap.put("size", GrouperClientUtils.length(results));
616   
617       return results;
618     } catch (RuntimeException re) {
619       debugMap.put("exception", GrouperClientUtils.getFullStackTrace(re));
620       throw re;
621     } finally {
622       GrouperDigitalMarketplaceLog.marketplaceLog(debugMap, startTime);
623     }
624   
625   }
626 
627   /**
628    * @param digitalMarketplaceExternalSystemConfigId
629    * @param grouperDigitalMarketplaceUser must be fresh
630    * @param groupNamesToAdd 
631    * @param groupNamesToDelete 
632    * @param grouperDigitalMarketplaceGroup
633    * @param isIncremental
634    * @return true if added, false if already exists
635    */
636   public static boolean updateMembershipsForDigitalMarketplaceUser(String digitalMarketplaceExternalSystemConfigId, String loginName,
637       Set<String> groupNamesToAdd, Set<String> groupNamesToDelete) throws GrouperDigitalMarketplaceUserDoesNotExist {
638     
639     Map<String, Object> debugMap = new LinkedHashMap<String, Object>();
640     
641     debugMap.put("method", "assignUserToDigitalMarketplaceGroup");
642     debugMap.put("loginName", loginName);
643     
644     groupNamesToAdd = GrouperUtil.nonNull(groupNamesToAdd);
645     groupNamesToDelete = GrouperUtil.nonNull(groupNamesToDelete);
646     
647     if (groupNamesToAdd.size() > 0) {
648       debugMap.put("groupNamesToAdd", GrouperUtil.join(groupNamesToAdd.iterator(), ","));
649     }
650 
651     if (groupNamesToDelete.size() > 0) {
652       debugMap.put("groupNamesToDelete", GrouperUtil.join(groupNamesToDelete.iterator(), ","));
653     }
654 
655     
656     long startTime = System.nanoTime();
657     
658     try {
659   
660       Long lastRetrieved = lastRetrieveUserByLoginNameForMshipUpdate.get(loginName);
661       
662       // make sure we dont get an inconsistent read
663       if (lastRetrieved != null) {
664         long millisSinceLastRetrieved = System.currentTimeMillis() - lastRetrieved;
665         if (millisSinceLastRetrieved < 20000 && millisSinceLastRetrieved > 0) {
666           long sleepMillis = 20000 - millisSinceLastRetrieved;
667           debugMap.put("sleepMillis", sleepMillis);
668           GrouperUtil.sleep(sleepMillis);
669         }
670       }
671 
672       GrouperDigitalMarketplaceUser grouperDigitalMarketplaceUser = GrouperDigitalMarketplaceApiCommands.retrieveDigitalMarketplaceUser(digitalMarketplaceExternalSystemConfigId, loginName);
673 
674       if (grouperDigitalMarketplaceUser == null) {
675         throw new GrouperDigitalMarketplaceUserDoesNotExist("Login name: '" + loginName + "'");
676       }
677       
678       lastRetrieveUserByLoginNameForMshipUpdate.put(loginName, System.currentTimeMillis());
679 
680       // restart timer
681       startTime = System.nanoTime();
682       
683       boolean hasChange = false;
684       Set<String> digitalMarketplaceUuserGroups = GrouperClientUtils.nonNull(grouperDigitalMarketplaceUser.getGroups());
685       for (String groupNameToAdd : groupNamesToAdd) {
686         if (!digitalMarketplaceUuserGroups.contains(groupNameToAdd)) {
687           hasChange = true;
688         }
689       }
690       for (String groupNameToDelete : groupNamesToDelete) {
691         if (digitalMarketplaceUuserGroups.contains(groupNameToDelete)) {
692           hasChange = true;
693         }
694       }
695       
696       
697       if (hasChange) {
698         debugMap.put("hasChange", true);
699         JsonNode jsonObject = grouperDigitalMarketplaceUser.getJsonObject();
700         
701 //        {  
702 //          "groups":[
703 //             "sbe-myit-users",
704 //             "University of Pennsylvania",
705 //             "University of Pennsylvania - 91-Information Systems and Computing",
706 //             "University of Pennsylvania - 91-Information Systems and Computing - 9166-ISC-Tech Services-Network Operations"
707 //          ]
708 //       }
709         
710         ArrayNode groups = (ArrayNode)jsonObject.get("groups");
711 
712 //        removeNonExistentGroups(groups, debugMap);
713 
714         ObjectNode userWithGroupsJson = GrouperUtil.jsonJacksonNode();
715         
716         ArrayNode groupsJson = GrouperUtil.jsonJacksonArrayNode();
717 
718         for (int i=0;i<groups.size();i++) {
719           String existingGroupName = groups.get(i).asText();
720           if (!groupNamesToDelete.contains(existingGroupName)) {
721             groupsJson.add(existingGroupName);
722           }
723         }
724         
725         for (String groupNameToAdd : groupNamesToAdd) {
726           if (!digitalMarketplaceUuserGroups.contains(groupNameToAdd)) {
727             groupsJson.add(groupNameToAdd);
728           }
729         }
730 
731         userWithGroupsJson.set("groups", groupsJson);
732         
733         String userJson = userWithGroupsJson.toString();
734         
735         // /api/rx/application/user/Allen
736         executePutPostMethod(digitalMarketplaceExternalSystemConfigId, debugMap, "/api/rx/application/user/" + grouperDigitalMarketplaceUser.getLoginName(), null, userJson, true);
737         
738         return true;
739       } 
740         
741       debugMap.put("foundExistingMembership", true);
742     
743       return false;
744   
745     } catch (RuntimeException re) {
746       debugMap.put("exception", GrouperClientUtils.getFullStackTrace(re));
747       throw re;
748     } finally {
749       GrouperDigitalMarketplaceLog.marketplaceLog(debugMap, startTime);
750     }
751   }
752 
753   /**
754    * execute a GET method
755    * @param digitalMarketplaceExternalSystemConfigId
756    * @param debugMap
757    * @param path
758    * @param paramMap
759    * @param requestBody 
760    * @param isPutNotPost 
761    * @return the json object
762    */
763   private static JsonNode executePutPostMethod(String digitalMarketplaceExternalSystemConfigId, Map<String, Object> debugMap, String path, Map<String, String> paramMap, String requestBody, boolean isPutNotPost) {
764   
765     GrouperHttpClientuperHttpClient.html#GrouperHttpClient">GrouperHttpClient grouperHttpClient = new GrouperHttpClient();
766     
767     String jwtToken = retrieveJwtToken(digitalMarketplaceExternalSystemConfigId, debugMap);
768   
769     String fullUrl = calculateUrl(digitalMarketplaceExternalSystemConfigId, path, paramMap);
770     grouperHttpClient.assignUrl(fullUrl);
771     if (isPutNotPost) {
772       grouperHttpClient.assignGrouperHttpMethod(GrouperHttpMethod.put);
773     } else {
774       grouperHttpClient.assignGrouperHttpMethod(GrouperHttpMethod.post);
775     }
776     
777     debugMap.put(isPutNotPost ? "put" : "post", true);
778     //debugMap.put("requestBody", requestBody);
779     grouperHttpClient.addHeader("authorization", "AR-JWT " + jwtToken);
780     grouperHttpClient.addHeader("Content-Type", "application/json");
781     
782     String xRequestedBy = GrouperConfig.retrieveConfig().propertyValueStringRequired("grouper.remedyDigitalMarketplaceConnector."+digitalMarketplaceExternalSystemConfigId+".xRequestedByHeader");
783     grouperHttpClient.addHeader("X-Requested-By", xRequestedBy);
784     if (!GrouperClientUtils.isBlank(requestBody)) {
785       grouperHttpClient.assignBody(requestBody);
786     }
787     
788     int responseCodeInt = -1;
789     String responseBody = null;
790     long startTime = System.nanoTime();
791     try {
792       grouperHttpClient.executeRequest();
793       responseCodeInt = grouperHttpClient.getResponseCode();
794       
795       try {
796         responseBody = grouperHttpClient.getResponseBody();
797       } catch (Exception e) {
798         debugMap.put("getResponseBodyException", ExceptionUtils.getStackTrace(e));
799       }
800       
801     } catch (Exception e) {
802       throw new RuntimeException("error in authn", e);
803     } finally {
804       debugMap.put("getMillis", ((System.nanoTime() - startTime) / 1000000) + "ms");
805     }
806     
807     debugMap.put("responseCodeInt", responseCodeInt);
808     if (responseCodeInt != 200 && responseCodeInt != 201 && responseCodeInt != 204) {
809       throw new RuntimeException("get didnt return 200, it returned: " + responseCodeInt + "," + responseBody);
810     }
811   
812     // hmmm, no body
813     if (GrouperClientUtils.isBlank(responseBody)) {
814       return null;
815     }
816     
817     JsonNode jsonObject = GrouperUtil.jsonJacksonNode( responseBody );     
818   
819     return jsonObject;
820   }
821 
822 //  /**
823 //   * 
824 //   * @param groups
825 //   * @param debugMap 
826 //   * @return the new node with removed groups
827 //   */
828 //  private static void removeNonExistentGroups(ArrayNode groups, Map<String, Object> debugMap) {
829 //
830 //    if (groups == null || groups.size() == 0) {
831 //      return;
832 //    }
833 //
834 //    //get all groupNames
835 //    Set<String> groupNames = new LinkedHashSet<String>();
836 //    for (int i=0;i<groups.size();i++) {
837 //      String groupName = groups.get(i).asText();
838 //      groupNames.add(groupName);
839 //    }
840 //
841 //    List<String> originalGroupNames = new ArrayList<String>(groupNames);
842 //
843 //    for (int i=0;i<originalGroupNames.size();i++) {
844 //      String groupName = originalGroupNames.get(i);
845 //      if (!GrouperDigitalMarketplaceGroup.retrieveGroups().containsKey(groupName)) {
846 //        groups.remove(i);
847 //        debugMap.put("removeGroup_" + groupName, true);
848 //      }
849 //    }
850 //  }
851   
852   /**
853    * @param digitalMarketplaceExternalSystemConfigId
854    * @param groupName 
855    * @param longGroupName 
856    * @param comments 
857    * @param groupType
858    * @return true if added, false if already exists
859    */
860   public static void createDigitalMarketplaceGroup(String digitalMarketplaceExternalSystemConfigId, String groupName, String longGroupName, String comments, String groupType) {
861     
862     Map<String, Object> debugMap = new LinkedHashMap<String, Object>();
863   
864     debugMap.put("method", "createDigitalMarketplaceGroup");
865     debugMap.put("groupName", groupName);
866 
867     long startTime = System.nanoTime();
868     
869     try {
870   
871       // restart timer
872       startTime = System.nanoTime();
873       
874       debugMap.put("foundExistingGroup", false);
875       
876       //  {
877       //    "resourceType": "com.bmc.arsys.rx.services.group.domain.RegularGroup",
878       //    "groupName": "chris-hyzer-test",
879       //    "longGroupName": "chris-hyzer-test",
880       //    "groupType": "Change",
881       //    "comments": "chris-hyzer-test comments",
882       //    "status": "Current", "tags" : ["virtualmarketplace"]
883       //  }          
884       
885       
886       ObjectNode groupJsonObject = GrouperUtil.jsonJacksonNode();
887       
888       groupJsonObject.put("resourceType", "com.bmc.arsys.rx.services.group.domain.RegularGroup");
889       groupJsonObject.put("groupName", groupName);
890       if (GrouperClientUtils.isBlank(longGroupName)) {
891         longGroupName = groupName;
892       }
893       groupJsonObject.put("longGroupName", longGroupName);
894       groupType = StringUtils.defaultIfBlank(groupType, "Change");
895       groupJsonObject.put("groupType", groupType);
896       if (!GrouperClientUtils.isBlank(comments)) {
897         groupJsonObject.put("comments", comments);
898       }
899       groupJsonObject.put("status", "Current");
900       ArrayNode tagsArray = GrouperUtil.jsonJacksonArrayNode();
901       tagsArray.add("virtualmarketplace");
902       groupJsonObject.put("tags", tagsArray );
903       
904       String groupJson = groupJsonObject.toString();
905       
906       // /api/rx/application/user/Allen
907       executePutPostMethod(digitalMarketplaceExternalSystemConfigId, debugMap, "/api/rx/application/group/", null, groupJson, false);
908         
909     } catch (RuntimeException re) {
910       debugMap.put("exception", GrouperClientUtils.getFullStackTrace(re));
911       throw re;
912     } finally {
913       GrouperDigitalMarketplaceLog.marketplaceLog(debugMap, startTime);
914     }
915   }
916 
917   /**
918    * @param digitalMarketplaceExternalSystemConfigId
919    * @param groupName 
920    * @return true if added, false if already exists
921    */
922   public static Boolean deleteDigitalMarketplaceGroup(String digitalMarketplaceExternalSystemConfigId, String groupName) {
923     
924     Map<String, Object> debugMap = new LinkedHashMap<String, Object>();
925   
926     debugMap.put("method", "deleteDigitalMarketplaceGroup");
927     debugMap.put("groupName", groupName);
928   
929     long startTime = System.nanoTime();
930     
931     try {
932   
933       // refresh the user object
934       GrouperDigitalMarketplaceGroup grouperDigitalMarketplaceGroupExisting = retrieveDigitalMarketplaceGroups(digitalMarketplaceExternalSystemConfigId).get(groupName);
935       
936       // restart timer
937       startTime = System.nanoTime();
938       
939       if (grouperDigitalMarketplaceGroupExisting != null) {
940         debugMap.put("foundExistingGroup", true);
941         
942         // /api/rx/application/user/Allen
943         executeDeleteMethod(digitalMarketplaceExternalSystemConfigId, debugMap, "/api/rx/application/group/" + groupName, null);
944 
945         return true;
946       } 
947         
948       debugMap.put("foundExistingGroup", true);
949     
950       return false;
951   
952     } catch (RuntimeException re) {
953       debugMap.put("exception", GrouperClientUtils.getFullStackTrace(re));
954       throw re;
955     } finally {
956       GrouperDigitalMarketplaceLog.marketplaceLog(debugMap, startTime);
957     }
958   }
959 
960   /**
961    * execute a DELETE method
962    * @param digitalMarketplaceExternalSystemConfigId
963    * @param debugMap
964    * @param path
965    * @param paramMap
966    * @return the json object
967    */
968   private static JsonNode executeDeleteMethod(String digitalMarketplaceExternalSystemConfigId, Map<String, Object> debugMap, String path, Map<String, String> paramMap) {
969   
970     GrouperHttpClientuperHttpClient.html#GrouperHttpClient">GrouperHttpClient grouperHttpClient = new GrouperHttpClient();
971     
972   
973     String jwtToken = retrieveJwtToken(digitalMarketplaceExternalSystemConfigId, debugMap);
974   
975     String fullUrl = calculateUrl(digitalMarketplaceExternalSystemConfigId, path, paramMap);
976     
977     grouperHttpClient.assignGrouperHttpMethod(GrouperHttpMethod.delete);
978     grouperHttpClient.assignUrl(fullUrl);
979     
980     debugMap.put("delete", true);
981 
982     //debugMap.put("requestBody", requestBody);
983     grouperHttpClient.addHeader("authorization", "AR-JWT " + jwtToken);
984     grouperHttpClient.addHeader("Content-Type", "application/json");
985     String xRequestedBy = GrouperConfig.retrieveConfig().propertyValueStringRequired("grouper.remedyDigitalMarketplaceConnector."+digitalMarketplaceExternalSystemConfigId+".xRequestedByHeader");
986     grouperHttpClient.addHeader("X-Requested-By", xRequestedBy);
987     
988     int responseCodeInt = -1;
989     String responseBody = null;
990     long startTime = System.nanoTime();
991     try {
992       grouperHttpClient.executeRequest();
993       responseCodeInt = grouperHttpClient.getResponseCode();
994       
995       try {
996         responseBody = grouperHttpClient.getResponseBody();
997       } catch (Exception e) {
998         debugMap.put("getResponseBodyException", ExceptionUtils.getStackTrace(e));
999       }
1000       
1001     } catch (Exception e) {
1002       throw new RuntimeException("error in authn", e);
1003     } finally {
1004       debugMap.put("getMillis", ((System.nanoTime() - startTime) / 1000000) + "ms");
1005     }
1006     
1007     debugMap.put("responseCodeInt", responseCodeInt);
1008     if (responseCodeInt != 200 && responseCodeInt != 201 && responseCodeInt != 204) {
1009       throw new RuntimeException("get didnt return 204, it returned: " + responseCodeInt + "," + responseBody);
1010     }
1011   
1012     // hmmm, no body
1013     if (GrouperClientUtils.isBlank(responseBody)) {
1014       return null;
1015     }
1016     
1017     JsonNode jsonObject = GrouperUtil.jsonJacksonNode( responseBody );     
1018   
1019     return jsonObject;
1020   }
1021 
1022 }