/*
 * @(#)JnlpResource.java	1.8 05/11/17
 * 
 * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * -Redistribution of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *
 * -Redistribution in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *
 * Neither the name of Sun Microsystems, Inc. or the names of contributors may
 * be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
 * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
 * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
 * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
 * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 *
 * You acknowledge that this software is not designed, licensed or intended
 * for use in the design, construction, operation or maintenance of any
 * nuclear facility.
 */

package jnlp.sample.servlet;

import javax.servlet.ServletContext;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Date;

/**
 * A JnlpResource encapsulate the information about a resource that is
 * needed to process a JNLP Download Request.
 * <p>
 * The pattern matching arguments are: name, version-id, os, arch, and locale.
 * <p>
 * The outgoing arguments are:
 * - path to resource in (WAR File)
 * - product version-id (Version-id to return or null. Typically same as version-id above)
 * - mime-type for content
 * - lastModified date of WAR file resource
 */
public class JnlpResource
{
    private static final String JNLP_MIME_TYPE = "application/x-java-jnlp-file";

    private static final String JAR_MIME_TYPE = "application/x-java-archive";

    private static final String JAR_MIME_TYPE_NEW = "application/java-archive";

    // Default extension for the JNLP file
    private static final String JNLP_EXTENSION = ".jnlp";

    private static final String JAR_EXTENSION = ".jar";

    private static String _jnlpExtension = JNLP_EXTENSION;

    private static String _jarExtension = JAR_EXTENSION;

    public static void setDefaultExtensions( String jnlpExtension, String jarExtension )
    {
        if ( jnlpExtension != null && jnlpExtension.length() > 0 )
        {
            if ( !jnlpExtension.startsWith( "." ) )
            {
                jnlpExtension = "." + jnlpExtension;
            }
            _jnlpExtension = jnlpExtension;
        }
        if ( jarExtension != null && jarExtension.length() > 0 )
        {
            if ( !jarExtension.startsWith( "." ) )
            {
                jarExtension = "." + jarExtension;
            }
            _jarExtension = jarExtension;
        }
    }

    /* Pattern matching arguments */
    private String _name;      // Name of resource with path (this is the same as path for non-version based)

    private String _versionId;    // Version-id for resource, or null if none

    private String[] _osList;     // List of OSes for which resource should be returned

    private String[] _archList;   // List of architectures for which the resource should be returned

    private String[] _localeList; // List of locales for which the resource should be returned

    /* Information used for reply */
    private String _path;            // Path to resource in WAR file (unique)

    private URL _resource;        // URL to resource in WAR file (unique - same as above really)

    private long _lastModified;    // Last modified in WAR file

    private String _mimeType;        // Mime-type for resource

    private String _returnVersionId; // Version Id to return

    private String _encoding;        // Accept encoding

    public JnlpResource( ServletContext context, String path )
    {
        this( context, null, null, null, null, null, path, null );
    }

    public JnlpResource( ServletContext context, String name, String versionId, String[] osList, String[] archList,
                         String[] localeList, String path, String returnVersionId )
    {
        this( context, name, versionId, osList, archList, localeList, path, returnVersionId, null );
    }

