001// Licensed under the Apache License, Version 2.0 (the "License"); 002// you may not use this file except in compliance with the License. 003// You may obtain a copy of the License at 004// 005// http://www.apache.org/licenses/LICENSE-2.0 006// 007// Unless required by applicable law or agreed to in writing, software 008// distributed under the License is distributed on an "AS IS" BASIS, 009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 010// See the License for the specific language governing permissions and 011// limitations under the License. 012 013package org.apache.tapestry5.ioc; 014 015import java.io.Closeable; 016import java.io.IOException; 017import java.io.InputStream; 018import java.net.URL; 019import java.util.Collections; 020import java.util.Enumeration; 021import java.util.HashSet; 022import java.util.Set; 023import java.util.jar.Manifest; 024 025import org.apache.tapestry5.commons.util.ExceptionUtils; 026 027import static org.apache.tapestry5.ioc.IOCConstants.MODULE_BUILDER_MANIFEST_ENTRY_NAME; 028 029/** 030 * A collection of utility methods for a couple of different areas, including creating the initial {@link 031 * org.apache.tapestry5.ioc.Registry}. 032 */ 033public final class IOCUtilities 034{ 035 private IOCUtilities() 036 { 037 } 038 039 /** 040 * Construct a default Registry, including modules identifed via the Tapestry-Module-Classes Manifest entry. The 041 * registry will have been {@linkplain Registry#performRegistryStartup() started up} before it is returned. 042 * 043 * @return constructed Registry, after startup 044 * @see #addDefaultModules(RegistryBuilder) 045 */ 046 public static Registry buildDefaultRegistry() 047 { 048 RegistryBuilder builder = new RegistryBuilder(); 049 050 addDefaultModules(builder); 051 052 Registry registry = builder.build(); 053 054 registry.performRegistryStartup(); 055 056 return registry; 057 } 058 059 /** 060 * Scans the classpath for JAR Manifests that contain the Tapestry-Module-Classes attribute and adds each 061 * corresponding class to the RegistryBuilder. In addition, looks for a system property named "tapestry.modules" and 062 * adds all of those modules as well. The tapestry.modules approach is intended for development. 063 * To prevent auto-loading of Manifest-defined modules the system property named "tapestry.manifest-modules-blacklist" 064 * can be used. 065 * 066 * @param builder 067 * the builder to which modules will be added 068 * @see org.apache.tapestry5.ioc.annotations.ImportModule 069 * @see RegistryBuilder#add(String) 070 */ 071 public static void addDefaultModules(RegistryBuilder builder) 072 { 073 Set<String> blacklistedManifestModules = new HashSet<>(); 074 String modulesBlacklist = System.getProperty("tapestry.manifest-modules-blacklist"); 075 if (modulesBlacklist != null) 076 { 077 String[] blacklistedClassnames = modulesBlacklist.split(","); 078 079 080 for (String classname : blacklistedClassnames) 081 { 082 blacklistedManifestModules.add(classname.trim()); 083 } 084 } 085 086 try 087 { 088 Enumeration<URL> urls = builder.getClassLoader().getResources("META-INF/MANIFEST.MF"); 089 090 while (urls.hasMoreElements()) 091 { 092 URL url = urls.nextElement(); 093 094 addModulesInManifest(builder, url, blacklistedManifestModules); 095 } 096 097 addModulesInList(builder, System.getProperty("tapestry.modules")); 098 099 } catch (IOException ex) 100 { 101 throw new RuntimeException(ex.getMessage(), ex); 102 } 103 } 104 105 private static void addModulesInManifest(RegistryBuilder builder, URL url, Set<String> blacklist) 106 { 107 InputStream in = null; 108 109 Throwable fail = null; 110 111 try 112 { 113 in = url.openStream(); 114 115 Manifest mf = new Manifest(in); 116 117 in.close(); 118 119 in = null; 120 121 String list = mf.getMainAttributes().getValue(MODULE_BUILDER_MANIFEST_ENTRY_NAME); 122 123 addModulesInList(builder, list, blacklist); 124 } catch (RuntimeException ex) 125 { 126 fail = ex; 127 } catch (IOException ex) 128 { 129 fail = ex; 130 } finally 131 { 132 close(in); 133 } 134 135 if (fail != null) 136 throw new RuntimeException(String.format("Exception loading module(s) from manifest %s: %s", 137 url.toString(), 138 ExceptionUtils.toMessage(fail)), fail); 139 140 } 141 142 static void addModulesInList(RegistryBuilder builder, String list) 143 { 144 addModulesInList(builder, list, Collections.emptySet()); 145 } 146 147 static void addModulesInList(RegistryBuilder builder, String list, Set<String> blacklist) 148 { 149 if (list == null) return; 150 151 String[] classnames = list.split(","); 152 153 for (String classname : classnames) 154 { 155 String trimmedClassname = classname.trim(); 156 if (blacklist != null && blacklist.contains(trimmedClassname)) 157 { 158 continue; 159 } 160 161 builder.add(trimmedClassname); 162 } 163 } 164 165 /** 166 * Closes an input stream (or other Closeable), ignoring any exception. 167 * 168 * @param closeable 169 * the thing to close, or null to close nothing 170 */ 171 private static void close(Closeable closeable) 172 { 173 if (closeable != null) 174 { 175 try 176 { 177 closeable.close(); 178 } catch (IOException ex) 179 { 180 // Ignore. 181 } 182 } 183 } 184}