1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 package org.eclipse.jgit.internal.transport.sshd;
44
45 import static java.text.MessageFormat.format;
46
47 import java.io.IOException;
48 import java.nio.file.Files;
49 import java.nio.file.Path;
50 import java.security.GeneralSecurityException;
51 import java.security.KeyPair;
52 import java.util.ArrayList;
53 import java.util.Collection;
54 import java.util.Collections;
55 import java.util.Iterator;
56 import java.util.List;
57 import java.util.NoSuchElementException;
58 import java.util.concurrent.CancellationException;
59
60 import org.eclipse.jgit.transport.sshd.KeyCache;
61
62
63
64
65
66 public class CachingKeyPairProvider extends EncryptedFileKeyPairProvider
67 implements Iterable<KeyPair> {
68
69 private final KeyCache cache;
70
71
72
73
74
75
76
77
78
79
80
81 public CachingKeyPairProvider(List<Path> paths, KeyCache cache) {
82 super(paths);
83 this.cache = cache;
84 }
85
86 @Override
87 public Iterator<KeyPair> iterator() {
88 Collection<? extends Path> resources = getPaths();
89 if (resources.isEmpty()) {
90 return Collections.emptyListIterator();
91 }
92 return new CancellingKeyPairIterator(resources);
93 }
94
95 @Override
96 public Iterable<KeyPair> loadKeys() {
97 return this;
98 }
99
100 @Override
101 protected KeyPair doLoadKey(Path resource)
102 throws IOException, GeneralSecurityException {
103 if (!Files.exists(resource)) {
104 log.warn(format(SshdText.get().identityFileNotFound, resource));
105 return null;
106 }
107
108
109
110 String resourceId = resource.toString();
111 if (cache == null) {
112 return doLoadKey(resourceId, resource, getPasswordFinder());
113 }
114 Throwable t[] = { null };
115 KeyPair key = cache.get(resource, p -> {
116 try {
117 return doLoadKey(resourceId, p, getPasswordFinder());
118 } catch (IOException | GeneralSecurityException e) {
119 t[0] = e;
120 return null;
121 }
122 });
123 if (t[0] != null) {
124 if (t[0] instanceof CancellationException) {
125 throw (CancellationException) t[0];
126 }
127 throw new IOException(
128 format(SshdText.get().keyLoadFailed, resource), t[0]);
129 }
130 return key;
131 }
132
133 private class CancellingKeyPairIterator implements Iterator<KeyPair> {
134
135 private final Iterator<Path> paths;
136
137 private KeyPair nextItem;
138
139 private boolean nextSet;
140
141 public CancellingKeyPairIterator(Collection<? extends Path> resources) {
142 List<Path> copy = new ArrayList<>(resources.size());
143 copy.addAll(resources);
144 paths = copy.iterator();
145 }
146
147 @Override
148 public boolean hasNext() {
149 if (nextSet) {
150 return nextItem != null;
151 }
152 nextSet = true;
153 while (nextItem == null && paths.hasNext()) {
154 try {
155 nextItem = doLoadKey(paths.next());
156 } catch (CancellationException cancelled) {
157 throw cancelled;
158 } catch (Exception other) {
159 log.warn(other.toString());
160 }
161 }
162 return nextItem != null;
163 }
164
165 @Override
166 public KeyPair next() {
167 if (!nextSet && !hasNext()) {
168 throw new NoSuchElementException();
169 }
170 KeyPair result = nextItem;
171 nextItem = null;
172 nextSet = false;
173 if (result == null) {
174 throw new NoSuchElementException();
175 }
176 return result;
177 }
178
179 }
180 }