1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package edu.internet2.middleware.shibboleth.common.resource;
18
19 import java.io.File;
20 import java.io.FileInputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23
24 import org.joda.time.DateTime;
25 import org.opensaml.util.resource.AbstractFilteredResource;
26 import org.opensaml.util.resource.ResourceException;
27 import org.opensaml.xml.util.DatatypeHelper;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30 import org.tmatesoft.svn.core.SVNException;
31 import org.tmatesoft.svn.core.SVNURL;
32 import org.tmatesoft.svn.core.auth.BasicAuthenticationManager;
33 import org.tmatesoft.svn.core.wc.SVNClientManager;
34 import org.tmatesoft.svn.core.wc.SVNRevision;
35 import org.tmatesoft.svn.core.wc.SVNStatus;
36 import org.tmatesoft.svn.core.wc.SVNStatusClient;
37 import org.tmatesoft.svn.core.wc.SVNStatusType;
38 import org.tmatesoft.svn.core.wc.SVNUpdateClient;
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 public class SVNResource extends AbstractFilteredResource {
57
58
59 private final Logger log = LoggerFactory.getLogger(SVNResource.class);
60
61
62 private final SVNClientManager svnClientMgr;
63
64
65 private String remoteResource;
66
67
68 private File localResource;
69
70
71 private SVNRevision revision;
72
73
74
75
76
77
78
79
80 public SVNResource(String remote, String local, long revision) {
81 svnClientMgr = SVNClientManager.newInstance();
82
83 remoteResource = DatatypeHelper.safeTrimOrNullString(remote);
84 if (this.remoteResource == null) {
85 throw new IllegalArgumentException("Remote repository location may not be null or empty");
86 }
87
88 if (DatatypeHelper.isEmpty(local)) {
89 throw new IllegalArgumentException("Local repository location may not be null or empty");
90 }
91 localResource = new File(local);
92
93 if (revision < 0) {
94 this.revision = SVNRevision.HEAD;
95 } else {
96 this.revision = SVNRevision.create(revision);
97 }
98 }
99
100
101
102
103
104
105
106
107
108
109 public SVNResource(String remote, String local, long revision, String username, String password) {
110 svnClientMgr = SVNClientManager.newInstance();
111
112 if (username != null) {
113 if (password != null) {
114 BasicAuthenticationManager authnMgr = new BasicAuthenticationManager(username, password);
115 svnClientMgr.setAuthenticationManager(authnMgr);
116 } else {
117 throw new IllegalArgumentException(
118 "Unable to initialize subversion resource. User name was given but no password was provided.");
119 }
120 }
121
122 remoteResource = DatatypeHelper.safeTrimOrNullString(remote);
123 if (this.remoteResource == null) {
124 throw new IllegalArgumentException("Remote resource location may not be null or empty");
125 }
126
127 if (DatatypeHelper.isEmpty(local)) {
128 throw new IllegalArgumentException("Local repository location may not be null or empty");
129 }
130 localResource = new File(local);
131
132 if (revision < 0) {
133 this.revision = SVNRevision.HEAD;
134 } else {
135 this.revision = SVNRevision.create(revision);
136 }
137 }
138
139
140 public boolean exists() throws ResourceException {
141 checkoutOrUpdateResource();
142 return workingCopyExists();
143 }
144
145
146 public InputStream getInputStream() throws ResourceException {
147 checkoutOrUpdateResource();
148
149 try {
150 return new FileInputStream(localResource);
151 } catch (IOException e) {
152 log.error("Unable read local working copy {}", localResource.getAbsolutePath());
153 throw new ResourceException("Unable to read local working copy of configuration file "
154 + localResource.getAbsolutePath());
155 }
156 }
157
158
159 public DateTime getLastModifiedTime() throws ResourceException {
160 SVNStatus status = getResourceStatus();
161 if (status.getContentsStatus() == SVNStatusType.STATUS_NORMAL) {
162 return new DateTime(status.getWorkingContentsDate());
163 } else {
164 throw new ResourceException("SVN resource is in a state which prevents determining last modified date: "
165 + status.getContentsStatus().toString());
166 }
167 }
168
169
170 public String getLocation() {
171 try {
172 return SVNURL.parseURIDecoded(remoteResource).toDecodedString();
173 } catch (SVNException e) {
174 log.error("Unable to represent remote repository URL as a string");
175 return null;
176 }
177 }
178
179
180
181
182
183
184 protected boolean workingCopyExists() {
185 return localResource.exists();
186 }
187
188
189
190
191
192
193
194
195
196 protected void checkoutOrUpdateResource() throws ResourceException {
197 SVNStatus status = getResourceStatus();
198
199 if (!workingCopyExists()) {
200 checkoutResource();
201 return;
202 }
203
204 if (revision == SVNRevision.HEAD) {
205 if (!revision.equals(status.getRemoteRevision())) {
206 updateResource();
207 }
208 } else {
209 if (!revision.equals(status.getRevision())) {
210 updateResource();
211 }
212 }
213 }
214
215
216
217
218
219
220
221
222 protected SVNStatus getResourceStatus() throws ResourceException {
223 SVNStatusClient client = svnClientMgr.getStatusClient();
224
225 try {
226 return client.doStatus(localResource, true);
227 } catch (SVNException e) {
228 log.error("Unable to determine current status of SVN resource {}", new Object[] { localResource
229 .getAbsolutePath(), }, e);
230 throw new ResourceException("Unable to determine current status of SVN resource");
231 }
232 }
233
234
235
236
237
238
239 protected void checkoutResource() throws ResourceException {
240 SVNUpdateClient client = svnClientMgr.getUpdateClient();
241 client.setIgnoreExternals(false);
242
243 try {
244 SVNURL remoteResourceLocation = SVNURL.parseURIDecoded(remoteResource);
245 client.doCheckout(remoteResourceLocation, localResource, revision, revision, false);
246 } catch (SVNException e) {
247 log.error("Unable to check out resource {}", new Object[] { remoteResource }, e);
248 throw new ResourceException("Unable to check out resource from repository");
249 }
250 }
251
252
253
254
255
256
257 protected void updateResource() throws ResourceException {
258 SVNUpdateClient client = svnClientMgr.getUpdateClient();
259 client.setIgnoreExternals(false);
260
261 try {
262 client.doUpdate(localResource, revision, false);
263 } catch (SVNException e) {
264 log.error("Unable to update working copy of resource {} to revision {}", new Object[] { remoteResource,
265 revision.getNumber(), }, e);
266 throw new ResourceException("Unable to update working copy of resource");
267 }
268 }
269 }