001 package org.hackystat.telemetry.analyzer.configuration; 002 003 import java.util.ArrayList; 004 import java.util.Collection; 005 import java.util.Map; 006 import java.util.TreeMap; 007 008 import org.hackystat.sensorbase.resource.users.jaxb.User; 009 010 /** 011 * The manager for <code>TelemetryDefinitionInfo</code> objects. 012 * <p> 013 * Warning: This class is NOT thread-safe. 014 * <p> 015 * V8 Notes: If/when we decide to provide project-level sharing, we need to enforce the 016 * condition that only one instance of a name can exist for a given project. Right now it 017 * appears that multiple users can create telemetry definitions with the same name with 018 * project scope. 019 * <p> 020 * I wonder why this class is not thread safe. Perhaps synchronization occurs at a higher 021 * level. 022 * 023 * @author (Cedric) Qin Zhang 024 */ 025 class TelemetryDefinitionInfoRepository { 026 027 /** 028 * Two level map storing <code>TelemetryDefInfo</code> objects. The first 029 * level is keyed by userEmail (the owner), and the second level is keyed by the 030 * name of the telemetry definition object. 031 */ 032 private Map<String, Map<String, TelemetryDefinitionInfo>> ownerKeyDefMap = 033 new TreeMap<String, Map<String, TelemetryDefinitionInfo>>(); 034 035 /** 036 * Find the telemetry definition object by name. This method returns null if 037 * the telemetry definition cannot be found. 038 * <p> 039 * <b>Important</b> Note that it's possible that there are multiple 040 * definitions with the same name (all have project level sharing, but owned 041 * by different users). In this case, any one of the definitions might be 042 * returned. The only guarantee is that if there are definitions in the 043 * project share scope and global share scope with the same name, the one in 044 * project share scope will be returned. 045 * 046 * @param owner The owner under which to find the telemetry definition object. 047 * @param name The name of the telemetry definition. 048 * @param includeShared If true, then those telemetry definitions owned by 049 * other users, but is shared will also be returned. 050 * 051 * @return An instance of <code>TelemetryDefInfo</code> object if found, or null. 052 */ 053 TelemetryDefinitionInfo find(User owner, String name, boolean includeShared) { 054 TelemetryDefinitionInfo result = null; 055 056 // find whether there is any definition owned by this user 057 Map<String, TelemetryDefinitionInfo> secondLevelMap = 058 this.ownerKeyDefMap.get(owner.getEmail()); 059 if (secondLevelMap != null) { 060 result = secondLevelMap.get(name); 061 } 062 063 if (result == null && includeShared) { 064 // cannot find definition owned by this user, now search all definitions 065 // accessible to this user. 066 TelemetryDefinitionInfo globalDefInfo = null; 067 for (TelemetryDefinitionInfo theDefInfo : this.findAll(owner, true)) { 068 if (theDefInfo.getName().equals(name)) { 069 if (theDefInfo.getShareScope().isGlobal()) { 070 globalDefInfo = theDefInfo; 071 } 072 else { 073 result = theDefInfo; 074 break; 075 } 076 } 077 } 078 if (result == null) { 079 result = globalDefInfo; 080 } 081 } 082 083 return result; 084 } 085 086 /** 087 * Gets a list of telemetry definitions that this user has access to. 088 * 089 * @param owner The owner of the telemetry definitions returned. 090 * @param includesShared If true, then those telemetry definitions owned by 091 * other users, but is shared will also be returned. 092 * 093 * @return A collection of <code>TelemetryDefInfo</code> objects. 094 */ 095 Collection<TelemetryDefinitionInfo> findAll(User owner, boolean includesShared) { 096 if (!includesShared) { //NOPMD 097 Map<String, TelemetryDefinitionInfo> secondLevelMap = 098 this.ownerKeyDefMap.get(owner.getEmail()); 099 return secondLevelMap == null ? 100 new ArrayList<TelemetryDefinitionInfo>(0) : secondLevelMap.values(); 101 } 102 else { 103 // since shared defs need to be included, we need to scan everything. 104 ArrayList<TelemetryDefinitionInfo> list = new ArrayList<TelemetryDefinitionInfo>(); 105 for (Map<String, TelemetryDefinitionInfo> secondLevelMap : this.ownerKeyDefMap.values()) { 106 for (TelemetryDefinitionInfo defInfo : secondLevelMap.values()) { 107 if (owner.equals(defInfo.getOwner())) { 108 list.add(defInfo); 109 } 110 else { 111 ShareScope shareScope = defInfo.getShareScope(); 112 if (shareScope.isGlobal()) { 113 list.add(defInfo); 114 } 115 else if (shareScope.isProject()) { 116 try { 117 if (shareScope.getProject().getMembers().getMember().contains(owner)) { 118 list.add(defInfo); 119 } 120 } 121 catch (TelemetryConfigurationException ex) { 122 // should not happen, since we have already checked isProject() 123 throw new RuntimeException(ex); 124 } 125 } 126 } 127 } 128 } 129 return list; 130 } 131 } 132 133 /** 134 * Adds a telemetry definition to this in-memory repository. 135 * 136 * @param telemetryDefInfo Information about the definition to be added. 137 * @throws TelemetryConfigurationException If there is duplicated definition. 138 */ 139 void add(TelemetryDefinitionInfo telemetryDefInfo) throws TelemetryConfigurationException { 140 User owner = telemetryDefInfo.getOwner(); 141 Map<String, TelemetryDefinitionInfo> secondLevelMap = 142 this.ownerKeyDefMap.get(owner.getEmail()); 143 if (secondLevelMap == null) { 144 secondLevelMap = new TreeMap<String, TelemetryDefinitionInfo>(); 145 this.ownerKeyDefMap.put(owner.getEmail(), secondLevelMap); 146 } 147 148 String name = telemetryDefInfo.getName(); 149 if (secondLevelMap.containsKey(name)) { 150 throw new TelemetryConfigurationException("User " + owner.toString() 151 + " already has telemetry definition " + name + " defined."); 152 } 153 secondLevelMap.put(name, telemetryDefInfo); 154 } 155 156 /** 157 * Goes through all users, and checks whether there is a definition by name. 158 * 159 * @param telemetryDefinitionName The definition name. 160 * @return True if it exists. 161 */ 162 boolean exists(String telemetryDefinitionName) { 163 boolean found = false; 164 for (Map<String, TelemetryDefinitionInfo> secondLevelMap : this.ownerKeyDefMap.values()) { 165 if (secondLevelMap.containsKey(telemetryDefinitionName)) { 166 found = true; 167 break; 168 } 169 } 170 return found; 171 } 172 173 /** 174 * Deletes a telemetry object definition. Only the owner can make the 175 * deletion. This method does nothing if the definition does not exist. 176 * 177 * @param owner The owner of the definition. 178 * @param telemetryDefinitionName The name of the definition. 179 */ 180 void remove(User owner, String telemetryDefinitionName) { 181 Map<String, TelemetryDefinitionInfo> secondLevelMap = this.ownerKeyDefMap.get(owner.getEmail()); 182 if (secondLevelMap != null) { 183 secondLevelMap.remove(telemetryDefinitionName); 184 } 185 } 186 }