|
@@ -39,8 +39,10 @@ import com.dmdirc.util.io.ConfigFile;
|
39
|
39
|
import com.dmdirc.util.io.InvalidConfigFileException;
|
40
|
40
|
import com.dmdirc.util.resourcemanager.ResourceManager;
|
41
|
41
|
|
42
|
|
-import java.io.File;
|
43
|
42
|
import java.io.IOException;
|
|
43
|
+import java.nio.file.DirectoryStream;
|
|
44
|
+import java.nio.file.Files;
|
|
45
|
+import java.nio.file.Path;
|
44
|
46
|
import java.util.ArrayList;
|
45
|
47
|
import java.util.Collection;
|
46
|
48
|
import java.util.Collections;
|
|
@@ -48,6 +50,7 @@ import java.util.HashMap;
|
48
|
50
|
import java.util.LinkedHashSet;
|
49
|
51
|
import java.util.List;
|
50
|
52
|
import java.util.Map;
|
|
53
|
+import java.util.concurrent.ConcurrentHashMap;
|
51
|
54
|
|
52
|
55
|
import org.slf4j.Logger;
|
53
|
56
|
import org.slf4j.LoggerFactory;
|
|
@@ -65,9 +68,9 @@ public class IdentityManager implements IdentityFactory, IdentityController {
|
65
|
68
|
/** The domain used for profile settings. */
|
66
|
69
|
private static final String PROFILE_DOMAIN = "profile";
|
67
|
70
|
/** Base configuration directory where the main configuration file will be located. */
|
68
|
|
- private final String configDirectory;
|
|
71
|
+ private final Path configDirectory;
|
69
|
72
|
/** Directory to save and load identities in. */
|
70
|
|
- private final String identitiesDirectory;
|
|
73
|
+ private final Path identitiesDirectory;
|
71
|
74
|
/**
|
72
|
75
|
* The identities that have been loaded into this manager.
|
73
|
76
|
*
|
|
@@ -75,6 +78,8 @@ public class IdentityManager implements IdentityFactory, IdentityController {
|
75
|
78
|
* custom type as the key.
|
76
|
79
|
*/
|
77
|
80
|
private final MapList<String, ConfigProvider> identities = new MapList<>();
|
|
81
|
+ /** Map of paths to corresponding config providers, to facilitate reloading. */
|
|
82
|
+ private final Map<Path, ConfigProvider> configProvidersByPath = new ConcurrentHashMap<>();
|
78
|
83
|
/** The event bus to post events to. */
|
79
|
84
|
private final DMDircMBassador eventBus;
|
80
|
85
|
/**
|
|
@@ -100,7 +105,7 @@ public class IdentityManager implements IdentityFactory, IdentityController {
|
100
|
105
|
* @param identitiesDirectory The directory to store identities in.
|
101
|
106
|
* @param eventBus The event bus to post events to
|
102
|
107
|
*/
|
103
|
|
- public IdentityManager(final String baseDirectory, final String identitiesDirectory,
|
|
108
|
+ public IdentityManager(final Path baseDirectory, final Path identitiesDirectory,
|
104
|
109
|
final DMDircMBassador eventBus) {
|
105
|
110
|
this.configDirectory = baseDirectory;
|
106
|
111
|
this.identitiesDirectory = identitiesDirectory;
|
|
@@ -129,7 +134,7 @@ public class IdentityManager implements IdentityFactory, IdentityController {
|
129
|
134
|
target.setGlobalDefault();
|
130
|
135
|
target.setOrder(500000);
|
131
|
136
|
|
132
|
|
- final ConfigFile addonConfigFile = new ConfigFile((File) null);
|
|
137
|
+ final ConfigFile addonConfigFile = new ConfigFile((Path) null);
|
133
|
138
|
final Map<String, String> addonSettings = new HashMap<>();
|
134
|
139
|
addonSettings.put("name", "Addon defaults");
|
135
|
140
|
addonConfigFile.addDomain("identity", addonSettings);
|
|
@@ -148,13 +153,18 @@ public class IdentityManager implements IdentityFactory, IdentityController {
|
148
|
153
|
final String[] targets = {"default", "modealiases"};
|
149
|
154
|
|
150
|
155
|
for (String target : targets) {
|
151
|
|
- final File file = new File(identitiesDirectory + target);
|
|
156
|
+ final Path file = identitiesDirectory.resolve(target);
|
152
|
157
|
|
153
|
|
- if (file.exists() && !file.isDirectory()) {
|
|
158
|
+ if (Files.exists(file) && !Files.isDirectory(file)) {
|
154
|
159
|
boolean success = false;
|
155
|
160
|
for (int i = 0; i < 10 && !success; i++) {
|
156
|
|
- final String suffix = ".old" + (i > 0 ? "-" + i : "");
|
157
|
|
- success = file.renameTo(new File(file.getParentFile(), target + suffix));
|
|
161
|
+ try {
|
|
162
|
+ final String suffix = ".old" + (i > 0 ? "-" + i : "");
|
|
163
|
+ Files.move(file, identitiesDirectory.resolve(target + suffix));
|
|
164
|
+ success = true;
|
|
165
|
+ } catch (IOException ex) {
|
|
166
|
+ success = false;
|
|
167
|
+ }
|
158
|
168
|
}
|
159
|
169
|
|
160
|
170
|
if (!success) {
|
|
@@ -162,22 +172,33 @@ public class IdentityManager implements IdentityFactory, IdentityController {
|
162
|
172
|
"Unable to create directory for default settings folder ("
|
163
|
173
|
+ target + ')',
|
164
|
174
|
"A file with that name already exists, and couldn't be renamed."
|
165
|
|
- + " Rename or delete " + file.getAbsolutePath()));
|
|
175
|
+ + " Rename or delete " + file));
|
166
|
176
|
continue;
|
167
|
177
|
}
|
168
|
178
|
}
|
169
|
179
|
|
170
|
|
- if (!file.exists() && !file.mkdirs()) {
|
171
|
|
- eventBus.publishAsync(new UserErrorEvent(ErrorLevel.FATAL, null,
|
172
|
|
- "Unable to create required directory '" + file.getAbsolutePath()
|
173
|
|
- + "'. Please check file permissions or specify "
|
174
|
|
- + "a different configuration directory.", ""));
|
175
|
|
- return;
|
|
180
|
+ if (!Files.exists(file)) {
|
|
181
|
+ try {
|
|
182
|
+ Files.createDirectories(file);
|
|
183
|
+ } catch (IOException ex) {
|
|
184
|
+ eventBus.publishAsync(new UserErrorEvent(ErrorLevel.FATAL, null,
|
|
185
|
+ "Unable to create required directory '" + file + "'. Please check " +
|
|
186
|
+ "file permissions or specify a different configuration " +
|
|
187
|
+ "directory.", ""));
|
|
188
|
+ return;
|
|
189
|
+ }
|
176
|
190
|
}
|
177
|
191
|
|
178
|
|
- final File[] files = file.listFiles();
|
179
|
|
- if (files == null || files.length == 0) {
|
180
|
|
- extractIdentities(target);
|
|
192
|
+ try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(file)) {
|
|
193
|
+ if (!directoryStream.iterator().hasNext()) {
|
|
194
|
+ extractIdentities(target);
|
|
195
|
+ }
|
|
196
|
+ } catch (IOException ex) {
|
|
197
|
+ eventBus.publishAsync(new UserErrorEvent(ErrorLevel.FATAL, null,
|
|
198
|
+ "Unable to iterate required directory '" + file + "'. Please check " +
|
|
199
|
+ "file permissions or specify a different configuration " +
|
|
200
|
+ "directory.", ""));
|
|
201
|
+ return;
|
181
|
202
|
}
|
182
|
203
|
|
183
|
204
|
loadUser(file);
|
|
@@ -196,7 +217,7 @@ public class IdentityManager implements IdentityFactory, IdentityController {
|
196
|
217
|
|
197
|
218
|
if (bundledVersion.compareTo(installedVersion) > 0) {
|
198
|
219
|
extractIdentities("default");
|
199
|
|
- loadUser(new File(identitiesDirectory, "default"));
|
|
220
|
+ loadUser(identitiesDirectory.resolve("default"));
|
200
|
221
|
}
|
201
|
222
|
}
|
202
|
223
|
}
|
|
@@ -233,19 +254,16 @@ public class IdentityManager implements IdentityFactory, IdentityController {
|
233
|
254
|
|
234
|
255
|
@Override
|
235
|
256
|
public void loadUserIdentities() {
|
236
|
|
- final File dir = new File(identitiesDirectory);
|
237
|
|
-
|
238
|
|
- if (!dir.exists()) {
|
|
257
|
+ if (!Files.exists(identitiesDirectory)) {
|
239
|
258
|
try {
|
240
|
|
- dir.mkdirs();
|
241
|
|
- dir.createNewFile();
|
|
259
|
+ Files.createDirectories(identitiesDirectory);
|
242
|
260
|
} catch (IOException ex) {
|
243
|
261
|
eventBus.publishAsync(new UserErrorEvent(ErrorLevel.MEDIUM, ex,
|
244
|
262
|
"Unable to create identity dir", ""));
|
245
|
263
|
}
|
246
|
264
|
}
|
247
|
265
|
|
248
|
|
- loadUser(dir);
|
|
266
|
+ loadUser(identitiesDirectory);
|
249
|
267
|
}
|
250
|
268
|
|
251
|
269
|
/**
|
|
@@ -257,22 +275,21 @@ public class IdentityManager implements IdentityFactory, IdentityController {
|
257
|
275
|
"The specified File is not null",
|
258
|
276
|
"The specified File is a directory"
|
259
|
277
|
})
|
260
|
|
- private void loadUser(final File dir) {
|
|
278
|
+ private void loadUser(final Path dir) {
|
261
|
279
|
checkNotNull(dir);
|
262
|
|
- checkArgument(dir.isDirectory());
|
|
280
|
+ checkArgument(Files.isDirectory(dir));
|
263
|
281
|
|
264
|
|
- final File[] files = dir.listFiles();
|
265
|
|
- if (files == null) {
|
266
|
|
- eventBus.publishAsync(new UserErrorEvent(ErrorLevel.MEDIUM, null,
|
267
|
|
- "Unable to load user identity files from " + dir.getAbsolutePath(), ""));
|
268
|
|
- } else {
|
269
|
|
- for (File file : files) {
|
270
|
|
- if (file.isDirectory()) {
|
271
|
|
- loadUser(file);
|
|
282
|
+ try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(dir)) {
|
|
283
|
+ for (Path child : directoryStream) {
|
|
284
|
+ if (Files.isDirectory(child)) {
|
|
285
|
+ loadUser(child);
|
272
|
286
|
} else {
|
273
|
|
- loadIdentity(file);
|
|
287
|
+ loadIdentity(child);
|
274
|
288
|
}
|
275
|
289
|
}
|
|
290
|
+ } catch (IOException ex) {
|
|
291
|
+ eventBus.publishAsync(new UserErrorEvent(ErrorLevel.MEDIUM, ex,
|
|
292
|
+ "Unable to load user identity files from " + dir, ""));
|
276
|
293
|
}
|
277
|
294
|
}
|
278
|
295
|
|
|
@@ -282,37 +299,31 @@ public class IdentityManager implements IdentityFactory, IdentityController {
|
282
|
299
|
*
|
283
|
300
|
* @param file The file to load the identity from.
|
284
|
301
|
*/
|
285
|
|
- private void loadIdentity(final File file) {
|
|
302
|
+ private void loadIdentity(final Path file) {
|
286
|
303
|
synchronized (identities) {
|
287
|
|
- for (ConfigProvider identity : getAllIdentities()) {
|
288
|
|
- if (identity instanceof ConfigFileBackedConfigProvider
|
289
|
|
- && ((ConfigFileBackedConfigProvider) identity).isFile(file)) {
|
290
|
|
- // TODO: This manager should keep a list of files->identities instead of
|
291
|
|
- // relying on the identities remembering.
|
292
|
|
- try {
|
293
|
|
- identity.reload();
|
294
|
|
- } catch (IOException ex) {
|
295
|
|
- eventBus.publishAsync(new UserErrorEvent(ErrorLevel.MEDIUM, null,
|
296
|
|
- "I/O error when reloading identity file: "
|
297
|
|
- + file.getAbsolutePath() + " (" + ex.getMessage() + ')', ""));
|
298
|
|
- } catch (InvalidConfigFileException ex) {
|
299
|
|
- // Do nothing
|
300
|
|
- }
|
301
|
|
-
|
302
|
|
- return;
|
|
304
|
+ if (configProvidersByPath.containsKey(file)) {
|
|
305
|
+ try {
|
|
306
|
+ configProvidersByPath.get(file).reload();
|
|
307
|
+ } catch (IOException ex) {
|
|
308
|
+ eventBus.publishAsync(new UserErrorEvent(ErrorLevel.MEDIUM, null,
|
|
309
|
+ "I/O error when reloading identity file: "
|
|
310
|
+ + file + " (" + ex.getMessage() + ')', ""));
|
|
311
|
+ } catch (InvalidConfigFileException ex) {
|
|
312
|
+ // Do nothing
|
303
|
313
|
}
|
304
|
314
|
}
|
305
|
315
|
}
|
306
|
316
|
|
307
|
317
|
try {
|
308
|
|
- addConfigProvider(new ConfigFileBackedConfigProvider(this, file, false));
|
|
318
|
+ final ConfigProvider provider = new ConfigFileBackedConfigProvider(this, file, false);
|
|
319
|
+ addConfigProvider(provider);
|
|
320
|
+ configProvidersByPath.put(file, provider);
|
309
|
321
|
} catch (InvalidIdentityFileException ex) {
|
310
|
322
|
eventBus.publishAsync(new UserErrorEvent(ErrorLevel.MEDIUM, null,
|
311
|
|
- "Invalid identity file: " + file.getAbsolutePath() + " ("
|
312
|
|
- + ex.getMessage() + ')', ""));
|
|
323
|
+ "Invalid identity file: " + file + " (" + ex.getMessage() + ')', ""));
|
313
|
324
|
} catch (IOException ex) {
|
314
|
325
|
eventBus.publishAsync(new UserErrorEvent(ErrorLevel.MEDIUM, null,
|
315
|
|
- "I/O error when reading identity file: " + file.getAbsolutePath(), ""));
|
|
326
|
+ "I/O error when reading identity file: " + file, ""));
|
316
|
327
|
}
|
317
|
328
|
}
|
318
|
329
|
|
|
@@ -367,14 +378,15 @@ public class IdentityManager implements IdentityFactory, IdentityController {
|
367
|
378
|
*/
|
368
|
379
|
private void loadConfig() throws InvalidIdentityFileException {
|
369
|
380
|
try {
|
370
|
|
- final File file = new File(configDirectory + "dmdirc.config");
|
|
381
|
+ final Path file = configDirectory.resolve("dmdirc.config");
|
371
|
382
|
|
372
|
|
- if (!file.exists()) {
|
373
|
|
- file.createNewFile();
|
|
383
|
+ if (!Files.exists(file)) {
|
|
384
|
+ Files.createFile(file);
|
374
|
385
|
}
|
375
|
386
|
|
376
|
387
|
config = new ConfigFileBackedConfigProvider(this, file, true);
|
377
|
388
|
config.setOption("identity", "name", "Global config");
|
|
389
|
+ configProvidersByPath.put(file, config);
|
378
|
390
|
addConfigProvider(config);
|
379
|
391
|
} catch (IOException ex) {
|
380
|
392
|
eventBus.publishAsync(new UserErrorEvent(ErrorLevel.MEDIUM, ex,
|
|
@@ -437,6 +449,17 @@ public class IdentityManager implements IdentityFactory, IdentityController {
|
437
|
449
|
|
438
|
450
|
checkArgument(identities.containsValue(group, identity));
|
439
|
451
|
|
|
452
|
+ Path path = null;
|
|
453
|
+ for (Map.Entry<Path, ConfigProvider> entry : configProvidersByPath.entrySet()) {
|
|
454
|
+ if (entry.getValue() == identity) {
|
|
455
|
+ path = entry.getKey();
|
|
456
|
+ }
|
|
457
|
+ }
|
|
458
|
+
|
|
459
|
+ if (path != null) {
|
|
460
|
+ configProvidersByPath.remove(path);
|
|
461
|
+ }
|
|
462
|
+
|
440
|
463
|
synchronized (identities) {
|
441
|
464
|
identities.remove(group, identity);
|
442
|
465
|
}
|
|
@@ -663,11 +686,11 @@ public class IdentityManager implements IdentityFactory, IdentityController {
|
663
|
686
|
|
664
|
687
|
final String name = settings.get(IDENTITY_DOMAIN).get("name").replaceAll(ILLEGAL_CHARS, "_");
|
665
|
688
|
|
666
|
|
- File file = new File(identitiesDirectory + name);
|
|
689
|
+ Path file = identitiesDirectory.resolve(name);
|
667
|
690
|
int attempt = 1;
|
668
|
691
|
|
669
|
|
- while (file.exists()) {
|
670
|
|
- file = new File(identitiesDirectory + name + '-' + attempt);
|
|
692
|
+ while (Files.exists(file)) {
|
|
693
|
+ file = identitiesDirectory.resolve(name + '-' + attempt);
|
671
|
694
|
attempt++;
|
672
|
695
|
}
|
673
|
696
|
|