    public JnlpResource( ServletContext context, String name, String versionId, String[] osList, String[] archList,
                         String[] localeList, String path, String returnVersionId, String encoding )
    {
        // Matching arguments
        _encoding = encoding;
        _name = name;
        _versionId = versionId;
        _osList = osList;
        _archList = archList;
        _localeList = localeList;

        _returnVersionId = returnVersionId;

        /* Check for existance and get last modified timestamp */
        try
        {
            String orig_path = path.trim();
            String search_path = orig_path;
            _resource = context.getResource( orig_path );
            _mimeType = getMimeType( context, orig_path );
            if ( _resource != null )
            {

                boolean found = false;
                // pack200 compression
                if ( encoding != null && _mimeType != null &&
                        ( _mimeType.compareTo( JAR_MIME_TYPE ) == 0 || _mimeType.compareTo( JAR_MIME_TYPE_NEW ) == 0 ) &&
                        encoding.toLowerCase().contains( DownloadResponse.PACK200_GZIP_ENCODING ) )
                {
                    search_path = orig_path + ".pack.gz";
                    _resource = context.getResource( search_path );
                    // Get last modified time
                    if ( _resource != null )
                    {
                        _lastModified = getLastModified( context, _resource, search_path );
                        if ( _lastModified != 0 )
                        {
                            _path = search_path;
                            found = true;
                        }
                        else
                        {
                            _resource = null;
                        }
                    }
                }

                // gzip compression
                if ( !found && encoding != null &&
                        encoding.toLowerCase().contains( DownloadResponse.GZIP_ENCODING ) )
                {
                    search_path = orig_path + ".gz";
                    _resource = context.getResource( search_path );
                    // Get last modified time
                    if ( _resource != null )
                    {
                        _lastModified = getLastModified( context, _resource, search_path );
                        if ( _lastModified != 0 )
                        {
                            _path = search_path;
                            found = true;
                        }
                        else
                        {
                            _resource = null;
                        }
                    }
                }

                if ( !found )
                {
                    // no compression
                    search_path = orig_path;
                    _resource = context.getResource( search_path );
                    // Get last modified time
                    if ( _resource != null )
                    {
                        _lastModified = getLastModified( context, _resource, search_path );
                        if ( _lastModified != 0 )
                        {
                            _path = search_path;
                            found = true;
                        }
                        else
                        {
                            _resource = null;
                        }
                    }
                }
            }
        }
        catch ( IOException ioe )
        {
            _resource = null;
        }
    }

    long getLastModified( ServletContext context, URL resource, String path )
    {
        long lastModified = 0;
        URLConnection conn;
        try
        {
            // Get last modified time
            conn = resource.openConnection();
            lastModified = conn.getLastModified();
        }
        catch ( Exception e )
        {
            // do nothing
        }

        if ( lastModified == 0 )
        {
            // Arguably a bug in the JRE will not set the lastModified for file URLs, and
            // always return 0. This is a workaround for that problem.
            String filepath = context.getRealPath( path );
            if ( filepath != null )
            {
                File f = new File( filepath );
                if ( f.exists() )
                {
                    lastModified = f.lastModified();
                }
            }
        }
        return lastModified;
    }

    /* Get resource specific attributes */
    public String getPath()
    {
        return _path;
    }

    public URL getResource()
    {
        return _resource;
    }

    public String getMimeType()
    {
        return _mimeType;
    }

    public long getLastModified()
    {
        return _lastModified;
    }

    public boolean exists()
    {
        return _resource != null;
    }

    public boolean isJnlpFile()
    {
        return _path.endsWith( _jnlpExtension );
    }

    public boolean isJarFile()
    {
        return _path.endsWith( _jarExtension );
    }

    /* Get JNLP version specific attributes */
    public String getName()
    {
        return _name;
    }

    public String getVersionId()
    {
        return _versionId;
    }

    public String[] getOSList()
    {
        return _osList;
    }

    public String[] getArchList()
    {
        return _archList;
    }

    public String[] getLocaleList()
    {
        return _localeList;
    }

    public String getReturnVersionId()
    {
        return _returnVersionId;
    }

    private String getMimeType( ServletContext context, String path )
    {
        String mimeType = context.getMimeType( path );
        if ( mimeType != null )
        {
            return mimeType;
        }
        if ( path.endsWith( _jnlpExtension ) )
        {
            return JNLP_MIME_TYPE;
        }
        if ( path.endsWith( _jarExtension ) )
        {
            return JAR_MIME_TYPE;
        }
        return "application/unknown";
    }

    /**
     * Print info about an entry
     */
    public String toString()
    {
        return "JnlpResource[WAR Path: " + _path + showEntry( " versionId=", _versionId ) +
                showEntry( " name=", _name ) + " lastModified=" + new Date( _lastModified ) +
                showEntry( " osList=", _osList ) + showEntry( " archList=", _archList ) +
                showEntry( " localeList=", _localeList ) + "]" + showEntry( " returnVersionId=", _returnVersionId ) + "]";

    }

    private String showEntry( String msg, String value )
    {
        if ( value == null )
        {
            return "";
        }
        return msg + value;
    }

    private String showEntry( String msg, String[] value )
    {
        if ( value == null )
        {
            return "";
        }
        return msg + java.util.Arrays.asList( value ).toString();
    }
}