source: src/main/java/geniusweb/profilesserver/DefaultProfilesRepository.java@ 25

Last change on this file since 25 was 25, checked in by bart, 4 years ago

Fixed memory leak. MOPAC2. removed jcenter build dependencies

File size: 6.6 KB
Line 
1package geniusweb.profilesserver;
2
3import java.io.IOException;
4import java.util.Collections;
5import java.util.LinkedList;
6import java.util.List;
7import java.util.Map;
8import java.util.Optional;
9import java.util.concurrent.ConcurrentHashMap;
10import java.util.stream.Collectors;
11
12import geniusweb.issuevalue.Domain;
13import geniusweb.profile.Profile;
14import geniusweb.profilesserver.events.ChangeEvent;
15import geniusweb.profilesserver.events.DomainChangeEvent;
16import geniusweb.profilesserver.events.ProfileChangeEvent;
17import tudelft.utilities.listener.DefaultListenable;
18
19/**
20 * Listeners will get a notification call with a {@link ChangeEvent} when a
21 * {@link Domain} or {@link Profile} is changed/removed.
22 * <p>
23 * Warning: If possible create only 1 instance of this to avoid expensive
24 * duplicate bookkeeping. This class could , but is not made a singleton to
25 * facilitate testing.
26 * <p>
27 * This object is mutable: the {@link #available} map is updated with the
28 * current state of available domains and profiles.
29 */
30public class DefaultProfilesRepository extends DefaultListenable<ChangeEvent>
31 implements ProfilesRepository {
32 /**
33 * all currently available profiles and domains. Invariant for this class:
34 * All {@link Domain}s in this map have a unique name that matches the
35 * directory name and file name on the file system below the given
36 * directory. All profile values in the map their
37 * {@link Profile#getDomain()} match with the domain key in the map.
38 */
39 protected final Map<Domain, List<Profile>> available = new ConcurrentHashMap<>();
40
41 @Override
42 public synchronized Domain getDomain(String name) {
43 if (name == null) {
44 throw new IllegalArgumentException("domain = null");
45 }
46 Optional<Domain> optional = available.keySet().stream()
47 .filter(domain -> name.equals(domain.getName())).findFirst();
48 return optional.isPresent() ? optional.get() : null;
49 }
50
51 @Override
52 public synchronized Profile getProfile(String domainprofilename) {
53 if (domainprofilename == null) {
54 throw new IllegalArgumentException("profilename=null");
55 }
56 String[] values = domainprofilename.split("/", 2);
57 if (values.length != 2) {
58 throw new IllegalArgumentException(
59 "profile name must be <domainname>/<profilename> but got "
60 + domainprofilename);
61 }
62 // check if domain exists.
63 Domain domain = getDomain(values[0]);
64 if (domain == null) {
65 return null;
66 }
67 String profilename = values[1];
68 Optional<Profile> profile = available.get(domain).stream()
69 .filter(prof -> profilename.equals(prof.getName())).findFirst();
70 return profile.isPresent() ? profile.get() : null;
71 }
72
73 @Override
74 public synchronized List<Profile> getProfiles(String domainname) {
75 Domain domain = getDomain(domainname);
76 return domain == null ? Collections.EMPTY_LIST
77 : Collections.unmodifiableList(available.get(domain));
78
79 }
80
81 @Override
82 public synchronized List<String> getDomains() {
83 return available.keySet().stream().map(domain -> domain.getName())
84 .collect(Collectors.toList());
85 }
86
87 @Override
88 public synchronized void putProfile(Profile profile) throws IOException {
89 if (!available.containsKey(profile.getDomain())) {
90 throw new IOException("profile's domain does not exist");
91 }
92 add(profile);
93 }
94
95 /**************** support for updating ***************/
96 /**
97 * Add or replace domain. For internal use so little checking of arguments.
98 * If the domain already exists and is the same, we exit immediately. If
99 * such a domain does not yet exist, or a domain with the same name already
100 * exists but that domain is different, the old domain is removed (and all
101 * associated profiles). The new domain is inserted without any known
102 * profiles.
103 *
104 * @param domain the new domain.
105 */
106 protected void add(Domain domain) {
107 synchronized (this) {
108 if (domain == null) {
109 throw new IllegalArgumentException("domain=null");
110 }
111 if (available.containsKey(domain))
112 return;
113 Optional<Domain> existingdom = available.keySet().stream()
114 .filter(dom -> dom.getName().equals(domain.getName()))
115 .findFirst();
116 if (existingdom.isPresent()) {
117 remove(existingdom.get());
118 }
119 available.put(domain, new LinkedList<>());
120 }
121 notifyListeners(new DomainChangeEvent(null, domain));
122 }
123
124 /**
125 * Removes domain from the known domains list, plus all profiles associated
126 * with the domain. For internal use so little checking of arguments.
127 *
128 * @param domain the domain to remove. If null, nothing happens.
129 */
130 protected void remove(Domain domain) {
131 if (domain == null)
132 return;
133
134 List<Profile> removedprofiles;
135 synchronized (this) {
136 removedprofiles = available.get(domain);
137 available.remove(domain);
138 }
139
140 for (Profile prof : removedprofiles) {
141 notifyListeners(new ProfileChangeEvent(prof, null));
142 }
143 notifyListeners(new DomainChangeEvent(domain, null));
144 }
145
146 /**
147 * Adds profile. Internal use only. Domain must exist already. If a profile
148 * with same name already exists, that one is replaced with the given
149 * profile.
150 *
151 * @param profile the profile to add. Ignored if profile=null or if profile
152 * already known. Profile MUST contain a registered domain.
153 */
154 protected void add(Profile profile) {
155 Profile oldprofile = null;
156 synchronized (this) {
157 if (profile == null)
158 return;
159 List<Profile> profiles = available.get(profile.getDomain());
160 if (profiles == null) {
161 throw new IllegalArgumentException("Profile "
162 + profile.getName() + " uses unknown domain "
163 + profile.getDomain());
164 }
165 Optional<Profile> knownprofile = profiles.stream()
166 .filter(prof -> profile.getName().equals(prof.getName()))
167 .findFirst();
168 if (knownprofile.isPresent()) {
169 oldprofile = knownprofile.get();
170 if (profile.equals(oldprofile))
171 return;
172
173 profiles.remove(oldprofile);
174 }
175 profiles.add(profile);
176 }
177 notifyListeners(new ProfileChangeEvent(oldprofile, profile));
178
179 }
180
181 /**
182 * Removes profile. Internal use only.
183 *
184 * @param profile to be removed {@link Profile}
185 */
186 protected void remove(Profile profile) {
187 synchronized (this) {
188 if (profile == null) {
189 throw new IllegalArgumentException("profile=null");
190 }
191 List<Profile> profilelist = available.get(profile.getDomain());
192 if (profilelist == null) {
193 throw new IllegalArgumentException("Profile "
194 + profile.getName() + " uses unknown domain "
195 + profile.getDomain());
196 }
197 profilelist.remove(profile);
198 }
199 notifyListeners(new ProfileChangeEvent(profile, null));
200
201 }
202
203 /**
204 *
205 * @param name the name to check
206 * @return true iff name is simple name. False if not (also if name=null)
207 */
208 protected boolean isSimpleName(String name) {
209 if (name == null)
210 return false;
211 return name.matches("[a-zA-Z0-9]+");
212 }
213
214}
Note: See TracBrowser for help on using the repository browser.