1 /**
2 *
3 * Copyright 2005 The Apache Software Foundation
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.geronimo.system.configuration;
18
19 import java.io.File;
20 import java.io.FileInputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.OutputStream;
24 import java.net.MalformedURLException;
25 import java.util.ArrayList;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.SortedSet;
29 import java.util.Set;
30 import java.util.jar.JarFile;
31 import java.util.zip.ZipEntry;
32 import java.util.zip.ZipOutputStream;
33 import javax.management.ObjectName;
34
35 import org.apache.geronimo.gbean.AbstractName;
36 import org.apache.geronimo.gbean.GBeanInfo;
37 import org.apache.geronimo.gbean.GBeanInfoBuilder;
38 import org.apache.geronimo.kernel.Kernel;
39 import org.apache.geronimo.kernel.ObjectNameUtil;
40 import org.apache.geronimo.kernel.config.ConfigurationAlreadyExistsException;
41 import org.apache.geronimo.kernel.config.ConfigurationData;
42 import org.apache.geronimo.kernel.config.ConfigurationInfo;
43 import org.apache.geronimo.kernel.config.ConfigurationStore;
44 import org.apache.geronimo.kernel.config.ConfigurationUtil;
45 import org.apache.geronimo.kernel.config.InvalidConfigException;
46 import org.apache.geronimo.kernel.config.NoSuchConfigException;
47 import org.apache.geronimo.kernel.config.IOUtil;
48 import org.apache.geronimo.kernel.repository.Artifact;
49 import org.apache.geronimo.kernel.repository.FileWriteMonitor;
50 import org.apache.geronimo.kernel.repository.WritableListableRepository;
51 import org.apache.commons.logging.Log;
52 import org.apache.commons.logging.LogFactory;
53
54 /**
55 * Implementation of ConfigurationStore GBean that installs/loads Configurations from a
56 * repository.
57 *
58 * @version $Rev: 410741 $ $Date: 2006-05-31 21:35:48 -0700 (Wed, 31 May 2006) $
59 */
60 public class RepositoryConfigurationStore implements ConfigurationStore {
61 private final static Log log = LogFactory.getLog(RepositoryConfigurationStore.class);
62 private final Kernel kernel;
63 private final ObjectName objectName;
64 protected final WritableListableRepository repository;
65 private final InPlaceConfigurationUtil inPlaceConfUtil;
66
67 public RepositoryConfigurationStore(WritableListableRepository repository) {
68 this(null, null, repository);
69 }
70
71 public RepositoryConfigurationStore(Kernel kernel, String objectName, WritableListableRepository repository) {
72 this.kernel = kernel;
73 this.objectName = objectName == null ? null : ObjectNameUtil.getObjectName(objectName);
74 this.repository = repository;
75
76 inPlaceConfUtil = new InPlaceConfigurationUtil();
77 }
78
79 public String getObjectName() {
80 return objectName.getCanonicalName();
81 }
82
83 public AbstractName getAbstractName() {
84 return kernel == null? null:kernel.getAbstractNameFor(this);
85 }
86
87 public ConfigurationData loadConfiguration(Artifact configId) throws NoSuchConfigException, IOException, InvalidConfigException {
88 if(!configId.isResolved()) {
89 throw new IllegalArgumentException("Artifact "+configId+" is not fully resolved");
90 }
91 File location = repository.getLocation(configId);
92
93 if (existsReadable(location)) {
94 throw new NoSuchConfigException(configId);
95 }
96
97 ConfigurationData configurationData;
98 try {
99 if (location.isDirectory()) {
100 File serFile = new File(location, "META-INF");
101 serFile = new File(serFile, "config.ser");
102
103 if (!serFile.exists()) {
104 throw new InvalidConfigException("Configuration does not contain a META-INF/config.ser file: " + serFile);
105 } else if (!serFile.canRead()) {
106 throw new InvalidConfigException("Can not read configuration META-INF/config.ser file: " + serFile);
107 }
108
109 ConfigurationStoreUtil.verifyChecksum(serFile);
110
111 InputStream in = new FileInputStream(serFile);
112 try {
113 configurationData = ConfigurationUtil.readConfigurationData(in);
114 } finally {
115 IOUtil.close(in);
116 }
117 } else {
118 JarFile jarFile = new JarFile(location);
119 InputStream in = null;
120 try {
121 ZipEntry entry = jarFile.getEntry("META-INF/config.ser");
122 in = jarFile.getInputStream(entry);
123 configurationData = ConfigurationUtil.readConfigurationData(in);
124 } finally {
125 IOUtil.close(in);
126 IOUtil.close(jarFile);
127 }
128 }
129 } catch (ClassNotFoundException e) {
130 throw new InvalidConfigException("Unable to load class from config: " + configId, e);
131 }
132
133 configurationData.setConfigurationDir(location);
134 configurationData.setConfigurationStore(this);
135 if (kernel != null) {
136 configurationData.setNaming(kernel.getNaming());
137 }
138
139 return configurationData;
140 }
141
142 private boolean existsReadable(File location) {
143 return !location.exists() || !location.canRead();
144 }
145
146 public boolean containsConfiguration(Artifact configId) {
147 if(!configId.isResolved()) {
148 throw new IllegalArgumentException("Artifact "+configId+" is not fully resolved");
149 }
150 File location = repository.getLocation(configId);
151 if (location.isDirectory()) {
152 location = new File(location, "META-INF");
153 location = new File(location, "config.ser");
154 return location.isFile() && location.canRead();
155 } else {
156 JarFile jarFile = null;
157 try {
158 jarFile = new JarFile(location);
159 ZipEntry entry = jarFile.getEntry("META-INF/config.ser");
160 return entry != null && !entry.isDirectory();
161 } catch (IOException e) {
162 return false;
163 } finally {
164 IOUtil.close(jarFile);
165 }
166 }
167 }
168
169 public File createNewConfigurationDir(Artifact configId) throws ConfigurationAlreadyExistsException {
170 if(!configId.isResolved()) {
171 throw new IllegalArgumentException("Artifact "+configId+" is not fully resolved");
172 }
173 File location = repository.getLocation(configId);
174 if (location.exists()) {
175 throw new ConfigurationAlreadyExistsException("Configuration already exists: " + configId);
176 }
177 location.mkdirs();
178 if (!location.exists()) {
179 throw new ConfigurationAlreadyExistsException("Could not create configuration directory: " + location);
180 }
181 return location;
182 }
183
184 public Set resolve(Artifact configId, String moduleName, String path) throws NoSuchConfigException, MalformedURLException {
185 if(!configId.isResolved()) {
186 throw new IllegalArgumentException("Artifact "+configId+" is not fully resolved");
187 }
188 File location = repository.getLocation(configId);
189 if (location.isDirectory()) {
190 File inPlaceLocation = null;
191 try {
192 inPlaceLocation = inPlaceConfUtil.readInPlaceLocation(location);
193 } catch (IOException e) {
194 }
195 if (null != inPlaceLocation) {
196 location = inPlaceLocation;
197 }
198
199 if (moduleName != null) {
200 location = new File(location, moduleName);
201 }
202
203 if (location.isDirectory()) {
204 Set matches = IOUtil.search(location, path);
205 return matches;
206 } else {
207 Set matches = IOUtil.search(location, path);
208 return matches;
209 }
210 } else {
211 Set matches = IOUtil.search(location, moduleName + "/" +path);
212 return matches;
213 }
214 }
215
216 public void exportConfiguration(Artifact configId, OutputStream output) throws IOException, NoSuchConfigException {
217 if(!configId.isResolved()) {
218 throw new IllegalArgumentException("Artifact "+configId+" is not fully resolved");
219 }
220 File dir = repository.getLocation(configId);
221 if (dir == null) {
222 throw new NoSuchConfigException(configId);
223 }
224 if (existsReadable(dir)) {
225 throw new IOException("Cannot read config store directory for " + configId + " (" + dir.getAbsolutePath() + ")");
226 }
227 ZipOutputStream out = new ZipOutputStream(output);
228 byte[] buf = new byte[10240];
229 writeToZip(dir, out, "", buf);
230 if (inPlaceConfUtil.isInPlaceConfiguration(dir)) {
231 dir = inPlaceConfUtil.readInPlaceLocation(dir);
232 writeToZip(dir, out, "", buf);
233 }
234 out.closeEntry();
235 out.finish();
236 out.flush();
237 }
238
239 private void writeToZip(File dir, ZipOutputStream out, String prefix, byte[] buf) throws IOException {
240 File[] all = dir.listFiles();
241 for (int i = 0; i < all.length; i++) {
242 File file = all[i];
243 if (file.isDirectory()) {
244 writeToZip(file, out, prefix + file.getName() + "/", buf);
245 } else {
246 ZipEntry entry = new ZipEntry(prefix + file.getName());
247 out.putNextEntry(entry);
248 writeToZipStream(file, out, buf);
249 }
250 }
251 }
252
253 private void writeToZipStream(File file, OutputStream out, byte[] buf) throws IOException {
254 FileInputStream in = new FileInputStream(file);
255 int count;
256 try {
257 while ((count = in.read(buf, 0, buf.length)) > -1) {
258 out.write(buf, 0, count);
259 }
260 } finally {
261 in.close();
262 }
263 }
264
265 public void install(InputStream in, int size, Artifact configId, FileWriteMonitor fileWriteMonitor) throws IOException {
266 try {
267 repository.copyToRepository(in, size, configId, fileWriteMonitor);
268 } catch (IOException e) {
269 throw e;
270 } finally {
271 IOUtil.close(in);
272 }
273 }
274
275 public boolean isInPlaceConfiguration(Artifact configId) throws NoSuchConfigException, IOException {
276 if(!configId.isResolved()) {
277 throw new IllegalArgumentException("Artifact "+configId+" is not fully resolved");
278 }
279 File location = repository.getLocation(configId);
280 if (location.isDirectory()) {
281 return inPlaceConfUtil.isInPlaceConfiguration(location);
282 } else {
283 return false;
284 }
285 }
286
287 public void install(ConfigurationData configurationData) throws IOException, InvalidConfigException {
288
289 File source = configurationData.getConfigurationDir();
290 if (!source.exists()) {
291 throw new InvalidConfigException("Source does not exist " + source);
292 } else if (!source.canRead()) {
293 throw new InvalidConfigException("Source is not readable " + source);
294 }
295
296
297 Artifact configId = configurationData.getId();
298 File destination = repository.getLocation(configId);
299
300
301 if (!source.equals(destination)) {
302 if (destination.exists()) {
303 throw new ConfigurationAlreadyExistsException(configId.toString());
304 }
305
306 if (source.isFile()) {
307
308
309 repository.copyToRepository(source, configId, null);
310 } else if (source.isDirectory()) {
311
312 IOUtil.recursiveCopy(source, destination);
313 } else {
314 throw new InvalidConfigException("Unable to install configuration from " + source);
315 }
316 }
317
318 ExecutableConfigurationUtil.writeConfiguration(configurationData, destination);
319
320
321 inPlaceConfUtil.writeInPlaceLocation(configurationData, destination);
322 }
323
324 public void uninstall(Artifact configId) throws NoSuchConfigException, IOException {
325 if(!configId.isResolved()) {
326 throw new IllegalArgumentException("Artifact "+configId+" is not fully resolved");
327 }
328 ConfigurationInfo configurationInfo = null;
329 try {
330 configurationInfo = loadConfigurationInfo(configId);
331 } catch (IOException e) {
332
333 }
334 File location = repository.getLocation(configId);
335 IOUtil.recursiveDelete(location);
336
337
338 if (configurationInfo != null) {
339 IOException ioException = null;
340 for (Iterator iterator = configurationInfo.getOwnedConfigurations().iterator(); iterator.hasNext();) {
341 Artifact ownedConfiguration = (Artifact) iterator.next();
342 try {
343 uninstall(ownedConfiguration);
344 } catch (NoSuchConfigException e) {
345
346 } catch (IOException e) {
347 if (ioException != null) {
348 ioException = e;
349 }
350 }
351 if (ioException != null) {
352 throw ioException;
353 }
354 }
355 }
356 }
357
358 public List listConfigurations() {
359 SortedSet artifacts = repository.list();
360
361 List configs;
362 synchronized (this) {
363 configs = new ArrayList();
364 for (Iterator i = artifacts.iterator(); i.hasNext();) {
365 Artifact configId = (Artifact) i.next();
366 File dir = repository.getLocation(configId);
367 File meta = new File(dir, "META-INF");
368 if(!meta.isDirectory() || !meta.canRead()) {
369 continue;
370 }
371 File ser = new File(meta, "config.ser");
372 if(!ser.isFile() || !ser.canRead() || ser.length() == 0) {
373 continue;
374 }
375 try {
376 ConfigurationInfo configurationInfo = loadConfigurationInfo(configId);
377 configs.add(configurationInfo);
378 } catch (NoSuchConfigException e) {
379 log.error("Unexpected error: found META-INF/config.ser for "+configId+" but couldn't load ConfigurationInfo", e);
380 } catch (IOException e) {
381 log.error("Unable to load ConfigurationInfo for "+configId, e);
382 }
383 }
384 }
385 return configs;
386 }
387
388 private ConfigurationInfo loadConfigurationInfo(Artifact configId) throws NoSuchConfigException, IOException {
389 File location = repository.getLocation(configId);
390
391 if (!location.exists() && !location.canRead()) {
392 throw new NoSuchConfigException(configId);
393 }
394
395 File inPlaceLocation = inPlaceConfUtil.readInPlaceLocation(location);
396
397 ConfigurationInfo configurationInfo;
398 if (location.isDirectory()) {
399 File infoFile = new File(location, "META-INF");
400 infoFile = new File(infoFile, "config.info");
401
402 InputStream in = new FileInputStream(infoFile);
403 try {
404 configurationInfo = ConfigurationUtil.readConfigurationInfo(in, getAbstractName(), inPlaceLocation);
405 } finally {
406 IOUtil.close(in);
407 }
408 } else {
409 JarFile jarFile = new JarFile(location);
410 InputStream in = null;
411 try {
412 ZipEntry entry = jarFile.getEntry("META-INF/config.info");
413 in = jarFile.getInputStream(entry);
414 configurationInfo = ConfigurationUtil.readConfigurationInfo(in, getAbstractName(), inPlaceLocation);
415 } finally {
416 IOUtil.close(in);
417 IOUtil.close(jarFile);
418 }
419 }
420
421 return configurationInfo;
422 }
423
424 /**
425 // * Thread to cleanup unused Config Store entries.
426 // * On Windows, open files can't be deleted. Until MultiParentClassLoaders
427 // * are GC'ed, we won't be able to delete Config Store directories/files.
428 // */
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453 /**
454 // * For every directory in the pendingDeletionIndex, attempt to delete all
455 // * sub-directories and files.
456 // */
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493 public static final GBeanInfo GBEAN_INFO;
494
495 public static GBeanInfo getGBeanInfo() {
496 return GBEAN_INFO;
497 }
498
499 static {
500 GBeanInfoBuilder builder = GBeanInfoBuilder.createStatic(RepositoryConfigurationStore.class, "ConfigurationStore");
501 builder.addAttribute("kernel", Kernel.class, false);
502 builder.addAttribute("objectName", String.class, false);
503 builder.addReference("Repository", WritableListableRepository.class, "Repository");
504 builder.setConstructor(new String[]{"kernel", "objectName", "Repository"});
505 GBEAN_INFO = builder.getBeanInfo();
506 }
507 }