1 /**
2 *
3 * Copyright 2003-2004 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
18 package org.apache.geronimo.deployment;
19
20 import java.io.ByteArrayInputStream;
21 import java.io.File;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.OutputStream;
26 import java.net.URI;
27 import java.net.URISyntaxException;
28 import java.net.URL;
29 import java.util.ArrayList;
30 import java.util.Collection;
31 import java.util.HashSet;
32 import java.util.Iterator;
33 import java.util.LinkedHashMap;
34 import java.util.LinkedHashSet;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Set;
38 import java.util.StringTokenizer;
39 import java.util.Collections;
40 import java.util.jar.Attributes;
41 import java.util.jar.JarFile;
42 import java.util.jar.Manifest;
43 import java.util.zip.ZipEntry;
44 import java.util.zip.ZipFile;
45
46 import org.apache.geronimo.common.DeploymentException;
47 import org.apache.geronimo.deployment.util.DeploymentUtil;
48 import org.apache.geronimo.gbean.AbstractName;
49 import org.apache.geronimo.gbean.AbstractNameQuery;
50 import org.apache.geronimo.gbean.GBeanData;
51 import org.apache.geronimo.gbean.GBeanInfo;
52 import org.apache.geronimo.gbean.ReferencePatterns;
53 import org.apache.geronimo.gbean.GReferenceInfo;
54 import org.apache.geronimo.kernel.GBeanAlreadyExistsException;
55 import org.apache.geronimo.kernel.GBeanNotFoundException;
56 import org.apache.geronimo.kernel.Naming;
57 import org.apache.geronimo.kernel.config.Configuration;
58 import org.apache.geronimo.kernel.config.ConfigurationData;
59 import org.apache.geronimo.kernel.config.ConfigurationManager;
60 import org.apache.geronimo.kernel.config.ConfigurationModuleType;
61 import org.apache.geronimo.kernel.config.NoSuchConfigException;
62 import org.apache.geronimo.kernel.repository.Artifact;
63 import org.apache.geronimo.kernel.repository.Environment;
64
65 /**
66 * @version $Rev:385232 $ $Date: 2006-11-06 02:28:54 -0800 (Mon, 06 Nov 2006) $
67 */
68 public class DeploymentContext {
69 private final File baseDir;
70 private final File inPlaceConfigurationDir;
71 private final ResourceContext resourceContext;
72 private final byte[] buffer = new byte[4096];
73 private final Map childConfigurationDatas = new LinkedHashMap();
74 private final ConfigurationManager configurationManager;
75 private final Configuration configuration;
76 private final Naming naming;
77 private final List additionalDeployment = new ArrayList();
78 protected final AbstractName moduleName;
79
80 public DeploymentContext(File baseDir, File inPlaceConfigurationDir, Environment environment, AbstractName moduleName, ConfigurationModuleType moduleType, Naming naming, ConfigurationManager configurationManager, Collection repositories) throws DeploymentException {
81 this(baseDir, inPlaceConfigurationDir, environment, moduleName, moduleType, naming, createConfigurationManager(configurationManager, repositories));
82 }
83
84 public DeploymentContext(File baseDir, File inPlaceConfigurationDir, Environment environment, AbstractName moduleName, ConfigurationModuleType moduleType, Naming naming, ConfigurationManager configurationManager) throws DeploymentException {
85 if (baseDir == null) throw new NullPointerException("baseDir is null");
86 if (environment == null) throw new NullPointerException("environment is null");
87 if (moduleType == null) throw new NullPointerException("type is null");
88 if (configurationManager == null) throw new NullPointerException("configurationManager is null");
89
90 if (!baseDir.exists()) {
91 baseDir.mkdirs();
92 }
93 this.baseDir = baseDir;
94
95 this.inPlaceConfigurationDir = inPlaceConfigurationDir;
96
97 this.moduleName = moduleName;
98
99 this.naming = naming;
100
101 this.configuration = createTempConfiguration(environment, moduleType, baseDir, inPlaceConfigurationDir, configurationManager, naming);
102
103 this.configurationManager = configurationManager;
104
105 if (null == inPlaceConfigurationDir) {
106 resourceContext = new CopyResourceContext(configuration, baseDir);
107 } else {
108 resourceContext = new InPlaceResourceContext(configuration, inPlaceConfigurationDir);
109 }
110 }
111
112 private static ConfigurationManager createConfigurationManager(ConfigurationManager configurationManager, Collection repositories) {
113 return new DeploymentConfigurationManager(configurationManager, repositories);
114 }
115
116 private static Configuration createTempConfiguration(Environment environment, ConfigurationModuleType moduleType, File baseDir, File inPlaceConfigurationDir, ConfigurationManager configurationManager, Naming naming) throws DeploymentException {
117 try {
118 configurationManager.loadConfiguration(new ConfigurationData(moduleType, null, null, null, environment, baseDir, inPlaceConfigurationDir, naming));
119 return configurationManager.getConfiguration(environment.getConfigId());
120 } catch (Exception e) {
121 throw new DeploymentException("Unable to create configuration for deployment", e);
122 }
123 }
124
125 public ConfigurationManager getConfigurationManager() {
126 return configurationManager;
127 }
128
129 public Artifact getConfigID() {
130 return configuration.getId();
131 }
132
133 public File getBaseDir() {
134 return baseDir;
135 }
136
137 public File getInPlaceConfigurationDir() {
138 return inPlaceConfigurationDir;
139 }
140
141 public Naming getNaming() {
142 return naming;
143 }
144
145 public GBeanData addGBean(String name, GBeanInfo gbeanInfo) throws GBeanAlreadyExistsException {
146 if (name == null) throw new NullPointerException("name is null");
147 if (gbeanInfo == null) throw new NullPointerException("gbean is null");
148 GBeanData gbean = new GBeanData(gbeanInfo);
149 configuration.addGBean(name, gbean);
150 return gbean;
151 }
152
153 public void addGBean(GBeanData gbean) throws GBeanAlreadyExistsException {
154 if (gbean == null) throw new NullPointerException("gbean is null");
155 if (gbean.getAbstractName() == null) throw new NullPointerException("gbean.getAbstractName() is null");
156 configuration.addGBean(gbean);
157 }
158
159 public void removeGBean(AbstractName name) throws GBeanNotFoundException {
160 if (name == null) throw new NullPointerException("name is null");
161 configuration.removeGBean(name);
162 }
163
164 public Set getGBeanNames() {
165 return new HashSet(configuration.getGBeans().keySet());
166 }
167
168 /**
169 * @deprecated use findGBeans(pattern)
170 */
171 public Set listGBeans(AbstractNameQuery pattern) {
172 return findGBeans(pattern);
173 }
174
175 public AbstractName findGBean(AbstractNameQuery pattern) throws GBeanNotFoundException {
176 return configuration.findGBean(pattern);
177 }
178
179 public AbstractName findGBean(Set patterns) throws GBeanNotFoundException {
180 return configuration.findGBean(patterns);
181 }
182
183 public LinkedHashSet findGBeans(AbstractNameQuery pattern) {
184 return configuration.findGBeans(pattern);
185 }
186
187 public LinkedHashSet findGBeans(Set patterns) {
188 return configuration.findGBeans(patterns);
189 }
190
191 public GBeanData getGBeanInstance(AbstractName name) throws GBeanNotFoundException {
192 Map gbeans = configuration.getGBeans();
193 GBeanData gbeanData = (GBeanData) gbeans.get(name);
194 if (gbeanData == null) {
195 throw new GBeanNotFoundException(name);
196 }
197 return gbeanData;
198 }
199
200 /**
201 * Add a packed jar file into the deployment context and place it into the
202 * path specified in the target path. The newly added packed jar is added
203 * to the classpath of the configuration.
204 *
205 * @param targetPath where the packed jar file should be placed
206 * @param jarFile the jar file to copy
207 * @throws IOException if there's a problem copying the jar file
208 */
209 public void addIncludeAsPackedJar(URI targetPath, JarFile jarFile) throws IOException {
210 resourceContext.addIncludeAsPackedJar(targetPath, jarFile);
211 }
212
213 /**
214 * Add a ZIP file entry into the deployment context and place it into the
215 * path specified in the target path. The newly added entry is added
216 * to the classpath of the configuration.
217 *
218 * @param targetPath where the ZIP file entry should be placed
219 * @param zipFile the ZIP file
220 * @param zipEntry the ZIP file entry
221 * @throws IOException if there's a problem copying the ZIP entry
222 */
223 public void addInclude(URI targetPath, ZipFile zipFile, ZipEntry zipEntry) throws IOException {
224 resourceContext.addInclude(targetPath, zipFile, zipEntry);
225 }
226
227 /**
228 * Add a file into the deployment context and place it into the
229 * path specified in the target path. The newly added file is added
230 * to the classpath of the configuration.
231 *
232 * @param targetPath where the file should be placed
233 * @param source the URL of file to be copied
234 * @throws IOException if there's a problem copying the ZIP entry
235 */
236 public void addInclude(URI targetPath, URL source) throws IOException {
237 resourceContext.addInclude(targetPath, source);
238 }
239
240 /**
241 * Add a file into the deployment context and place it into the
242 * path specified in the target path. The newly added file is added
243 * to the classpath of the configuration.
244 *
245 * @param targetPath where the file should be placed
246 * @param source the file to be copied
247 * @throws IOException if there's a problem copying the ZIP entry
248 */
249 public void addInclude(URI targetPath, File source) throws IOException {
250 resourceContext.addInclude(targetPath, source);
251 }
252
253 /**
254 * Import the classpath from a jar file's manifest. The imported classpath
255 * is crafted relative to <code>moduleBaseUri</code>.
256 *
257 * @param moduleFile the jar file from which the manifest is obtained.
258 * @param moduleBaseUri the base for the imported classpath
259 * @throws DeploymentException if there is a problem with the classpath in
260 * the manifest
261 */
262 public void addManifestClassPath(JarFile moduleFile, URI moduleBaseUri) throws DeploymentException {
263 Manifest manifest;
264 try {
265 manifest = moduleFile.getManifest();
266 } catch (IOException e) {
267 throw new DeploymentException("Could not read manifest: " + moduleBaseUri);
268 }
269
270 if (manifest == null) {
271 return;
272 }
273 String manifestClassPath = manifest.getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
274 if (manifestClassPath == null) {
275 return;
276 }
277
278 for (StringTokenizer tokenizer = new StringTokenizer(manifestClassPath, " "); tokenizer.hasMoreTokens();) {
279 String path = tokenizer.nextToken();
280
281 URI pathUri;
282 try {
283 pathUri = new URI(path);
284 } catch (URISyntaxException e) {
285 throw new DeploymentException("Invalid manifest classpath entry: module=" + moduleBaseUri + ", path=" + path);
286 }
287
288 if (!pathUri.getPath().endsWith(".jar")) {
289 throw new DeploymentException("Manifest class path entries must end with the .jar extension (J2EE 1.4 Section 8.2): module=" + moduleBaseUri);
290 }
291 if (pathUri.isAbsolute()) {
292 throw new DeploymentException("Manifest class path entries must be relative (J2EE 1.4 Section 8.2): moduel=" + moduleBaseUri);
293 }
294
295 try {
296 URI targetUri = moduleBaseUri.resolve(pathUri);
297 if (targetUri.getPath().endsWith("/")) throw new IllegalStateException("target path must not end with a '/' character: " + targetUri);
298 configuration.addToClassPath(targetUri.toString());
299 } catch (IOException e) {
300 throw new DeploymentException(e);
301 }
302 }
303 }
304
305 public void addClass(URI targetPath, String fqcn, byte[] bytes) throws IOException, URISyntaxException {
306 if (!targetPath.getPath().endsWith("/")) throw new IllegalStateException("target path must end with a '/' character: " + targetPath);
307
308 String classFileName = fqcn.replace('.', '/') + ".class";
309
310 File targetFile = getTargetFile(new URI(targetPath.toString() + classFileName));
311 addFile(targetFile, new ByteArrayInputStream(bytes));
312
313 configuration.addToClassPath(targetPath.toString());
314 }
315
316 public void addFile(URI targetPath, ZipFile zipFile, ZipEntry zipEntry) throws IOException {
317 resourceContext.addFile(targetPath, zipFile, zipEntry);
318 }
319
320 public void addFile(URI targetPath, URL source) throws IOException {
321 resourceContext.addFile(targetPath, source);
322 }
323
324 public void addFile(URI targetPath, File source) throws IOException {
325 resourceContext.addFile(targetPath, source);
326 }
327
328 public void addFile(URI targetPath, String source) throws IOException {
329 resourceContext.addFile(targetPath, source);
330 }
331
332 private void addFile(File targetFile, InputStream source) throws IOException {
333 targetFile.getParentFile().mkdirs();
334 OutputStream out = null;
335 try {
336 out = new FileOutputStream(targetFile);
337 int count;
338 while ((count = source.read(buffer)) > 0) {
339 out.write(buffer, 0, count);
340 }
341 } finally {
342 DeploymentUtil.close(out);
343 }
344 }
345
346 public File getTargetFile(URI targetPath) {
347 return resourceContext.getTargetFile(targetPath);
348 }
349
350 public ClassLoader getClassLoader() throws DeploymentException {
351 return configuration.getConfigurationClassLoader();
352 }
353
354 public Configuration getConfiguration() {
355 return configuration;
356 }
357
358 public void flush() throws IOException{
359 resourceContext.flush();
360 }
361
362 public void close() throws IOException, DeploymentException {
363 if (configurationManager != null) {
364 try {
365 configurationManager.unloadConfiguration(configuration.getId());
366 } catch (NoSuchConfigException ignored) {
367
368 }
369 }
370 }
371
372 public void addChildConfiguration(String moduleName, ConfigurationData configurationData) {
373 childConfigurationDatas.put(moduleName, configurationData);
374 }
375
376 public ConfigurationData getConfigurationData() throws DeploymentException {
377 List failures = verify();
378 if (!failures.isEmpty()) {
379 StringBuffer message = new StringBuffer();
380 for (Iterator iterator = failures.iterator(); iterator.hasNext();) {
381 String failure = (String) iterator.next();
382 if (message.length() > 0) message.append("\n");
383 message.append(failure);
384 }
385 throw new DeploymentException(message.toString());
386 }
387
388 ArrayList gbeans = new ArrayList(configuration.getGBeans().values());
389 Collections.sort(gbeans, new GBeanData.PriorityComparator());
390 ConfigurationData configurationData = new ConfigurationData(configuration.getModuleType(),
391 new LinkedHashSet(configuration.getClassPath()),
392 gbeans,
393 childConfigurationDatas,
394 configuration.getEnvironment(),
395 baseDir,
396 inPlaceConfigurationDir,
397 naming);
398
399 for (Iterator iterator = additionalDeployment.iterator(); iterator.hasNext();) {
400 ConfigurationData ownedConfiguration = (ConfigurationData) iterator.next();
401 configurationData.addOwnedConfigurations(ownedConfiguration.getId());
402 }
403
404 return configurationData;
405 }
406
407 public void addAdditionalDeployment(ConfigurationData configurationData) {
408 additionalDeployment.add(configurationData);
409 }
410
411 public List getAdditionalDeployment() {
412 return additionalDeployment;
413 }
414
415 public AbstractName getModuleName() {
416 return moduleName;
417 }
418
419 public List verify() throws DeploymentException {
420 List failures = new ArrayList();
421 for (Iterator iterator = configuration.getGBeans().entrySet().iterator(); iterator.hasNext();) {
422 Map.Entry entry = (Map.Entry) iterator.next();
423 AbstractName name = (AbstractName) entry.getKey();
424 GBeanData gbean = (GBeanData) entry.getValue();
425
426 for (Iterator iterator1 = gbean.getReferences().entrySet().iterator(); iterator1.hasNext();) {
427 Map.Entry referenceEntry = (Map.Entry) iterator1.next();
428 String referenceName = (String) referenceEntry.getKey();
429 ReferencePatterns referencePatterns = (ReferencePatterns) referenceEntry.getValue();
430
431 String failure = verifyReference(gbean, referenceName, referencePatterns);
432 if (failure != null) {
433 failures.add(failure);
434 }
435 }
436
437 for (Iterator iterator1 = gbean.getDependencies().iterator(); iterator1.hasNext();) {
438 ReferencePatterns referencePatterns = (ReferencePatterns) iterator1.next();
439 String failure = verifyDependency(name, referencePatterns);
440 if (failure != null) {
441 failures.add(failure);
442 }
443 }
444 }
445 return failures;
446 }
447
448 private String verifyReference(GBeanData gbean, String referenceName, ReferencePatterns referencePatterns) {
449 GReferenceInfo referenceInfo = gbean.getGBeanInfo().getReference(referenceName);
450
451
452 if (referenceInfo == null) return null;
453
454
455 if (referenceInfo.getProxyType().equals(Collection.class.getName())) return null;
456
457 if (!isVerifyReference(referencePatterns)) {
458 return "Unable to resolve reference \"" + referenceName + "\" in gbean " +
459 gbean.getAbstractName() + " to a gbean matching the pattern " + referencePatterns.getPatterns();
460 }
461 return null;
462 }
463
464 private String verifyDependency(AbstractName name, ReferencePatterns referencePatterns) {
465 if (!isVerifyReference(referencePatterns)) {
466 return "Unable to resolve dependency in gbean " + name +
467 " to a gbean matching the pattern " + referencePatterns.getPatterns();
468 }
469
470 return null;
471 }
472
473 private boolean isVerifyReference(ReferencePatterns referencePatterns) {
474
475
476 if (referencePatterns.isResolved()) return true;
477
478
479
480 Set patterns = referencePatterns.getPatterns();
481 for (Iterator iterator = patterns.iterator(); iterator.hasNext();) {
482 AbstractNameQuery query = (AbstractNameQuery) iterator.next();
483 if (query.getArtifact() != null) return true;
484 }
485
486
487 try {
488 findGBean(patterns);
489 return true;
490 } catch (GBeanNotFoundException e) {
491 return false;
492 }
493 }
494 }