FileDocCategorySizeDatePackage
SignedStaticContent.javaAPI DocGlassfish v2 API7674Fri May 04 22:34:12 BST 2007com.sun.enterprise.appclient.jws

SignedStaticContent.java

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package com.sun.enterprise.appclient.jws;

import com.sun.enterprise.deployment.backend.DeploymentLogger;
import com.sun.enterprise.security.SSLUtils;
import com.sun.enterprise.security.SecurityUtil;
import com.sun.enterprise.util.SystemPropertyConstants;
import com.sun.enterprise.util.i18n.StringManager;
import java.io.File;
import java.net.URI;
import java.security.AccessControlException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.Permission;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Represents a jar file that is signed before being served in 
 * response to a Java Web Start request.
 *
 * Signing occurs when the jar is first requested.  
 *
 * @author tjquinn
 */
public class SignedStaticContent extends StaticContent {
    
    /** the unsigned jar to be served in signed form */
    private final File unsignedJar;
    
    /** the signed jar */
    private final File signedJar;
    
    /** URI for the app server installation root */
    private URI installRootURI;

    private final Logger logger = DeploymentLogger.get();

    private final StringManager localStrings;
    
    
    /**
     * Creates a new instance of SignedStaticContent
     * 
     * @param origin the origin from which the jar to be signed comes
     * @param contentKey key by which this jar will be retrievable when requested
     * @param path the relative path within the app server to the jar
     * @param signedJar specifies what the resulting signed jar file should be
     * @param unsignedjar the existing unsigned jar to be signed just-in-time
     * @param installRootURI the app server's installation directory
     * @param isMain indicates if the jar contains the mail class that Java Web Start should launch
     */
    
    public SignedStaticContent(
            ContentOrigin origin, 
            String contentKey, 
            String path, 
            File signedJar,
            File unsignedJar, 
            URI installRootURI,
            StringManager localStrings,
            boolean isMain) throws Exception {
        super(origin, contentKey, path, signedJar, installRootURI, isMain);
        
        /*
         *Find out as much as we can in order to sign the jar, but do not sign it yet.
         */
        this.installRootURI = installRootURI;
        this.unsignedJar = unsignedJar;
        this.signedJar = signedJar;
        this.localStrings = localStrings;
    }
    
    /**
     *Returns the URI, relative to the app server installation directory, of
     *the signed jar file to be published, signing the unsigned jar if needed
     *to create the signed one to serve.
     *@return relative URI to the jar
     */
    public URI getRelativeURI() {
        try {
            ensureSignedFileUpToDate();
            return installRootURI.relativize(signedJar.toURI());
        } catch (Throwable t) {
            throw new RuntimeException(t);
        }
    }

    /**
     *Makes sure that the signed jar exists and is up-to-date compared to the
     *corresponding unsigned jar.  If not, create a new signed jar using
     *the alias provided on this object's constructor.
     *@throws KeyStoreException in case of errors reading the key store
     *@throws IllegalArgumentException if the unsigned jar does not exists
     */
    private synchronized void ensureSignedFileUpToDate() throws KeyStoreException, IllegalArgumentException, Exception {
        /*
         *Check to see if the signed version of this jar is present.
         */
        if ( ! unsignedJar.exists()) {
            throw new IllegalArgumentException(
                    localStrings.getString("jws.sign.noUnsignedJar", unsignedJar.getAbsolutePath()));
        }

        if ( ! signedJar.exists() || (signedJar.lastModified() < unsignedJar.lastModified())) {
            signJar();
        }
    }
    
    /**
     *Signs the jar file.
     *@throws Exception when signing the JAR or obtaining keystore information
     */
    private void signJar() throws Exception {
        /*
         *In EE environments synchronization does not include empty directories.
         *So the java-web-start/<app-name> directory may not yet exist if this
         *is a non-DAS instance.  So just make sure the required directories
         *are created.
         */
        File signedJarParent = signedJar.getParentFile();
        if ( ! signedJarParent.exists() && ! signedJarParent.mkdirs() ) {
            throw new Exception(localStrings.getString("jws.sign.errorCreatingDir", signedJarParent.getAbsolutePath()));
        }
        
        ASJarSigner.signJar(unsignedJar, signedJar);
    }
    
    /**
     *Returns the password for the keystore.
     *@return the keystore password
     */
    private String getKeystorePassword() {
        return SSLUtils.getKeyStorePass();
    }

    /**
     *Returns whether the specified alias is present in the keystore or not.  Also
     *logs a warning if the user-specified alias is missing.
     *@param keystore the keystore to use in checking the user alias
     *@param candidateAlias the alias to look for
     *@return true if the alias is present in the keystore; false otherwise
     *@throws KeyStoreException in case of error accessing the keystore
     */
    private boolean checkUserAlias(KeyStore keystore, String candidateAlias) throws KeyStoreException {
        boolean result;
        if ( ! (result = keystore.containsAlias(candidateAlias)) ) {
            logger.warning(localStrings.getString("jws.sign.userAliasAbsent", candidateAlias));
        }
        return result;
    }
}