You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AbstractCacheBackingTestSupport.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. /*******************************************************************************
  2. * Copyright (c) 2012 Contributors.
  3. * All rights reserved.
  4. * This program and the accompanying materials are made available
  5. * under the terms of the Eclipse Public License v1.0
  6. * which accompanies this distribution and is available at
  7. * http://eclipse.org/legal/epl-v10.html
  8. *
  9. * Contributors:
  10. * Lyor Goldstein (vmware) add support for weaved class being re-defined
  11. *******************************************************************************/
  12. package org.aspectj.weaver.tools.cache;
  13. import java.io.BufferedOutputStream;
  14. import java.io.File;
  15. import java.io.FileOutputStream;
  16. import java.io.IOException;
  17. import java.io.ObjectOutputStream;
  18. import java.io.StreamCorruptedException;
  19. import java.net.MalformedURLException;
  20. import java.net.URI;
  21. import java.net.URISyntaxException;
  22. import java.net.URL;
  23. import java.security.CodeSource;
  24. import java.security.ProtectionDomain;
  25. import java.util.Arrays;
  26. import java.util.Collection;
  27. import java.util.Collections;
  28. import org.aspectj.util.FileUtil;
  29. import org.aspectj.util.LangUtil;
  30. import org.aspectj.weaver.tools.cache.AbstractIndexedFileCacheBacking.IndexEntry;
  31. import junit.framework.TestCase;
  32. /**
  33. */
  34. public abstract class AbstractCacheBackingTestSupport extends TestCase {
  35. public static final String JAR_FILE_SUFFIX=".jar";
  36. /**
  37. * Prefix used in URL(s) that reference a resource inside a JAR
  38. */
  39. public static final String JAR_URL_PREFIX="jar:";
  40. /**
  41. * Separator used in URL(s) that reference a resource inside a JAR
  42. * to denote the sub-path inside the JAR
  43. */
  44. public static final char RESOURCE_SUBPATH_SEPARATOR='!';
  45. private File targetFolder;
  46. private File testTempFolder;
  47. protected File root;
  48. public static final String TEMP_SUBFOLDER_NAME="temp";
  49. protected AbstractCacheBackingTestSupport() {
  50. super();
  51. }
  52. protected AbstractCacheBackingTestSupport(String name) {
  53. super(name);
  54. }
  55. @Override
  56. public void setUp() throws Exception {
  57. super.setUp();
  58. if (root == null) {
  59. root = createTempFile("aspectj", "testdir");
  60. FileUtil.deleteContents(root);
  61. }
  62. }
  63. @Override
  64. public void tearDown() throws Exception {
  65. if (root != null) {
  66. FileUtil.deleteContents(root);
  67. root = null;
  68. }
  69. if (targetFolder != null) {
  70. FileUtil.deleteContents(targetFolder);
  71. }
  72. super.tearDown();
  73. }
  74. protected File ensureTempFolderExists () throws IllegalStateException {
  75. synchronized(TEMP_SUBFOLDER_NAME) {
  76. if (testTempFolder == null) {
  77. File parent=detectTargetFolder();
  78. testTempFolder = new File(parent, TEMP_SUBFOLDER_NAME);
  79. }
  80. }
  81. return ensureFolderExists(testTempFolder);
  82. }
  83. protected File detectTargetFolder () throws IllegalStateException {
  84. synchronized(TEMP_SUBFOLDER_NAME) {
  85. if (targetFolder == null) {
  86. try {
  87. File targetFolder = File.createTempFile("ajc", "TmpCacheDir");
  88. targetFolder.delete();
  89. targetFolder.mkdirs();
  90. } catch (IOException e) {
  91. throw new IllegalStateException("Unable to create cache dir",e);
  92. }
  93. // if ((targetFolder=detectTargetFolder(getClass())) == null) {
  94. //
  95. //// throw new IllegalStateException("Failed to detect target folder");
  96. // }
  97. }
  98. }
  99. return targetFolder;
  100. }
  101. protected File createTempFile (String prefix, String suffix) throws IOException {
  102. File destFolder=ensureTempFolderExists();
  103. return File.createTempFile(prefix, suffix, destFolder);
  104. }
  105. public static final File ensureFolderExists (File folder) throws IllegalStateException {
  106. if (folder == null) {
  107. throw new IllegalArgumentException("No folder to ensure existence");
  108. }
  109. if ((!folder.exists()) && (!folder.mkdirs())) {
  110. throw new IllegalStateException("Failed to create " + folder.getAbsolutePath());
  111. }
  112. return folder;
  113. }
  114. /**
  115. * @param anchor An anchor {@link Class} whose container we want to use
  116. * as the starting point for the "target" folder lookup up the
  117. * hierarchy
  118. * @return The &quot;target&quot; <U>folder</U> - <code>null</code> if not found
  119. * @see #detectTargetFolder(File)
  120. */
  121. public static final File detectTargetFolder (Class<?> anchor) {
  122. return detectTargetFolder(getClassContainerLocationFile(anchor));
  123. }
  124. /**
  125. * @param anchorFile An anchor {@link File) we want to use
  126. * as the starting point for the &quot;target&quot; folder lookup up the
  127. * hierarchy
  128. * @return The &quot;target&quot; <U>folder</U> - <code>null</code> if not found
  129. */
  130. public static final File detectTargetFolder (File anchorFile) {
  131. for (File file=anchorFile; file != null; file=file.getParentFile()) {
  132. if (!file.isDirectory()) {
  133. continue;
  134. }
  135. String name=file.getName();
  136. if ("target".equals(name) || "bin".equals(name) || "src".equals(name)) {
  137. File parent=file.getParentFile();
  138. return new File(parent, "target2");
  139. }
  140. }
  141. return null;
  142. }
  143. /**
  144. * @param clazz A {@link Class} object
  145. * @return A {@link File} of the location of the class bytes container
  146. * - e.g., the root folder, the containing JAR, etc.. Returns
  147. * <code>null</code> if location could not be resolved
  148. * @throws IllegalArgumentException If location is not a valid
  149. * {@link File} location
  150. * @see #getClassContainerLocationURI(Class)
  151. * @see File#File(URI)
  152. */
  153. public static File getClassContainerLocationFile (Class<?> clazz)
  154. throws IllegalArgumentException {
  155. try {
  156. URI uri=getClassContainerLocationURI(clazz);
  157. return (uri == null) ? null : new File(uri);
  158. } catch(URISyntaxException e) {
  159. throw new IllegalArgumentException(e.getClass().getSimpleName() + ": " + e.getMessage(), e);
  160. }
  161. }
  162. /**
  163. * @param clazz A {@link Class} object
  164. * @return A {@link URI} to the location of the class bytes container
  165. * - e.g., the root folder, the containing JAR, etc.. Returns
  166. * <code>null</code> if location could not be resolved
  167. * @throws URISyntaxException if location is not a valid URI
  168. * @see #getClassContainerLocationURL(Class)
  169. */
  170. public static URI getClassContainerLocationURI (Class<?> clazz) throws URISyntaxException {
  171. URL url=getClassContainerLocationURL(clazz);
  172. return (url == null) ? null : url.toURI();
  173. }
  174. /**
  175. * @param clazz A {@link Class} object
  176. * @return A {@link URL} to the location of the class bytes container
  177. * - e.g., the root folder, the containing JAR, etc.. Returns
  178. * <code>null</code> if location could not be resolved
  179. */
  180. public static URL getClassContainerLocationURL (Class<?> clazz) {
  181. ProtectionDomain pd=clazz.getProtectionDomain();
  182. CodeSource cs=(pd == null) ? null : pd.getCodeSource();
  183. URL url=(cs == null) ? null : cs.getLocation();
  184. if (url == null) {
  185. ClassLoader cl=getDefaultClassLoader(clazz);
  186. String className=clazz.getName().replace('.', '/') + ".class";
  187. if ((url=cl.getResource(className)) == null) {
  188. return null;
  189. }
  190. String srcForm=getURLSource(url);
  191. if (LangUtil.isEmpty(srcForm)) {
  192. return null;
  193. }
  194. try {
  195. url = new URL(srcForm);
  196. } catch(MalformedURLException e) {
  197. throw new IllegalArgumentException("getClassContainerLocationURL(" + clazz.getName() + ")"
  198. + "Failed to create URL=" + srcForm + " from " + url.toExternalForm()
  199. + ": " + e.getMessage());
  200. }
  201. }
  202. return url;
  203. }
  204. /**
  205. * @param anchor An &quot;anchor&quot; {@link Class} to be used in case
  206. * no thread context loader is available
  207. * @return A {@link ClassLoader} to be used by the caller. The loader is
  208. * resolved in the following manner:</P></BR>
  209. * <UL>
  210. * <LI>
  211. * If a non-<code>null</code> loader is returned from the
  212. * {@link Thread#getContextClassLoader()} call then use it.
  213. * </LI>
  214. *
  215. * <LI>
  216. * Otherwise, use the same loader that was used to load the anchor class.
  217. * </LI>
  218. * </UL>
  219. * @throws IllegalArgumentException if no anchor class provided (regardless of
  220. * whether it is used or not)
  221. */
  222. public static ClassLoader getDefaultClassLoader(Class<?> anchor) {
  223. if (anchor == null) {
  224. throw new IllegalArgumentException("No anchor class provided");
  225. }
  226. Thread t=Thread.currentThread();
  227. ClassLoader cl=t.getContextClassLoader();
  228. if (cl == null) {
  229. // No thread context class loader -> use class loader of this class.
  230. cl = anchor.getClassLoader();
  231. }
  232. if (cl == null) { // no class loader - assume system
  233. cl = ClassLoader.getSystemClassLoader();
  234. }
  235. return cl;
  236. }
  237. public static final String getURLSource (File file) {
  238. return getURLSource((file == null) ? null : file.toURI());
  239. }
  240. public static final String getURLSource (URI uri) {
  241. return getURLSource((uri == null) ? null : uri.toString());
  242. }
  243. /**
  244. * @param url The {@link URL} value - ignored if <code>null</code>
  245. * @return The URL(s) source path where {@link #JAR_URL_PREFIX} and
  246. * any sub-resource are stripped
  247. * @see #getURLSource(String)
  248. */
  249. public static final String getURLSource (URL url) {
  250. return getURLSource((url == null) ? null : url.toExternalForm());
  251. }
  252. /**
  253. * @param externalForm The {@link URL#toExternalForm()} string - ignored if
  254. * <code>null</code>/empty
  255. * @return The URL(s) source path where {@link #JAR_URL_PREFIX} and
  256. * any sub-resource are stripped
  257. */
  258. public static final String getURLSource (String externalForm) {
  259. String url=externalForm;
  260. if (LangUtil.isEmpty(url)) {
  261. return url;
  262. }
  263. url = stripJarURLPrefix(externalForm);
  264. if (LangUtil.isEmpty(url)){
  265. return url;
  266. }
  267. int sepPos=url.indexOf(RESOURCE_SUBPATH_SEPARATOR);
  268. if (sepPos < 0) {
  269. return adjustURLPathValue(url);
  270. } else {
  271. return adjustURLPathValue(url.substring(0, sepPos));
  272. }
  273. }
  274. /**
  275. * @param path A URL path value
  276. * @return The path after stripping any trailing '/' provided the path
  277. * is not '/' itself
  278. */
  279. public static final String adjustURLPathValue(final String path) {
  280. final int pathLen=LangUtil.isEmpty(path) ? 0 : path.length();
  281. if ((pathLen <= 1) || (path.charAt(pathLen - 1) != '/')) {
  282. return path;
  283. }
  284. return path.substring(0, pathLen - 1);
  285. }
  286. public static final String adjustURLPathValue(URL url) {
  287. return adjustURLPathValue((url == null) ? null : url.getPath());
  288. }
  289. public static String stripJarURLPrefix(String externalForm) {
  290. String url=externalForm;
  291. if (LangUtil.isEmpty(url)) {
  292. return url;
  293. }
  294. if (url.startsWith(JAR_URL_PREFIX)) {
  295. return url.substring(JAR_URL_PREFIX.length());
  296. }
  297. return url;
  298. }
  299. protected static final void writeIndex (File indexFile, IndexEntry ... entries) throws IOException {
  300. writeIndex(indexFile, LangUtil.isEmpty(entries) ? Collections.<IndexEntry>emptyList() : Arrays.asList(entries));
  301. }
  302. protected static final void writeIndex (File indexFile, Collection<? extends IndexEntry> entries) throws IOException {
  303. File indexDir=indexFile.getParentFile();
  304. if ((!indexDir.exists()) && (!indexDir.mkdirs())) {
  305. throw new IOException("Failed to create path to " + indexFile.getAbsolutePath());
  306. }
  307. int numEntries=LangUtil.isEmpty(entries) ? 0 : entries.size();
  308. IndexEntry[] entryValues=(numEntries <= 0) ? null : entries.toArray(new IndexEntry[numEntries]);
  309. // if no entries, simply delete the index file
  310. if (LangUtil.isEmpty(entryValues)) {
  311. if (indexFile.exists() && (!indexFile.delete())) {
  312. throw new StreamCorruptedException("Failed to clean up index file at " + indexFile.getAbsolutePath());
  313. }
  314. return;
  315. }
  316. ObjectOutputStream oos=new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(indexFile), 4096));
  317. try {
  318. oos.writeObject(entryValues);
  319. } finally {
  320. oos.close();
  321. }
  322. }
  323. public static final void assertArrayEquals (String msg, byte[] expected, byte[] actual) {
  324. int eLen=LangUtil.isEmpty(expected) ? 0 : expected.length;
  325. int aLen=LangUtil.isEmpty(actual) ? 0 : expected.length;
  326. assertEquals(msg + "[mismatched length]", eLen, aLen);
  327. for (int index=0; index < eLen; index++) {
  328. byte eb=expected[index], ab=actual[index];
  329. if (eb != ab) {
  330. fail(msg + ": Mismatched value at index=" + index
  331. + " - " + ab + " instead of " + eb
  332. + ": expected=" + Arrays.toString(expected) + ", actual=" + Arrays.toString(actual));
  333. }
  334. }
  335. }
  336. }