/atlassian-plugins-core/src/main/java/com/atlassian/plugin/url/InnerJarURLConnection.java

https://bitbucket.org/purewind/atlassian-plugins · Java · 201 lines · 114 code · 39 blank · 48 comment · 23 complexity · 59f067ec7224f8990cdaa52f51f24ab9 MD5 · raw file

  1. package com.atlassian.plugin.url;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.net.JarURLConnection;
  5. import java.net.MalformedURLException;
  6. import java.net.URL;
  7. import java.net.URLDecoder;
  8. import java.util.ArrayList;
  9. import java.util.List;
  10. import java.util.StringTokenizer;
  11. import java.util.jar.JarEntry;
  12. import java.util.jar.JarFile;
  13. import java.util.jar.JarInputStream;
  14. /**
  15. */
  16. class InnerJarURLConnection extends JarURLConnection {
  17. private URL baseResource;
  18. private String[] segments;
  19. private InputStream in;
  20. public InnerJarURLConnection(URL url) throws IOException {
  21. super(url = normaliseURL(url));
  22. String baseText = url.getPath();
  23. int bangLoc = baseText.indexOf("!");
  24. String baseResourceText = baseText.substring(0, bangLoc);
  25. String extraText = "";
  26. if (bangLoc <= (baseText.length() - 2)
  27. &&
  28. baseText.charAt(bangLoc + 1) == '/') {
  29. if (bangLoc + 2 == baseText.length()) {
  30. extraText = "";
  31. } else {
  32. extraText = baseText.substring(bangLoc + 1);
  33. }
  34. } else {
  35. throw new MalformedURLException("No !/ in url: " + url.toExternalForm());
  36. }
  37. List<String> segments = new ArrayList<String>();
  38. StringTokenizer tokens = new StringTokenizer(extraText, "!");
  39. while (tokens.hasMoreTokens()) {
  40. segments.add(tokens.nextToken());
  41. }
  42. this.segments = segments.toArray(new String[segments.size()]);
  43. this.baseResource = new URL(baseResourceText);
  44. }
  45. protected static URL normaliseURL(URL url) throws MalformedURLException {
  46. String text = normalizeUrlPath(url.toString());
  47. if (!text.startsWith("jar:")) {
  48. text = "jar:" + text;
  49. }
  50. if (text.indexOf('!') < 0) {
  51. text = text + "!/";
  52. }
  53. return new URL(text);
  54. }
  55. /**
  56. * Retrieve the nesting path segments.
  57. *
  58. * @return The segments.
  59. */
  60. protected String[] getSegments() {
  61. return this.segments;
  62. }
  63. /**
  64. * Retrieve the base resource <code>URL</code>.
  65. *
  66. * @return The base resource url.
  67. */
  68. protected URL getBaseResource() {
  69. return this.baseResource;
  70. }
  71. /**
  72. * @see java.net.URLConnection
  73. */
  74. public void connect() throws IOException {
  75. if (this.segments.length == 0) {
  76. setupBaseResourceInputStream();
  77. } else {
  78. setupPathedInputStream();
  79. }
  80. }
  81. /**
  82. * Setup the <code>InputStream</code> purely from the base resource.
  83. *
  84. * @throws java.io.IOException If an I/O error occurs.
  85. */
  86. protected void setupBaseResourceInputStream() throws IOException {
  87. this.in = getBaseResource().openStream();
  88. }
  89. /**
  90. * Setup the <code>InputStream</code> for URL with nested segments.
  91. *
  92. * @throws java.io.IOException If an I/O error occurs.
  93. */
  94. protected void setupPathedInputStream() throws IOException {
  95. InputStream curIn = getBaseResource().openStream();
  96. for (int i = 0; i < this.segments.length; ++i) {
  97. curIn = getSegmentInputStream(curIn, segments[i]);
  98. }
  99. this.in = curIn;
  100. }
  101. /**
  102. * Retrieve the <code>InputStream</code> for the nesting
  103. * segment relative to a base <code>InputStream</code>.
  104. *
  105. * @param baseIn The base input-stream.
  106. * @param segment The nesting segment path.
  107. * @return The input-stream to the segment.
  108. * @throws java.io.IOException If an I/O error occurs.
  109. */
  110. protected InputStream getSegmentInputStream(InputStream baseIn, String segment) throws IOException {
  111. JarInputStream jarIn = new JarInputStream(baseIn);
  112. JarEntry entry = null;
  113. while (jarIn.available() != 0) {
  114. entry = jarIn.getNextJarEntry();
  115. if (entry == null) {
  116. break;
  117. }
  118. if (("/" + entry.getName()).equals(segment)) {
  119. return jarIn;
  120. }
  121. }
  122. throw new IOException("unable to locate segment: " + segment);
  123. }
  124. /**
  125. * @see java.net.URLConnection
  126. */
  127. public InputStream getInputStream() throws IOException {
  128. if (this.in == null) {
  129. connect();
  130. }
  131. return this.in;
  132. }
  133. /**
  134. * @return JarFile
  135. * @throws java.io.IOException
  136. * @see java.net.JarURLConnection#getJarFile()
  137. */
  138. public JarFile getJarFile() throws IOException {
  139. String url = baseResource.toExternalForm();
  140. if (url.startsWith("file:/")) {
  141. url = url.substring(6);
  142. }
  143. return new JarFile(URLDecoder.decode(url, "UTF-8"));
  144. }
  145. private static String normalizeUrlPath(String name) {
  146. if (name.startsWith("/")) {
  147. name = name.substring(1);
  148. }
  149. // Looking for org/codehaus/werkflow/personality/basic/../common/core-idioms.xml
  150. // | i |
  151. // +-------+ remove
  152. //
  153. int i = name.indexOf("/..");
  154. // Can't be at the beginning because we have no root to refer to so
  155. // we start at 1.
  156. if (i > 0) {
  157. int j = name.lastIndexOf("/", i - 1);
  158. name = name.substring(0, j) + name.substring(i + 3);
  159. }
  160. return name;
  161. }
  162. }