/* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.activiti.osgi; import static org.activiti.osgi.Constants.BUNDLE_ACTIVITI_HEADER; import static org.osgi.framework.Constants.BUNDLE_MANIFESTVERSION; import static org.osgi.framework.Constants.BUNDLE_SYMBOLICNAME; import static org.osgi.framework.Constants.BUNDLE_VERSION; import java.io.OutputStream; import java.net.URL; import java.util.Set; import java.util.TreeSet; import java.util.jar.JarFile; import java.util.jar.JarInputStream; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.Deflater; import java.util.zip.ZipEntry; /** * @author <a href="gnodet@gmail.com">Guillaume Nodet</a> */ public class BarTransformer { public static void transform(URL url, OutputStream os) throws Exception { // Heuristicly retrieve name and version String name = url.getPath(); int idx = name.lastIndexOf('/'); if (idx >= 0) { name = name.substring(idx + 1); } String[] str = extractNameVersionType(name); // Build the list of folders containing resources String pathHeader; JarInputStream jis = new JarInputStream(url.openStream()); try { Set<String> paths = new TreeSet<String>(); ZipEntry e; while ((e = jis.getNextEntry()) != null) { String n = e.getName(); int i = n.lastIndexOf('/'); if (-1 == i) {// Add root path if the .bpmn20.xml is in the root of the bar file and the value is example.bpmn20.xml paths.add("/"); //Extender#checkBundle calls the HeaderParser#parseHeader and it does not parse an empty string } else if (i < n.length() - 1) { paths.add(n.substring(0, i + 1)); } } StringBuilder sb = new StringBuilder(); for (String s : paths) { if (sb.length() > 0) { sb.append(","); } sb.append(s); } pathHeader = sb.toString(); } finally { jis.close(); } // Build the stream jis = new JarInputStream(url.openStream()); try { JarOutputStream jos = new JarOutputStream(os); jos.setLevel(Deflater.NO_COMPRESSION); // Transform manifest Manifest m = jis.getManifest(); if (m == null) { m = new Manifest(); m.getMainAttributes().putValue("Manifest-Version", "2"); } if (m.getMainAttributes().getValue(BUNDLE_MANIFESTVERSION) == null) { m.getMainAttributes().putValue(BUNDLE_MANIFESTVERSION, "2"); } if (m.getMainAttributes().getValue(BUNDLE_SYMBOLICNAME) == null) { m.getMainAttributes().putValue(BUNDLE_SYMBOLICNAME, str[0]); } if (m.getMainAttributes().getValue(BUNDLE_VERSION) == null) { m.getMainAttributes().putValue(BUNDLE_VERSION, str[1]); } m.getMainAttributes().putValue(BUNDLE_ACTIVITI_HEADER, pathHeader); // Write manifest ZipEntry e = new ZipEntry(JarFile.MANIFEST_NAME); jos.putNextEntry(e); m.write(jos); jos.closeEntry(); // Write all entries byte[] readBuffer = new byte[8192]; while ((e = jis.getNextEntry()) != null) { ZipEntry e2 = new ZipEntry(e.getName()); // e2.setMethod(ZipEntry.STORED); // e2.setSize(e.getSize()); // e2.setCrc(e.getCrc()); jos.putNextEntry(e2); int bytesIn = jis.read(readBuffer); while (bytesIn != -1) { jos.write(readBuffer, 0, bytesIn); bytesIn = jis.read(readBuffer); } jos.closeEntry(); } jos.finish(); jos.flush(); } finally { jis.close(); } } private static final String DEFAULT_VERSION = "0.0.0"; private static final Pattern ARTIFACT_MATCHER = Pattern.compile("(.+)(?:-(\\d+)(?:\\.(\\d+)(?:\\.(\\d+))?)?(?:[^a-zA-Z0-9](.*))?)(?:\\.([^\\.]+))", Pattern.DOTALL); private static final Pattern FUZZY_MODIFIDER = Pattern.compile("(?:\\d+[.-])*(.*)", Pattern.DOTALL); public static String[] extractNameVersionType(String url) { Matcher m = ARTIFACT_MATCHER.matcher(url); if (!m.matches()) { return new String[] { url, DEFAULT_VERSION }; } else { //System.err.println(m.groupCount()); //for (int i = 1; i <= m.groupCount(); i++) { // System.err.println("Group " + i + ": " + m.group(i)); //} StringBuffer v = new StringBuffer(); String d1 = m.group(1); String d2 = m.group(2); String d3 = m.group(3); String d4 = m.group(4); String d5 = m.group(5); String d6 = m.group(6); if (d2 != null) { v.append(d2); if (d3 != null) { v.append('.'); v.append(d3); if (d4 != null) { v.append('.'); v.append(d4); if (d5 != null) { v.append("."); cleanupModifier(v, d5); } } else if (d5 != null) { v.append(".0."); cleanupModifier(v, d5); } } else if (d5 != null) { v.append(".0.0."); cleanupModifier(v, d5); } } return new String[] { d1, v.toString(), d6 }; } } private static void cleanupModifier(StringBuffer result, String modifier) { Matcher m = FUZZY_MODIFIDER.matcher(modifier); if (m.matches()) { modifier = m.group(1); } for (int i = 0; i < modifier.length(); i++) { char c = modifier.charAt(i); if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '-') { result.append(c); } } } }