001: /*
002: * SignJarsTask.java
003: *
004: * Created on April 17, 2007, 12:27 PM
005: */
006:
007: package org.netbeans.modules.javawebstart.signtask;
008:
009: import java.io.File;
010: import java.io.IOException;
011: import java.io.PrintWriter;
012:
013: import java.util.ArrayList;
014: import java.util.Enumeration;
015: import java.util.HashMap;
016: import java.util.HashSet;
017: import java.util.Iterator;
018: import java.util.LinkedList;
019: import java.util.List;
020: import java.util.Map;
021: import java.util.Map.Entry;
022: import java.util.Set;
023:
024: import java.util.zip.ZipEntry;
025: import java.util.zip.ZipFile;
026:
027: import org.apache.tools.ant.BuildException;
028: import org.apache.tools.ant.DirectoryScanner;
029: import org.apache.tools.ant.Project;
030: import org.apache.tools.ant.Task;
031: import org.apache.tools.ant.taskdefs.SignJar;
032: import org.apache.tools.ant.types.FileSet;
033:
034: /**
035: *
036: * @author Milan Kubec
037: */
038: public class SignJarsTask extends Task {
039:
040: private static final String SIG_START = "META-INF/";
041: private static final String SIG_END = ".SF";
042:
043: private int compIndex = 1;
044:
045: private String keystore;
046:
047: public void setKeystore(String s) {
048: keystore = s;
049: }
050:
051: private String storepass;
052:
053: public void setStorepass(String s) {
054: storepass = s;
055: }
056:
057: private String keypass;
058:
059: public void setKeypass(String s) {
060: keypass = s;
061: }
062:
063: private String alias;
064:
065: public void setAlias(String s) {
066: alias = s;
067: }
068:
069: private File mainJar;
070:
071: public void setMainjar(File f) {
072: mainJar = f;
073: }
074:
075: private File destDir;
076:
077: public void setDestdir(File f) {
078: destDir = f;
079: }
080:
081: private String codebase;
082:
083: public void setCodebase(String s) {
084: codebase = s;
085: }
086:
087: private String compProp;
088:
089: public void setComponentsprop(String s) {
090: compProp = s;
091: }
092:
093: private String signedJarsProp;
094:
095: public void setSignedjarsprop(String s) {
096: signedJarsProp = s;
097: }
098:
099: private List<FileSet> filesets = new LinkedList<FileSet>();
100:
101: public void addFileset(FileSet fs) {
102: filesets.add(fs);
103: }
104:
105: public void execute() throws BuildException {
106:
107: Map<Set<String>, List<File>> signersMap = new HashMap(); // Set<signerName> -> List<jarPath>
108: List<File> files2sign = new ArrayList<File>();
109: List<File> alreadySigned = new ArrayList<File>();
110:
111: Iterator it = filesets.iterator();
112: while (it.hasNext()) {
113: FileSet fs = (FileSet) it.next();
114: File dir = fs.getDir(getProject());
115: if (!dir.exists()) {
116: continue;
117: }
118: log("Processing FileSet: " + fs, Project.MSG_VERBOSE);
119: DirectoryScanner ds = fs.getDirectoryScanner(getProject());
120: File basedir = ds.getBasedir();
121: String[] files = ds.getIncludedFiles();
122: for (String f : files) {
123: try {
124: File fl = new File(basedir, f);
125: Set<String> sgs = getSignatures(fl);
126: if (sgs.size() == 0) {
127: files2sign.add(fl);
128: } else {
129: // test if the file is signed with passed alias
130: if (sgs.size() == 1
131: && sgs.contains(alias.toUpperCase())) {
132: alreadySigned.add(fl);
133: } else {
134: List lst = signersMap.get(sgs);
135: if (lst != null) {
136: lst.add(fl);
137: } else {
138: List<File> nlst = new ArrayList<File>();
139: nlst.add(fl);
140: signersMap.put(sgs, nlst);
141: }
142: }
143: }
144: } catch (IOException ex) {
145: throw new BuildException(ex, getLocation());
146: }
147: }
148: }
149:
150: log("Files to be signed: " + mainJar.toString() + ", "
151: + files2sign.toString(), Project.MSG_VERBOSE);
152:
153: // for already signed files generate component jnlp file
154: log("Files already signed by requested alias: "
155: + alreadySigned.toString(), Project.MSG_VERBOSE);
156:
157: StringBuilder signedJarsBuilder = new StringBuilder();
158: SignJar signJar = (SignJar) getProject().createTask("signjar");
159: signJar.setLocation(getLocation());
160: signJar.setKeystore(keystore);
161: signJar.setStorepass(storepass);
162: signJar.setKeypass(keypass);
163: signJar.setAlias(alias);
164: signJar.init();
165:
166: // test main jar if its already signed with passed alias ??
167: log("Signing main jar file: " + mainJar, Project.MSG_VERBOSE);
168: signJar.setJar(mainJar);
169: signJar.execute();
170:
171: if (files2sign.size() > 0) {
172: for (Iterator<File> iter = files2sign.iterator(); iter
173: .hasNext();) {
174: File f = iter.next();
175: log("Signing file: " + f, Project.MSG_VERBOSE);
176: signJar.setJar(f);
177: signJar.execute();
178: signedJarsBuilder.append("\n <jar href=\"lib/"
179: + f.getName() + "\" download=\"eager\"/>"); // XXX lib ?
180: }
181: }
182:
183: if (alreadySigned.size() > 0) {
184: for (Iterator<File> iter = alreadySigned.iterator(); iter
185: .hasNext();) {
186: File f = iter.next();
187: log("Adding signed file: " + f, Project.MSG_VERBOSE);
188: signedJarsBuilder.append("\n <jar href=\"lib/"
189: + f.getName() + "\" download=\"eager\"/>"); // XXX lib ?
190: }
191: }
192:
193: getProject().setProperty(signedJarsProp,
194: signedJarsBuilder.toString());
195:
196: StringBuilder compsBuilder = new StringBuilder();
197: for (Iterator<Entry<Set<String>, List<File>>> iter = signersMap
198: .entrySet().iterator(); iter.hasNext();) {
199: Entry<Set<String>, List<File>> entry = iter.next();
200: log("Already signed: keystore aliases = " + entry.getKey()
201: + " -> signed jars = " + entry.getValue(),
202: Project.MSG_VERBOSE);
203:
204: String compName = "jnlpcomponent" + compIndex++;
205: createJNLPComponentFile(entry.getKey(), entry.getValue(),
206: compName);
207: compsBuilder.append("\n <extension name=\""
208: + compName + "\" href=\"" + compName + ".jnlp\"/>");
209: }
210:
211: getProject().setProperty(compProp, compsBuilder.toString());
212:
213: }
214:
215: /**
216: * Returns set of signature aliases used to sign this file
217: */
218: private static Set<String> getSignatures(File f) throws IOException {
219: ZipFile jarFile = null;
220: Set<String> signatures = new HashSet<String>(3);
221: try {
222: jarFile = new ZipFile(f);
223: for (Enumeration<ZipEntry> en = (Enumeration<ZipEntry>) jarFile
224: .entries(); en.hasMoreElements();) {
225: ZipEntry je = en.nextElement();
226: if (!je.isDirectory()
227: && je.getName().startsWith(SIG_START)
228: && je.getName().endsWith(SIG_END)) {
229: // there is signature file, get the name
230: String sigName = je.getName().substring(
231: SIG_START.length(),
232: je.getName().indexOf(SIG_END));
233: signatures.add(sigName);
234: }
235: }
236: } finally {
237: if (jarFile != null)
238: jarFile.close();
239: }
240: return signatures;
241: }
242:
243: /**
244: * return filename of the JNLP component file ??
245: */
246: private String createJNLPComponentFile(Set<String> aliases,
247: List<File> jars, String compName) {
248:
249: File f = new File(destDir, compName + ".jnlp");
250: PrintWriter writer = null;
251: try {
252: writer = new PrintWriter(f);
253: } catch (IOException ioe) {
254: throw new BuildException(ioe, getLocation());
255: }
256: writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
257: writer.println("<jnlp spec=\"1.0+\" codebase=\"" + codebase
258: + "\">");
259: writer.println(" <information>");
260: writer.println(" <title>" + compName + "</title>");
261: writer.println(" <vendor>" + concatSet(aliases)
262: + "</vendor>");
263: writer.println(" </information>");
264: writer.println(" <security>");
265: writer.println(" <all-permissions/>");
266: writer.println(" </security>");
267: writer.println(" <resources>");
268: for (Iterator<File> iter = jars.iterator(); iter.hasNext();) {
269: writer.println(" <jar href=\""
270: + getPath(destDir.getAbsolutePath(), iter.next()
271: .getAbsolutePath())
272: + "\" download=\"eager\"/>");
273: }
274: writer.println(" </resources>");
275: writer.println(" <component-desc/>");
276: writer.println("</jnlp>");
277: writer.flush();
278: writer.close();
279:
280: return f.getAbsolutePath();
281:
282: }
283:
284: /* Returns the result after subtraction of filePath - dirPath
285: */
286: private String getPath(String dirPath, String filePath) {
287: String retVal = null;
288: if (filePath.indexOf(dirPath) != -1) {
289: retVal = (filePath.substring(dirPath.length() + 1).replace(
290: '\\', '/'));
291: }
292: return retVal;
293: }
294:
295: /* Returns the concatenation of set items, separated by commas
296: */
297: private String concatSet(Set<String> s) {
298: StringBuilder sb = new StringBuilder();
299: boolean first = true;
300: for (Iterator iter = s.iterator(); iter.hasNext();) {
301: if (first) {
302: sb.append(iter.next());
303: } else {
304: sb.append(", " + iter.next());
305: }
306: }
307: return sb.toString();
308: }
309:
310: }
|