Source

gradle-bnd-plugin / src / main / groovy / in / vayana / gradle_plugins / GradleBndPlugin.groovy

/*******************************************************************************
*   Copyright 2011 Vayana Services Private Limited                             *
*                                                                              *
*   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 in.vayana.gradle_plugins

import java.util.jar.JarFile
import java.util.jar.JarEntry
import java.util.jar.JarOutputStream
import aQute.lib.osgi.Analyzer
import org.gradle.api.*

class GradleBndPluginExtension {
   def Map<String,String> instructions = new HashMap<String,String>() 
   def Set<String> exclusions = new HashSet<String>()
   def Boolean condense = false
}

class GradleBndPlugin implements Plugin<Project> {
    def void apply(Project project) {
        project.extensions.bnd = new GradleBndPluginExtension()
        project.tasks.jar {
            // having a lot of issues with the construct below. Need to see if it could be
            // used to make the code a lot more compact
            // from {project.configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }

            // in general I think there is a lot of potential to make the code simpler by using
            // file trees .. but leaving it for now as is.
            doLast() {
                classpathJars = [] // to be used eventually

                oldJar = project.jar.archivePath
                excludedJars = []
                if(project.bnd.condense) {
                    pattern = /(.*)-[^-]*$/
                    // move old jar away
                    newJarName = oldJar.toString().replaceFirst('.jar$',".standalone.jar")
                    oldJar.renameTo(new File(newJarName))

                    // create the new jar with same name as the output of the jar task

                    outputJar = new JarOutputStream(
                                    new FileOutputStream(oldJar.toString()))
                    ((project.configurations.compile as List) + new File(newJarName)).each { file -> 
                        matcher = (file.name =~ pattern)
                        if (matcher.matches()) jarBaseName = matcher[0][1] else jarBaseName = null
                        if ((jarBaseName != null) && !(jarBaseName in project.bnd.exclusions)) {
                            // extract each entry from the jar file
                            jarFile = new JarFile(file.absolutePath)
                            jarFile.entries().each { entry ->
                                // copy each entry over into the destination jar
                                outEntry = new JarEntry(entry.getName())
                                outEntry.setTime(entry.getTime())
                                try {
                                    outputJar.putNextEntry(outEntry)
                                    if (! entry.isDirectory()) {
                                        outputJar.write(
                                            jarFile.getInputStream(entry).getBytes())
                                    }
                                } catch (java.util.zip.ZipException ze) {
                                    // this happens quite often due to duplicates
                                    // especially META-INF/MANIFEST.MF
                                    // or recreation of the same directories eg. org., com. etc
                                    ; // ignore
                                }
                            }
                        } else {
                            excludedJars.add(file.toString())
                        }
                    }
                    outputJar.close()
                }

                Analyzer analyzer = new Analyzer()
                analyzer.setJar(new File(oldJar.toString()))
                project.bnd.instructions.each {name,value ->
                    analyzer.setProperty(name,value)
                }
                excludedJars.each{jarPath -> analyzer.addClasspath(new File(jarPath))}
                java.util.jar.Manifest manifest = analyzer.calcManifest()
                jarFile = new File(oldJar.toString())
                tmpFileName = oldJar.toString() + ".tmp"
                jarFile.renameTo(tmpFileName)
                outputJar = new JarOutputStream(
                                new FileOutputStream(oldJar.toString()))
                jarFileHandle = new File(tmpFileName)
                jarFile = new JarFile(jarFileHandle) // , false, java.util.zip.ZipFile.OPEN_DELETE)
                jarFile.entries().each { entry ->
                    if (entry.toString() != "META-INF/MANIFEST.MF") {
                        outEntry = new JarEntry(entry.getName())
                        outEntry.setTime(entry.getTime())
                        try {
                            outputJar.putNextEntry(outEntry)
                            if (! entry.isDirectory()) {
                                outputJar.write(
                                    jarFile.getInputStream(entry).getBytes())
                            }
                        } catch (java.util.zip.ZipException ze) {
                            ; // ignore
                        }
                    }
                }
                
                jarFile.close();
                new File(tmpFileName).delete();
                jarEntry = new JarEntry("META-INF/MANIFEST.MF")
                outputJar.putNextEntry(jarEntry)
                manifest.write(outputJar)
                outputJar.close()
            }
        }
    }
}