[fyi][icedtea-web] backend and itw-settings for extended applets security

Adam Domurad adomurad at redhat.com
Mon Feb 4 13:38:36 PST 2013


On 02/04/2013 01:02 PM, Adam Domurad wrote:
> On 02/03/2013 07:08 AM, Jiri Vanek wrote:
>> Whole troubles are described here 
>> http://icedtea.classpath.org/wiki/Extended_Applets_Security rather 
>> then in plain email.
>>
>> There is what me and adam have already achieved on this topic, what 
>> we have o achieve and where we are stil not so sure....
>>
>> This patch is containing itw-settings part and backend for 
>> manipulating stored entries.and basic matching.
>> I noted this just [fyi] because adam ave already walked through the 
>> patch, and so have I across his part. So the patch should be ready 
>> for push unless someone find something malicious on implementation or 
>> even on design.
>>
>> J.
>
> TYVM for writing out the wiki page.
>
> I'm allowed to comment right ? :-)
> The table is looking quite nice.
>
> We can push as is and work from head perhaps ? Needs ChangeLog, though.
>
> I have attached a preferred iteration of my test.
> I have attached a preferred refactoring of LockingFile :-)
>
>> diff -r e631770d76ba 
>> netx/net/sourceforge/appletextendedsecurity/AppletExtendedSecurity.java
>> --- /dev/null    Thu Jan 01 00:00:00 1970 +0000
>> +++ 
>> b/netx/net/sourceforge/appletextendedsecurity/AppletExtendedSecurity.java 
>>  Fri Feb 01 20:55:48 2013 +0100
>> @@ -0,0 +1,84 @@
>> +/*
>> +Copyright (C) 2013 Red Hat
>> +
>> +This program is free software; you can redistribute it and/or modify
>> +it under the terms of the GNU General Public License as published by
>> +the Free Software Foundation; either version 2 of the License, or
>> +(at your option) any later version.
>> +
>> +This program is distributed in the hope that it will be useful, but
>> +WITHOUT ANY WARRANTY; without even the implied warranty of
>> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
>> +General Public License for more details.
>> +
>> +You should have received a copy of the GNU General Public License
>> +along with this program; if not, write to the Free Software
>> +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
>> +*/
>> +
>> +package net.sourceforge.appletextendedsecurity;
>> +
>> +import javax.naming.ConfigurationException;
>> +import 
>> net.sourceforge.appletextendedsecurity.impl.UnsignedAppletActionStorageImpl;
>> +import net.sourceforge.jnlp.config.DeploymentConfiguration;
>> +import net.sourceforge.jnlp.runtime.JNLPRuntime;
>> +
>> +
>> +public class AppletExtendedSecurity {
>> +
>> +    private static UnsignedAppletActionStorageImpl globalInstance;
>> +    private static UnsignedAppletActionStorageImpl customInstance;
>> +
>> +    /**
>> +     *
>> +     * @return storage with global items from /etc/
>> +     */
>> +
>> +    public static UnsignedAppletActionStorage 
>> getUnsignedAppletActionGlobalStorage(){
>> +        if (globalInstance == null){
>> +            globalInstance = new 
>> UnsignedAppletActionStorageImpl(DeploymentConfiguration.getAppletTrustGlobalSettingsPath());
>> +        }
>> +        return globalInstance;
>> +    }
>> +
>> +
>> +    /**
>> +     *
>> +     * @return storage with custom items from /home/
>> +     */
>> +    public static UnsignedAppletActionStorage 
>> getUnsignedAppletActionCustomStorage(){
>> +        if (customInstance == null){
>> +            customInstance = new 
>> UnsignedAppletActionStorageImpl(DeploymentConfiguration.getAppletTrustCustomSettingsPath());
>> +        }
>> +        return customInstance;
>> +    }
>> +
>> +    public static AppletSecurityLevel getDefaultSecurityLevel(){
>> +        return AppletSecurityLevel.getDefault();
>> +    }
>> +
>> +    /**
>> +     *
>> +     * @return user-set seurity level or default one if user-set do 
>> not exists
>> +     */
>> +    public static AppletSecurityLevel getCustomSecurityLevel(){
>> +        DeploymentConfiguration conf = JNLPRuntime.getConfiguration();
>> +        if (conf==null){
>> +            conf = new DeploymentConfiguration();
>> +            try {
>> +            conf.load();
>> +            } catch (ConfigurationException ex){
>> +                ex.printStackTrace();
>> +                return getDefaultSecurityLevel();
>> +            }
>> +        }
>> +        String s = 
>> conf.getProperty(DeploymentConfiguration.KEY_SECURITY_LEVEL);
>> +        if (s==null) {
>> +            return getDefaultSecurityLevel();
>> +        }
>> +        return AppletSecurityLevel.fromString(s);
>> +    }
>> +
>> +
>> +
>> +}
>> diff -r e631770d76ba 
>> netx/net/sourceforge/appletextendedsecurity/AppletSecurityLevel.java
>> --- /dev/null    Thu Jan 01 00:00:00 1970 +0000
>> +++ 
>> b/netx/net/sourceforge/appletextendedsecurity/AppletSecurityLevel.java  Fri 
>> Feb 01 20:55:48 2013 +0100
>> @@ -0,0 +1,83 @@
>> +/*
>> +Copyright (C) 2013 Red Hat
>> +
>> +This program is free software; you can redistribute it and/or modify
>> +it under the terms of the GNU General Public License as published by
>> +the Free Software Foundation; either version 2 of the License, or
>> +(at your option) any later version.
>> +
>> +This program is distributed in the hope that it will be useful, but
>> +WITHOUT ANY WARRANTY; without even the implied warranty of
>> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
>> +General Public License for more details.
>> +
>> +You should have received a copy of the GNU General Public License
>> +along with this program; if not, write to the Free Software
>> +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
>> +*/
>> +
>> +package net.sourceforge.appletextendedsecurity;
>> +
>> +import net.sourceforge.jnlp.runtime.Translator;
>> +
>> +public enum AppletSecurityLevel {
>> +
>> +    DENY_ALL, DENY_UNSIGNED, ASK_UNSIGNED, ALLOW_UNSIGNED;
>> +
>> +    public static String allToString() {
>> +        return DENY_ALL.toChars()+" "+DENY_UNSIGNED.toChars()+" 
>> "+ASK_UNSIGNED.toChars()+" "+ALLOW_UNSIGNED.toChars();
>> +    }
>> +
>> +
>> +
>> +    public String toChars() {
>> +        switch (this) {
>> +            case DENY_ALL:
>> +                return " DENY_ALL";
>> +            case DENY_UNSIGNED:
>> +                return "DENY_UNSIGNED";
>> +            case ASK_UNSIGNED:
>> +                return "ASK_UNSIGNED";
>> +            case ALLOW_UNSIGNED:
>> +                return "ALLOW_UNSIGNED";
>> +        }
>> +        throw new RuntimeException("Unknown AppletSecurityLevel");
>> +    }
>> +
>> +    public String toExplanation() {
>> +        switch (this) {
>> +            case DENY_ALL:
>> +                return 
>> Translator.R("APPEXTSECappletSecurityLevelExtraHighId")+" - 
>> "+Translator.R("APPEXTSECappletSecurityLevelExtraHighExplanation");
>> +            case DENY_UNSIGNED:
>> +                return 
>> Translator.R("APPEXTSECappletSecurityLevelVeryHighId")+" - 
>> "+Translator.R("APPEXTSECappletSecurityLevelVeryHighExplanation");
>> +            case ASK_UNSIGNED:
>> +                return 
>> Translator.R("APPEXTSECappletSecurityLevelHighId")+" - 
>> "+Translator.R("APPEXTSECappletSecurityLevelHighExplanation");
>> +            case ALLOW_UNSIGNED:
>> +                return 
>> Translator.R("APPEXTSECappletSecurityLevelLowId")+" - 
>> "+Translator.R("APPEXTSECappletSecurityLevelLowExplanation");
>> +        }
>> +        throw new RuntimeException("Unknown AppletSecurityLevel");
>> +    }
>> +
>> +    public static AppletSecurityLevel fromString(String s) {
>> +        if (s.trim().equalsIgnoreCase("DENY_ALL")) {
>> +            return AppletSecurityLevel.DENY_ALL;
>> +        } else if (s.trim().equalsIgnoreCase("DENY_UNSIGNED")) {
>> +            return AppletSecurityLevel.DENY_UNSIGNED;
>> +        } else if (s.trim().equalsIgnoreCase("ASK_UNSIGNED")) {
>> +            return AppletSecurityLevel.ASK_UNSIGNED;
>> +        } else if (s.trim().equalsIgnoreCase("ALLOW_UNSIGNED")) {
>> +            return AppletSecurityLevel.ALLOW_UNSIGNED;
>> +        } else {
>> +            throw new RuntimeException("Unknown AppletSecurityLevel 
>> for " + s);
>> +        }
>> +    }
>> +
>> +    @Override
>> +    public String toString() {
>> +        return toExplanation();
>> +    }
>> +
>> +    public static AppletSecurityLevel getDefault(){
>> +        return ASK_UNSIGNED;
>> +    }
>> +}
>> diff -r e631770d76ba 
>> netx/net/sourceforge/appletextendedsecurity/UnsignedAppletAction.java
>> --- /dev/null    Thu Jan 01 00:00:00 1970 +0000
>> +++ 
>> b/netx/net/sourceforge/appletextendedsecurity/UnsignedAppletAction.java 
>> Fri Feb 01 20:55:48 2013 +0100
>> @@ -0,0 +1,73 @@
>> +/*
>> +Copyright (C) 2013 Red Hat
>> +
>> +This program is free software; you can redistribute it and/or modify
>> +it under the terms of the GNU General Public License as published by
>> +the Free Software Foundation; either version 2 of the License, or
>> +(at your option) any later version.
>> +
>> +This program is distributed in the hope that it will be useful, but
>> +WITHOUT ANY WARRANTY; without even the implied warranty of
>> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
>> +General Public License for more details.
>> +
>> +You should have received a copy of the GNU General Public License
>> +along with this program; if not, write to the Free Software
>> +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
>> +*/
>> +
>> +package net.sourceforge.appletextendedsecurity;
>> +
>> +import net.sourceforge.jnlp.runtime.Translator;
>> +
>> +public enum UnsignedAppletAction {
>> +
>> +    ALWAYS, NEVER, YES, NO;
>> +
>> +    public String toChar() {
>> +        switch (this) {
>> +            case ALWAYS:
>> +                return "A";
>> +            case NEVER:
>> +                return "N";
>> +            case YES:
>> +                return "y";
>> +            case NO:
>> +                return "n";
>
> I don't know anymore if storing yes/no is worth the added confusion of 
> displaying them as options in icedtea-web settings (4 options for what 
> to do with an applet -- 2 of them still requiring confirmation ??)
>
>> +        }
>> +        throw new RuntimeException("Unknown UnsignedAppletAction");
>> +    }
>> +
>> +    public String toExplanation() {
>> +        switch (this) {
>> +            case ALWAYS:
>> +                return 
>> Translator.R("APPEXTSECunsignedAppletActionAlways");
>> +            case NEVER:
>> +                return 
>> Translator.R("APPEXTSECunsignedAppletActionNever");
>> +            case YES:
>> +                return 
>> Translator.R("APPEXTSECunsignedAppletActionYes");
>> +            case NO:
>> +                return Translator.R("APPEXTSECunsignedAppletActionNo");
>> +        }
>> +        throw new RuntimeException("Unknown UnsignedAppletAction");
>> +    }
>> +
>> +    public static UnsignedAppletAction fromString(String s) {
>> +        if (s.startsWith("A")) {
>> +            return UnsignedAppletAction.ALWAYS;
>> +        } else if (s.startsWith("N")) {
>> +            return UnsignedAppletAction.NEVER;
>> +        } else if (s.startsWith("y")) {
>> +            return UnsignedAppletAction.YES;
>> +        } else if (s.startsWith("n")) {
>> +            return UnsignedAppletAction.NO;
>> +        } else {
>> +            throw new RuntimeException("Unknown UnsignedAppletAction 
>> for " + s);
>> +        }
>> +    }
>> +
>> +    @Override
>> +    public String toString() {
>> +        return toChar() + " - " + toExplanation();
>
> Drop this toChar() IMO.
>
>> +    }
>> +}
>> diff -r e631770d76ba 
>> netx/net/sourceforge/appletextendedsecurity/UnsignedAppletActionEntry.java
>> --- /dev/null    Thu Jan 01 00:00:00 1970 +0000
>> +++ 
>> b/netx/net/sourceforge/appletextendedsecurity/UnsignedAppletActionEntry.java 
>>  Fri Feb 01 20:55:48 2013 +0100
>> @@ -0,0 +1,169 @@
>> +/*
>> +Copyright (C) 2013 Red Hat
>> +
>> +This program is free software; you can redistribute it and/or modify
>> +it under the terms of the GNU General Public License as published by
>> +the Free Software Foundation; either version 2 of the License, or
>> +(at your option) any later version.
>> +
>> +This program is distributed in the hope that it will be useful, but
>> +WITHOUT ANY WARRANTY; without even the implied warranty of
>> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
>> +General Public License for more details.
>> +
>> +You should have received a copy of the GNU General Public License
>> +along with this program; if not, write to the Free Software
>> +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
>> +*/
>> +package net.sourceforge.appletextendedsecurity;
>> +
>> +import java.io.IOException;
>> +import java.io.Writer;
>> +import java.util.ArrayList;
>> +import java.util.Date;
>> +import java.util.List;
>> +
>> +public class UnsignedAppletActionEntry {
>> +
>> +
>> +
>> +    private UnsignedAppletAction unsignedAppletAction;
>> +    private Date timeStamp;
>> +    private UrlRegEx documentBase;
>> +    private UrlRegEx codeBase;
>> +    private String mainClass;
>> +    private List<String> archives;
>> +
>> +
>> +    public static UnsignedAppletActionEntry createFromString(String 
>> s) {
>> +        String[] split = s.split("\\s+");
>> +        UnsignedAppletActionEntry nw  = new UnsignedAppletActionEntry(
>> +                UnsignedAppletAction.fromString(split[0]),
>> +                new Date(new Long(split[1])),
>> +                new UrlRegEx(split[2]),
>> +                null,
>> +                null,
>> +                null
>> +                );
>> +        if (split.length>3){
>> +            nw.setCodeBase(new UrlRegEx(split[3]));
>> +        }
>
> Please space this out eg 'if (split.length > 3)'
>
>> +        if (split.length>4){
>> +            nw.setMainClass(split[4]);
>> +        }
>> +        if (split.length>5){
>> + 
>> nw.setArchives(createArchivesList(s.substring(s.indexOf(split[4])+split[4].length()).trim()));
>> +        }
>> +        return nw;
>> +    }
>> +
>> +    public UnsignedAppletActionEntry(UnsignedAppletAction 
>> unsignedAppletAction, Date timeStamp, UrlRegEx documentBase, UrlRegEx 
>> codeBase, String mainClass, List<String> archives) {
>> +        this.unsignedAppletAction = unsignedAppletAction;
>> +        this.timeStamp = timeStamp;
>> +        this.documentBase = documentBase;
>> +        this.codeBase = codeBase;
>> +        this.mainClass = mainClass;
>> +        this.archives = archives;
>> +
>> +    }
>> +
>> +
>> +
>> +    @Override
>> +    public String toString() {
>> +        return unsignedAppletAction.toChar() +
>> +                " " + ((timeStamp == null)?"1":timeStamp.getTime()) +
>> +                " " + ((documentBase == 
>> null)?"":documentBase.getRegEx()) +
>> +                " " + ((codeBase == null)?"":codeBase.getRegEx()) +
>> +                " " + ((mainClass == null)?"":mainClass) +
>> +                " " + createArchivesString(archives);
>
> Space this out too. I don't understand though why documentBase or 
> codeBase would ever be null -- isn't a UrlRegEx of '.*' more 
> appropriate ?
>
>> +
>> +    }
>> +
>> +    public void write(Writer bw) throws IOException {
>> +        bw.write(this.toString());
>> +    }
>> +
>> +     public Date getTimeStamp() {
>> +        return timeStamp;
>> +    }
>> +
>> +    public UrlRegEx getDocumentBase() {
>> +        return documentBase;
>> +    }
>> +
>> +    public void setTimeStamp(Date timeStamp) {
>> +        this.timeStamp = timeStamp;
>> +    }
>> +
>> +    public void setDocumentBase(UrlRegEx documentBase) {
>> +        this.documentBase = documentBase;
>> +    }
>> +
>> +    public UnsignedAppletAction getUnsignedAppletAction() {
>> +        return unsignedAppletAction;
>> +    }
>> +
>> +    public void setUnsignedAppletAction(UnsignedAppletAction 
>> unsignedAppletAction) {
>> +        this.unsignedAppletAction = unsignedAppletAction;
>> +    }
>> +
>> +    public UrlRegEx getCodeBase() {
>> +        return codeBase;
>> +    }
>> +
>> +    public void setCodeBase(UrlRegEx codeBase) {
>> +        this.codeBase = codeBase;
>> +    }
>> +
>> +    public String getMainClass() {
>> +        return mainClass;
>> +    }
>> +
>> +    public void setMainClass(String mainClass) {
>> +        this.mainClass = mainClass;
>> +    }
>> +
>> +    public List<String> getArchives() {
>> +        return archives;
>> +    }
>> +
>> +    public void setArchives(List<String> archives) {
>> +        this.archives = archives;
>> +    }
>> +
>> +    public  static String createArchivesString(List<String> 
>> listOfArchives) {
>> +        if (listOfArchives == null){
>> +            return "";
>> +        }
>> +        StringBuilder sb = new StringBuilder();
>> +        for (int i = 0; i < listOfArchives.size(); i++) {
>> +            String string = listOfArchives.get(i);
>> +            if (string.trim().isEmpty()){
>> +                continue;
>> +            }
>> +            sb.append(string).append(";");
>> +        }
>> +        return sb.toString();
>> +    }
>> +    public  static List<String> createArchivesList(String 
>> semicolonedArchives) {
>> +        if (semicolonedArchives == null) return null;
>> +        if (semicolonedArchives.trim().isEmpty()) return null;
>> +        String[] items = semicolonedArchives.trim().split(";");
>> +        List<String> r = new ArrayList<String>(items.length);
>> +        for (int i = 0; i < items.length; i++) {
>> +            String string = items[i];
>> +            if (string.trim().isEmpty()){
>> +                continue;
>> +            }
>> +            r.add(string);
>> +
>> +        }
>> +        return r;
>> +
>> +    }
>> +
>> +
>> +
>> +
>> +}
>> diff -r e631770d76ba 
>> netx/net/sourceforge/appletextendedsecurity/UnsignedAppletActionStorage.java
>> --- /dev/null    Thu Jan 01 00:00:00 1970 +0000
>> +++ 
>> b/netx/net/sourceforge/appletextendedsecurity/UnsignedAppletActionStorage.java 
>>  Fri Feb 01 20:55:48 2013 +0100
>> @@ -0,0 +1,106 @@
>> +/*
>> +Copyright (C) 2013 Red Hat
>> +
>> +This program is free software; you can redistribute it and/or modify
>> +it under the terms of the GNU General Public License as published by
>> +the Free Software Foundation; either version 2 of the License, or
>> +(at your option) any later version.
>> +
>> +This program is distributed in the hope that it will be useful, but
>> +WITHOUT ANY WARRANTY; without even the implied warranty of
>> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
>> +General Public License for more details.
>> +
>> +You should have received a copy of the GNU General Public License
>> +along with this program; if not, write to the Free Software
>> +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
>> +*//*
>> +Copyright (C) 2013 Red Hat
>> +
>> +This program is free software; you can redistribute it and/or modify
>> +it under the terms of the GNU General Public License as published by
>> +the Free Software Foundation; either version 2 of the License, or
>> +(at your option) any later version.
>> +
>> +This program is distributed in the hope that it will be useful, but
>> +WITHOUT ANY WARRANTY; without even the implied warranty of
>> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
>> +General Public License for more details.
>> +
>> +You should have received a copy of the GNU General Public License
>> +along with this program; if not, write to the Free Software
>> +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
>> +*/
>> +package net.sourceforge.appletextendedsecurity;
>> +
>> +import java.util.List;
>> +
>> +/**
>> + *
>> + * @author jvanek
>
> Are we allowing authors now ? :-)
>
>> + */
>> +public interface UnsignedAppletActionStorage {
>> +
>> +
>> +    /**
>> +     * This methods iterates through record in 
>> DeploymentConfiguration.getAppletTrustSettingsPath(),
>> +     * and is mathing regexes saved here against params. so 
>> parameters here are NOR tegexes,
>> +     * but are matched against saved regexes
>> +     *
>> +     * Null or empty values are dangerously ignored, user, be aware 
>> of it.
>> +     * eg:
>> +     * match only codeBase will be null someCodeBase null null
>> +     * match only documentBase will be someDocBase null null null
>> +     * match only applet not regarding code or document base will be 
>> null null mainClas archives
>> +     * @param documentBase
>> +     * @param codeBase
>> +     * @param mainClass
>> +     * @param archives
>> +     * @return
>> +     */
>> +    public UnsignedAppletActionEntry getMatchingItem(String 
>> documentBase, String codeBase, String mainClass, List<String> archives);
>> +    /**
>> +     * Shortcut getMatchingItem(documentBase, null,null,null)
>> +     * @param documentBase
>> +     * @return
>> +     */
>> +    public UnsignedAppletActionEntry 
>> getMatchingItemByDocumentBase(String documentBase);
>> +    /**
>> +     * Shortcut getMatchingItem(null, codeBase,null,null)
>> +     * @param codeBase
>> +     * @return
>> +     */
>> +    public UnsignedAppletActionEntry 
>> getMatchingItemByCodeBase(String codeBase);
>> +    /**
>> +     * Shortcut getMatchingItem(documentBase, codeBase,null,null)
>> +     * @param documentBase
>> +     * @param  codeBase
>> +     * @return
>> +     */
>> +    public UnsignedAppletActionEntry getMatchingItemByBases(String 
>> documentBase, String codeBase);
>> +
>> +    /**
>> +     * Will add new record. Note that regexes are stored for bases 
>> matching.
>> +     *
>> +     * eg UnsignedAppletActionEntry which will dany some applet no 
>> metter of page will be
>> +     * new UnsignedAppletActionEntry(UnsignedAppletAction.NEVER, new 
>> Date(), null, null, someMain, someArchives)
>> +     *
>> +     * eg UnsignedAppletActionEntry which will allow all applets on 
>> page with same codebase will be
>> +     * new UnsignedAppletActionEntry(UnsignedAppletAction.NEVER, new 
>> Date(), ".*", ".*", null, null);
>> +     *
>> +     * @param item
>> +     */
>> +    public void add(final UnsignedAppletActionEntry item);
>> +
>> +    /**
>> +     * Will replace (current impl is matching  by  object's hashcode
>> +     * This is not reloading the list(but still saving after), so 
>> StorageIoEception
>> +     * can be thrown if it was not loaded before.
>> +     *
>> +     * Imho this should be used only to actualise timestamps or 
>> change UnsignedAppletAction
>> +     * @param item
>> +     */
>> +    public void update(final UnsignedAppletActionEntry item);
>> +
>> +
>> +}
>> diff -r e631770d76ba 
>> netx/net/sourceforge/appletextendedsecurity/UrlRegEx.java
>> --- /dev/null    Thu Jan 01 00:00:00 1970 +0000
>> +++ b/netx/net/sourceforge/appletextendedsecurity/UrlRegEx.java Fri 
>> Feb 01 20:55:48 2013 +0100
>> @@ -0,0 +1,47 @@
>> +/*
>> +Copyright (C) 2013 Red Hat
>> +
>> +This program is free software; you can redistribute it and/or modify
>> +it under the terms of the GNU General Public License as published by
>> +the Free Software Foundation; either version 2 of the License, or
>> +(at your option) any later version.
>> +
>> +This program is distributed in the hope that it will be useful, but
>> +WITHOUT ANY WARRANTY; without even the implied warranty of
>> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
>> +General Public License for more details.
>> +
>> +You should have received a copy of the GNU General Public License
>> +along with this program; if not, write to the Free Software
>> +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
>> +*/
>> +
>> +package net.sourceforge.appletextendedsecurity;
>> +
>> +public class UrlRegEx {
>> +
>> +    String regEx;
>> +    //cache pattern during each set and init?
>> +    //Pattern p
>
> Drop this comment before pushing (implementation is fine as-is).
>
>> +
>> +    public UrlRegEx(String s) {
>> +        regEx = s;
>> +    }
>> +
>> +    @Override
>> +    public String toString() {
>> +        return getRegEx();
>> +    }
>> +
>> +    public String getRegEx() {
>> +        return regEx;
>> +    }
>> +
>> +    public String getFilteredRegEx() {
>> +        return regEx.replaceAll("\\\\Q", "").replaceAll("\\\\E", "");
>> +    }
>> +
>> +    public void setRegEx(String regEx) {
>> +        this.regEx = regEx;
>> +    }
>> +}
>> diff -r e631770d76ba 
>> netx/net/sourceforge/appletextendedsecurity/impl/UnsignedAppletActionStorageImpl.java
>> --- /dev/null    Thu Jan 01 00:00:00 1970 +0000
>> +++ 
>> b/netx/net/sourceforge/appletextendedsecurity/impl/UnsignedAppletActionStorageImpl.java 
>>  Fri Feb 01 20:55:48 2013 +0100
>> @@ -0,0 +1,220 @@
>> +/*
>> +Copyright (C) 2013 Red Hat
>> +
>> +This program is free software; you can redistribute it and/or modify
>> +it under the terms of the GNU General Public License as published by
>> +the Free Software Foundation; either version 2 of the License, or
>> +(at your option) any later version.
>> +
>> +This program is distributed in the hope that it will be useful, but
>> +WITHOUT ANY WARRANTY; without even the implied warranty of
>> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
>> +General Public License for more details.
>> +
>> +You should have received a copy of the GNU General Public License
>> +along with this program; if not, write to the Free Software
>> +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
>> +*/
>> +
>> +package net.sourceforge.appletextendedsecurity.impl;
>> +
>> +import java.io.BufferedWriter;
>> +import java.io.File;
>> +import java.io.IOException;
>> +import java.util.ArrayList;
>> +import java.util.Collections;
>> +import java.util.List;
>> +import 
>> net.sourceforge.appletextendedsecurity.UnsignedAppletActionEntry;
>> +import 
>> net.sourceforge.appletextendedsecurity.UnsignedAppletActionStorage;
>> +import net.sourceforge.jnlp.util.lockingfile.LockingReaderWriter;
>> +import net.sourceforge.jnlp.util.lockingfile.StorageIoException;
>> +
>> +public class UnsignedAppletActionStorageImpl extends 
>> LockingReaderWriter implements UnsignedAppletActionStorage{
>> +
>> +    protected List<UnsignedAppletActionEntry> items;
>> +
>> +    public UnsignedAppletActionStorageImpl(String location) {
>> +        this(new File(location));
>> +    }
>> +
>> +    public UnsignedAppletActionStorageImpl(File location) {
>> +        super(location);
>> +    }
>> +
>> +    @Override
>> +    public void writeContents() throws IOException {
>> +        super.writeContents();
>> +    }
>> +
>> +    @Override
>> +    public synchronized void writeContentsLocked() throws IOException {
>> +        super.writeContentsLocked();
>> +    }
>> +
>> +
>> +
>> +
>> +
>> +    @Override
>> +    protected void readContents() throws IOException {
>> +        if (items == null) {
>> +            items = new ArrayList<UnsignedAppletActionEntry>();
>> +        } else {
>> +            items.clear();
>> +        }
>> +        super.readContents();
>> +    }
>> +
>> +    @Override
>> +    protected void readLine(String line) {
>> +        if (line.trim().length() != 0) {
>> + this.items.add(UnsignedAppletActionEntry.createFromString(line));
>> +        }
>> +    }
>> +
>> +    @Override
>> +    public void writeContent(BufferedWriter bw) throws IOException {
>> +        for (UnsignedAppletActionEntry item : items) {
>> +            item.write(bw);
>> +            bw.newLine();
>> +        }
>> +    }
>> +
>> +    @Override
>> +    public void add(final UnsignedAppletActionEntry item) {
>> +        doLocked(new Runnable() {
>> +
>> +            @Override
>> +            public void run() {
>> +                try {
>> +                    readContents();
>> +                    items.add(item);
>> +                    writeContents();
>> +                } catch (IOException ex) {
>> +                    throw new StorageIoException(ex);
>> +                }
>> +            }
>> +        });
>> +    }
>> +
>> +
>> +        @Override
>> +    public void update(final UnsignedAppletActionEntry item) {
>> +        doLocked(new Runnable() {
>> +
>> +            @Override
>> +            public void run() {
>> +                try {
>> +                    if (items == null){
>> +                        throw new StorageIoException("Storage is not 
>> initialised, can not update");
>> +                    }
>> +                    if (!items.contains(item)){
>> +                        throw new StorageIoException("Storage do not 
>> contains item you are updateing. can not update");
>> +                    }
>> +                    writeContents();
>> +                } catch (IOException ex) {
>> +                    throw new StorageIoException(ex);
>> +                }
>> +            }
>> +        });
>> +    }
>> +
>> +
>> +
>> +    @Override
>> +    public UnsignedAppletActionEntry getMatchingItem(String 
>> documentBase, String codeBase, String mainClass, List<String> 
>> archives) {
>> +        List<UnsignedAppletActionEntry> result = 
>> getMatchingItems(documentBase, codeBase, mainClass, archives);
>> +        if (result == null || result.isEmpty()) {
>> +            return null;
>> +        }
>> +        //returns first item
>> +        return result.get(0);
>> +        //Or longest match??
>> +    }
>> +
>> +    public List<UnsignedAppletActionEntry> getMatchingItems(String 
>> documentBase, String codeBase, String mainClass, List<String> 
>> archives ) {
>> +        List<UnsignedAppletActionEntry> result = new ArrayList();
>> +        lock();
>> +        try {
>> +            readContents();
>> +            if (items == null) {
>> +                return result;
>> +            }
>> +            for (UnsignedAppletActionEntry unsignedAppletActionEntry 
>> : items) {
>> +                if 
>> (isMatching(unsignedAppletActionEntry,documentBase,codeBase,mainClass,archives)){
>> +                    result.add(unsignedAppletActionEntry);
>> +                }
>> +            }
>> +            return null;
>> +        } catch (IOException e) {
>> +            throw new StorageIoException(e);
>> +        } finally {
>> +            unlock();
>> +            return result;
>> +        }
>> +    }
>> +
>> +    private boolean isMatching(UnsignedAppletActionEntry 
>> unsignedAppletActionEntry, String documentBase, String codeBase, 
>> String mainClass, List<String> archives) {
>> +    boolean result = true;
>> +        if (documentBase != null && !documentBase.trim().isEmpty()) {
>> +            result = result && 
>> documentBase.matches(unsignedAppletActionEntry.getDocumentBase().getRegEx());
>> +        }
>> +        if (codeBase != null && !codeBase.trim().isEmpty()) {
>> +            result = result && 
>> codeBase.matches(unsignedAppletActionEntry.getCodeBase().getRegEx());
>> +        }
>> +        if (mainClass != null && !mainClass.trim().isEmpty()) {
>> +            result = result && 
>> mainClass.equals(unsignedAppletActionEntry.getMainClass());
>> +        }
>> +
>> +        if (archives != null) {
>> +            result = result && comapreArchives(archives, 
>> unsignedAppletActionEntry.getArchives());
>> +        }
>> +        return result;
>> +    }
>> +
>> +    @Override
>> +    public String toString() {
>> +        return getBackingFile()+" "+super.toString();
>> +    }
>> +
>> +    private boolean comapreArchives(List<String> archives, 
>> List<String> saved) {
>> +        if (archives.size()!=saved.size()) return false;
>> +        Collections.sort(archives);
>> +        Collections.sort(saved);
>> +        for (int i = 0; i < saved.size(); i++) {
>> +            String string1 = saved.get(i);
>> +            String string2 = archives.get(i);
>> +            //intentional reference compare
>> +            if (string1==string2) {
>> +                continue;
>> +            }
>> +            if (string1 == null || string2 == null){
>> +                return false;
>> +            }
>> +            if (string1.trim().equals(string2.trim())){
>> +                continue;
>> +            }
>> +            return false;
>> +        }
>> +        return true;
>> +    }
>> +
>> +    @Override
>> +    public UnsignedAppletActionEntry 
>> getMatchingItemByDocumentBase(String documentBase) {
>> +        return getMatchingItem(documentBase, null, null, null);
>> +    }
>> +
>> +    @Override
>> +    public UnsignedAppletActionEntry 
>> getMatchingItemByCodeBase(String codeBase) {
>> +        return getMatchingItem(null, codeBase, null, null);
>> +    }
>> +
>> +    @Override
>> +    public UnsignedAppletActionEntry getMatchingItemByBases(String 
>> documentBase, String codeBase) {
>> +        return getMatchingItem(documentBase, codeBase, null, null);
>> +    }
>> +
>> +
>> +
>> +
>> +}
>> diff -r e631770d76ba 
>> netx/net/sourceforge/appletextendedsecurity/impl/UnsignedAppletActionStorageOperator.java
>> --- /dev/null    Thu Jan 01 00:00:00 1970 +0000
>> +++ 
>> b/netx/net/sourceforge/appletextendedsecurity/impl/UnsignedAppletActionStorageOperator.java 
>>  Fri Feb 01 20:55:48 2013 +0100
>> @@ -0,0 +1,180 @@
>> +/*
>> +Copyright (C) 2013 Red Hat
>> +
>> +This program is free software; you can redistribute it and/or modify
>> +it under the terms of the GNU General Public License as published by
>> +the Free Software Foundation; either version 2 of the License, or
>> +(at your option) any later version.
>> +
>> +This program is distributed in the hope that it will be useful, but
>> +WITHOUT ANY WARRANTY; without even the implied warranty of
>> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
>> +General Public License for more details.
>> +
>> +You should have received a copy of the GNU General Public License
>> +along with this program; if not, write to the Free Software
>> +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
>> +*/
>> +
>> +package net.sourceforge.appletextendedsecurity.impl;
>> +
>> +import java.io.File;
>> +import java.io.IOException;
>> +import java.util.Date;
>> +import net.sourceforge.appletextendedsecurity.UnsignedAppletAction;
>> +import 
>> net.sourceforge.appletextendedsecurity.UnsignedAppletActionEntry;
>> +import net.sourceforge.appletextendedsecurity.UrlRegEx;
>> +import net.sourceforge.jnlp.util.lockingfile.StorageIoException;
>> +
>> +public class UnsignedAppletActionStorageOperator extends 
>> UnsignedAppletActionStorageImpl {
>> +
>> +
>> +    public UnsignedAppletActionStorageOperator(String location) {
>> +        this(new File(location));
>> +    }
>> +
>> +    public UnsignedAppletActionStorageOperator(File location) {
>> +        super(location);
>> +    }
>> +
>> +    public UnsignedAppletActionEntry[] toArray() {
>> +        lock();
>> +        try {
>> +            readContents();
>> +            return items.toArray(new 
>> UnsignedAppletActionEntry[items.size()]);
>> +        } catch (IOException e) {
>> +            throw new StorageIoException(e);
>> +        } finally {
>> +            unlock();
>> +        }
>> +    }
>> +
>> +    public void clear() {
>> +        doLocked(new Runnable() {
>> +
>> +            public void run() {
>> +                try {
>> +                    items.clear();
>> +                    writeContents();
>> +                } catch (IOException e) {
>> +                    throw new StorageIoException(e);
>> +                }
>> +            }
>> +        });
>> +    }
>> +
>> +    public void removeByBehaviour(final UnsignedAppletAction 
>> unsignedAppletAction) {
>> +        doLocked(new Runnable() {
>> +
>> +            public void run() {
>> +                try {
>> +                    readContents();
>> +                    for (int i = 0; i < items.size(); i++) {
>> +                        UnsignedAppletActionEntry 
>> unsignedAppletActionEntry = items.get(i);
>> +                        if 
>> (unsignedAppletActionEntry.getUnsignedAppletAction() == 
>> unsignedAppletAction) {
>> +                            items.remove(i);
>> +                            i--;
>> +                        }
>> +
>> +                    }
>> +                    writeContents();
>> +                } catch (IOException e) {
>> +                    throw new StorageIoException(e);
>> +                }
>> +            }
>> +        });
>> +    }
>> +
>> +    private void swap(final int i, final int ii) {
>> +        doLocked(new Runnable() {
>> +
>> +            public void run() {
>> +                try {
>> +                    readContents();
>> +                    UnsignedAppletActionEntry backup = items.get(i);
>> +                    items.set(i, items.get(ii));
>> +                    items.set(ii, backup);
>> +                    writeContents();
>> +                } catch (IOException e) {
>> +                    throw new StorageIoException(e);
>> +                }
>> +            }
>> +        });
>> +
>> +    }
>> +
>> +    public void moveUp(int selectedRow) {
>> +        if (selectedRow <= 0) {
>> +            return;
>> +        }
>> +        swap(selectedRow, selectedRow - 1);
>> +    }
>> +
>> +    public void moveDown(int selectedRow) {
>> +        if (selectedRow >= items.size() - 1) {
>> +            return;
>> +        }
>> +        swap(selectedRow, selectedRow + 1);
>> +    }
>> +
>> +    public void remove(final int item) {
>> +        doLocked(new Runnable() {
>> +
>> +            public void run() {
>> +                try {
>> +                    readContents();
>> +                    items.remove(item);
>> +                    writeContents();
>> +                } catch (IOException ex) {
>> +                    throw new StorageIoException(ex);
>> +                }
>> +            }
>> +        });
>> +    }
>> +
>> +    public void modify(final UnsignedAppletActionEntry source, final 
>> int columnIndex, final Object aValue)  {
>> +         Runnable r = new Runnable() {
>> +
>> +            public void run() {
>> +
>> +                try {
>> +                    if (!items.contains(source)){
>> +                        throw new StorageIoException("Item to be 
>> modified not found in storage");
>> +                    }
>> +
>> +                    if (columnIndex == 0) {
>> + source.setUnsignedAppletAction((UnsignedAppletAction) aValue);
>> +                    }
>> +                    if (columnIndex == 1) {
>> +                        source.setTimeStamp((Date) aValue);
>> +                    }
>> +                    if (columnIndex == 2) {
>> +                        source.setDocumentBase(new UrlRegEx((String) 
>> aValue));
>> +                    }
>> +                    if (columnIndex == 3) {
>> +                        source.setCodeBase(new UrlRegEx((String) 
>> aValue));
>> +                    }
>> +                    if (columnIndex == 4) {
>> +                        source.setMainClass((String) aValue);
>> +                    }
>> +                    if (columnIndex == 5) {
>> + 
>> source.setArchives(UnsignedAppletActionEntry.createArchivesList((String) 
>> aValue));
>> +                    }
>> +
>> +                    writeContents();
>> +                } catch (IOException ex) {
>> +                    throw new StorageIoException(ex);
>> +                }
>> +            }
>> +        };
>> +        doLocked(r);
>> +
>> +    }
>> +
>> +    @Override
>> +    public synchronized void writeContentsLocked() throws IOException {
>> +        super.writeContentsLocked();
>> +    }
>> +
>> +
>> +}
>> diff -r e631770d76ba netx/net/sourceforge/jnlp/config/Defaults.java
>> --- a/netx/net/sourceforge/jnlp/config/Defaults.java    Thu Jan 31 
>> 11:12:35 2013 +0100
>> +++ b/netx/net/sourceforge/jnlp/config/Defaults.java    Fri Feb 01 
>> 20:55:48 2013 +0100
>> @@ -42,6 +42,7 @@
>>  import java.io.File;
>>  import java.util.HashMap;
>>  import java.util.Map;
>> +import net.sourceforge.appletextendedsecurity.AppletSecurityLevel;
>>
>>  import net.sourceforge.jnlp.ShortcutDesc;
>>  import net.sourceforge.jnlp.runtime.JNLPProxySelector;
>> @@ -384,6 +385,41 @@
>> DeploymentConfiguration.KEY_PLUGIN_JVM_ARGUMENTS,
>>                          null,
>>                          null
>> +                },
>> +               //unsigned applet security level
>> +                {
>
> Why are you not just using a set of strings with a string validator ? 
> I do not like the idea of having an anonymous class here.
>
>> + DeploymentConfiguration.KEY_SECURITY_LEVEL,
>> +                new ValueValidator() {
>> +
>> +                    @Override
>> +                    public void validate(Object value) throws 
>> IllegalArgumentException {
>> +                        if (value == null) {
>> +                            throw new 
>> IllegalArgumentException("Value can't be null");
>> +                        }
>> +                        if (value instanceof AppletSecurityLevel) {
>> +                            //??
>> +                            return;
>> +                        }
>> +                        if (!(value instanceof String)) {
>> +                            throw new 
>> IllegalArgumentException("Expected was String, was " + 
>> value.getClass());
>> +                        }
>> +                        try {
>> +                            AppletSecurityLevel validated = 
>> AppletSecurityLevel.fromString((String) value);
>> +                            if (validated == null) {
>> +                                throw new 
>> IllegalArgumentException("Result can't be null, was");
>> +                            }
>> +                            //thrown by fromString
>> +                        } catch (RuntimeException ex) {
>> +                            throw new IllegalArgumentException(ex);
>> +                        }
>> +                    }
>> +
>> +                    @Override
>> +                    public String getPossibleValues() {
>> +                        return AppletSecurityLevel.allToString();
>> +                    }
>> +                },
>> +                null
>>                  }
>>          };
>>
>> diff -r e631770d76ba 
>> netx/net/sourceforge/jnlp/config/DeploymentConfiguration.java
>> --- a/netx/net/sourceforge/jnlp/config/DeploymentConfiguration.java 
>>  Thu Jan 31 11:12:35 2013 +0100
>> +++ b/netx/net/sourceforge/jnlp/config/DeploymentConfiguration.java 
>>  Fri Feb 01 20:55:48 2013 +0100
>> @@ -51,6 +51,7 @@
>>      public static final String DEPLOYMENT_DIR = ".icedtea";
>>      public static final String DEPLOYMENT_CONFIG = "deployment.config";
>>      public static final String DEPLOYMENT_PROPERTIES = 
>> "deployment.properties";
>> +    public static final String APPLET_TRUST_SETTINGS = 
>> ".appletTrustSettings";
>>
>>      public static final String DEPLOYMENT_COMMENT = "Netx deployment 
>> configuration";
>>
>> @@ -105,6 +106,9 @@
>>      /** Boolean. Only show security prompts to user if true */
>>      public static final String KEY_SECURITY_PROMPT_USER = 
>> "deployment.security.askgrantdialog.show";
>>
>> +    //enum of AppletSecurityLevel in result
>> +    public static final String KEY_SECURITY_LEVEL = 
>> "deployment.security.level";
>> +
>>      public static final String KEY_SECURITY_TRUSTED_POLICY = 
>> "deployment.security.trusted.policy";
>>
>>      /** Boolean. Only give 
>> AWTPermission("showWindowWithoutWarningBanner") if true */
>> @@ -196,6 +200,17 @@
>>          load(true);
>>      }
>>
>> +    public static File getAppletTrustCustomSettingsPath() {
>> +        return new File(System.getProperty("user.home") + 
>> File.separator + DEPLOYMENT_DIR
>> +                + File.separator + APPLET_TRUST_SETTINGS);
>> +    }
>> +
>> +     public static File getAppletTrustGlobalSettingsPath() {
>> +       return new File(File.separator + "etc" + File.separator + 
>> ".java" + File.separator
>> +                + "deployment" + File.separator + 
>> APPLET_TRUST_SETTINGS);
>> +
>> +    }
>> +
>>      /**
>>       * Initialize this deployment configuration by reading 
>> configuration files.
>>       * Generally, it will try to continue and ignore errors it finds 
>> (such as file not found).
>> diff -r e631770d76ba 
>> netx/net/sourceforge/jnlp/controlpanel/ControlPanel.java
>> --- a/netx/net/sourceforge/jnlp/controlpanel/ControlPanel.java Thu 
>> Jan 31 11:12:35 2013 +0100
>> +++ b/netx/net/sourceforge/jnlp/controlpanel/ControlPanel.java Fri 
>> Feb 01 20:55:48 2013 +0100
>> @@ -41,6 +41,7 @@
>>  import javax.swing.JFrame;
>>  import javax.swing.JLabel;
>>  import javax.swing.JList;
>> +import javax.swing.JOptionPane;
>>  import javax.swing.JPanel;
>>  import javax.swing.JScrollPane;
>>  import javax.swing.SwingConstants;
>> @@ -230,7 +231,10 @@
>>                  new SettingsPanel(Translator.R("CPTabNetwork"), 
>> createNetworkSettingsPanel()),
>>                  // TODO: This is commented out since this is not 
>> implemented yet
>>                  // new SettingsPanel(Translator.R("CPTabRuntimes"), 
>> createRuntimesSettingsPanel()),
>> -                new SettingsPanel(Translator.R("CPTabSecurity"), 
>> createSecuritySettingsPanel()), };
>> +                new SettingsPanel(Translator.R("CPTabSecurity"), 
>> createSecuritySettingsPanel()),
>> +                //todo refactor to work with tmp file and apply as 
>> asu designed it
>> +                new 
>> SettingsPanel(Translator.R("APPEXTSECControlPanelExtendedAppletSecurityTitle"), 
>> new 
>> UnsignedAppletsTrustingListPanel(DeploymentConfiguration.getAppletTrustGlobalSettingsPath(),DeploymentConfiguration.getAppletTrustCustomSettingsPath(), 
>> this.config) )
>> +        };
>>
>>          // Add panels.
>>          final JPanel settingsPanel = new JPanel(new CardLayout());
>> @@ -360,6 +364,7 @@
>>              config.save();
>>          } catch (IOException e) {
>>              e.printStackTrace();
>> +            JOptionPane.showMessageDialog(this, e);
>>          }
>>      }
>>
>> diff -r e631770d76ba 
>> netx/net/sourceforge/jnlp/controlpanel/UnsignedAppletActionTableModel.java
>> --- /dev/null    Thu Jan 01 00:00:00 1970 +0000
>> +++ 
>> b/netx/net/sourceforge/jnlp/controlpanel/UnsignedAppletActionTableModel.java 
>>  Fri Feb 01 20:55:48 2013 +0100
>> @@ -0,0 +1,168 @@
>> +/*
>> +Copyright (C) 2013 Red Hat
>> +
>> +This program is free software; you can redistribute it and/or modify
>> +it under the terms of the GNU General Public License as published by
>> +the Free Software Foundation; either version 2 of the License, or
>> +(at your option) any later version.
>> +
>> +This program is distributed in the hope that it will be useful, but
>> +WITHOUT ANY WARRANTY; without even the implied warranty of
>> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
>> +General Public License for more details.
>> +
>> +You should have received a copy of the GNU General Public License
>> +along with this program; if not, write to the Free Software
>> +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
>> +*/
>> +
>> +package net.sourceforge.jnlp.controlpanel;
>> +
>> +import java.util.Date;
>> +import javax.swing.table.AbstractTableModel;
>> +import net.sourceforge.appletextendedsecurity.UnsignedAppletAction;
>> +import 
>> net.sourceforge.appletextendedsecurity.UnsignedAppletActionEntry;
>> +import net.sourceforge.appletextendedsecurity.UrlRegEx;
>> +import 
>> net.sourceforge.appletextendedsecurity.impl.UnsignedAppletActionStorageOperator;
>> +import net.sourceforge.jnlp.runtime.Translator;
>> +
>> +public class UnsignedAppletActionTableModel extends 
>> AbstractTableModel {
>> +
>> +    final UnsignedAppletActionStorageOperator back;
>> +    private final String[] columns = new 
>> String[]{Translator.R("APPEXTSECguiTableModelTableColumnAction"),
>> + Translator.R("APPEXTSECguiTableModelTableColumnDateOfAction"),
>> + Translator.R("APPEXTSECguiTableModelTableColumnDocumentBase"),
>> + Translator.R("APPEXTSECguiTableModelTableColumnCodeBase"),
>> + Translator.R("APPEXTSECguiTableModelTableColumnMainClass"),
>> + Translator.R("APPEXTSECguiTableModelTableColumnArchives")};
>> +
>> +    public 
>> UnsignedAppletActionTableModel(UnsignedAppletActionStorageOperator 
>> back) {
>> +        this.back = back;
>> +    }
>> +
>> +    @Override
>> +    public int getRowCount() {
>> +        try {
>> +            return back.toArray().length;
>> +        } catch (Exception ex) {
>> +            throw new RuntimeException(ex);
>
> Why is this wrapping needed ?
>
>> +        }
>> +    }
>> +
>> +    @Override
>> +    public int getColumnCount() {
>> +        return columns.length;
>> +    }
>> +
>> +    @Override
>> +    public String getColumnName(int columnIndex) {
>> +        return columns[columnIndex];
>> +    }
>> +
>> +    @Override
>> +    public Class<?> getColumnClass(int columnIndex) {
>> +        if (columnIndex == 0) {
>> +            return UnsignedAppletAction.class;
>> +        }
>> +        if (columnIndex == 1) {
>> +            return Date.class;
>> +        }
>> +        if (columnIndex == 2) {
>> +            return UrlRegEx.class;
>> +        }
>> +        if (columnIndex == 3) {
>> +            return UrlRegEx.class;
>> +        }
>> +        if (columnIndex == 3) {
>> +            return String.class;
>> +        }
>> +        if (columnIndex == 3) {
>> +            return String.class;
>> +        }
>> +        return Object.class;
>> +    }
>> +
>> +    @Override
>> +    public boolean isCellEditable(int rowIndex, int columnIndex) {
>> +        if (back.isReadOnly()){
>> +            return false;
>> +        }
>> +        if (columnIndex==1) return false;
>> +        if (columnIndex==0) return true;
>> +        if (getValueAt(rowIndex, columnIndex-1)==null || 
>> getValueAt(rowIndex, columnIndex-1).toString().trim().isEmpty()) 
>> return false;
>> +        return true;
>> +    }
>> +
>> +    @Override
>> +    public Object getValueAt(int rowIndex, int columnIndex) {
>> +
>> +        UnsignedAppletActionEntry source = back.toArray()[rowIndex];
>> +        if (columnIndex == 0) {
>> +            return source.getUnsignedAppletAction();
>> +        }
>> +        if (columnIndex == 1) {
>> +            return source.getTimeStamp();
>> +        }
>> +        if (columnIndex == 2) {
>> +            return source.getDocumentBase();
>> +        }
>> +        if (columnIndex == 3) {
>> +            return source.getCodeBase();
>> +        }
>> +        if (columnIndex == 4) {
>> +            return source.getMainClass();
>> +        }
>> +        if (columnIndex == 5) {
>> +            return 
>> UnsignedAppletActionEntry.createArchivesString(source.getArchives());
>> +        }
>> +        return null;
>> +    }
>> +
>> +    @Override
>> +    public void setValueAt(final Object aValue, final int rowIndex, 
>> final int columnIndex) {
>> +        final UnsignedAppletActionEntry source = 
>> back.toArray()[rowIndex];
>> +       back.modify(source, columnIndex, aValue);
>> +
>> +    }
>> +
>> +    public void addRow() {
>> +        int i = back.toArray().length;
>> +        String s = "\\Qhttp://localhost:80/\\E.*";
>> +        back.add(new UnsignedAppletActionEntry(
>> +                UnsignedAppletAction.NEVER,
>> +                new Date(),
>> +                new UrlRegEx(s),
>> +                new UrlRegEx(s),
>> +                null,
>> +                null));
>> +        fireTableRowsInserted(i, i + 1);
>> +    }
>> +
>> +    public void removeRow(int i) {
>> +        back.remove(i);
>> +        fireTableRowsDeleted(i, i);
>> +    }
>> +
>> +    public void clear() {
>> +        int i = getRowCount();
>> +        back.clear();
>> +        fireTableRowsDeleted(0, i);
>> +    }
>> +
>> +    void removeByBehaviour(UnsignedAppletAction unsignedAppletAction) {
>> +        int i = getRowCount();
>> +        back.removeByBehaviour(unsignedAppletAction);
>> +        fireTableRowsDeleted(0, i);
>> +    }
>> +
>> +    void moveUp(int selectedRow) {
>> +        int i = getRowCount();
>> +        back.moveUp(selectedRow);
>> +        fireTableRowsUpdated(i-1, i);
>> +    }
>> +    void moveDown(int selectedRow) {
>> +        int i = getRowCount();
>> +        back.moveDown(selectedRow);
>> +        fireTableRowsUpdated(i, i+1);
>> +    }
>> +}
>> diff -r e631770d76ba 
>> netx/net/sourceforge/jnlp/controlpanel/UnsignedAppletsTrustingListPanel.java
>> --- /dev/null    Thu Jan 01 00:00:00 1970 +0000
>> +++ 
>> b/netx/net/sourceforge/jnlp/controlpanel/UnsignedAppletsTrustingListPanel.java 
>>  Fri Feb 01 20:55:48 2013 +0100
>> @@ -0,0 +1,665 @@
>> +/*
>> + Copyright (C) 2013 Red Hat
>> +
>> + This program is free software; you can redistribute it and/or modify
>> + it under the terms of the GNU General Public License as published by
>> + the Free Software Foundation; either version 2 of the License, or
>> + (at your option) any later version.
>> +
>> + This program is distributed in the hope that it will be useful, but
>> + WITHOUT ANY WARRANTY; without even the implied warranty of
>> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
>> + General Public License for more details.
>> +
>> + You should have received a copy of the GNU General Public License
>> + along with this program; if not, write to the Free Software
>> + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 
>> USA
>> + */
>> +package net.sourceforge.jnlp.controlpanel;
>> +
>> +import java.awt.BorderLayout;
>> +import java.io.BufferedWriter;
>> +import java.io.File;
>> +import java.io.FileOutputStream;
>> +import java.io.OutputStreamWriter;
>> +import java.text.DateFormat;
>> +import java.util.ArrayList;
>> +import java.util.Collections;
>> +import java.util.Date;
>> +import java.util.List;
>> +import java.util.regex.Pattern;
>> +import javax.swing.event.ChangeEvent;
>> +import javax.swing.event.ListSelectionEvent;
>> +import net.sourceforge.appletextendedsecurity.UnsignedAppletAction;
>> +import net.sourceforge.appletextendedsecurity.UrlRegEx;
>> +import javax.swing.DefaultCellEditor;
>> +import javax.swing.JComboBox;
>> +import javax.swing.JFrame;
>> +import javax.swing.JOptionPane;
>> +import javax.swing.JPanel;
>> +import javax.swing.JTable;
>> +import javax.swing.JTextField;
>> +import javax.swing.event.ChangeListener;
>> +import javax.swing.event.ListSelectionListener;
>> +import javax.swing.table.DefaultTableCellRenderer;
>> +import javax.swing.table.DefaultTableModel;
>> +import javax.swing.table.TableCellEditor;
>> +import javax.swing.table.TableCellRenderer;
>> +import javax.swing.table.TableModel;
>> +import 
>> net.sourceforge.appletextendedsecurity.UnsignedAppletActionEntry;
>> +import 
>> net.sourceforge.appletextendedsecurity.impl.UnsignedAppletActionStorageOperator;
>> +import net.sourceforge.appletextendedsecurity.AppletSecurityLevel;
>> +import net.sourceforge.jnlp.config.DeploymentConfiguration;
>> +import net.sourceforge.jnlp.runtime.Translator;
>> +
>> +public class UnsignedAppletsTrustingListPanel extends 
>> javax.swing.JPanel {
>> +
>> +    private javax.swing.JButton jButton1;
>> +    private javax.swing.JButton jButton2;
>> +    private javax.swing.JButton jButton3;
>> +    private javax.swing.JButton jButton4;
>> +    private javax.swing.JButton jButton5;
>> +    private javax.swing.JButton jButton6;
>> +    private javax.swing.JButton jButton7;
>> +    private javax.swing.JButton jButton8;
>> +    private javax.swing.JCheckBox jCheckBox1;
>> +    private javax.swing.JCheckBox jCheckBox2;
>> +    private javax.swing.JComboBox jComboBox1;
>> +    private javax.swing.JComboBox jComboBox2;
>> +    private javax.swing.JLabel jLabel1;
>> +    private javax.swing.JLabel jLabel2;
>> +    private javax.swing.JScrollPane jScrollPane1;
>> +    private javax.swing.JTabbedPane jTabPane1;
>> +    private javax.swing.JTable jTable1;
>> +    private javax.swing.JScrollPane jScrollPane2;
>> +    private javax.swing.JTable jTable2;
>
> Numbers are not an acceptable naming convention IMO. Same with the 
> numbered methods. But I think it can be fixed once in HEAD. The 
> functionality is the priority ATM.
>
>> +    private final UnsignedAppletActionStorageOperator customBackEnd;
>> +    private final UnsignedAppletActionStorageOperator globalBackEnd;
>> +    private final UnsignedAppletActionTableModel customModel;
>> +    private final UnsignedAppletActionTableModel globalModel;
>> +    private final DeploymentConfiguration conf;
>> +    private javax.swing.JTable currentTable;
>> +    private UnsignedAppletActionTableModel currentModel;
>> +    private String lastDoc;
>> +    private String lastCode;
>> +
>> +
>> +    /*
>> +     * for testing and playing
>> +     */
>> +    public static void main(String args[]) {
>> +        final String defaultDir = System.getProperty("user.home") + 
>> "/Desktop/";
>> +        final String defaultFileName1 = "terrorList1";
>> +        final String defaultFileName2 = "terrorList2";
>> +        final String defaultFile1 = defaultDir + defaultFileName1;
>> +        final String defaultFile2 = defaultDir + defaultFileName2;
>> +        java.awt.EventQueue.invokeLater(new Runnable() {
>> +            @Override
>> +            public void run() {
>> +                try {
>> +                    JFrame f = new JFrame();
>> +                    f.setSize(700, 300);
>> +                    f.setLayout(new BorderLayout());
>> + f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
>> +                    DeploymentConfiguration cc = new 
>> DeploymentConfiguration();
>> +                    cc.load();
>> +                    File ff1 = new File(defaultFile1);
>> +                    File ff2 = new File(defaultFile2);
>> +                    f.add(new UnsignedAppletsTrustingListPanel(ff2, 
>> ff1, cc));
>> +                    f.setVisible(true);
>> +                } catch (Exception ex) {
>> +                    ex.printStackTrace();
>> +                }
>> +            }
>> +        });
>> +    }
>> +
>> +    public UnsignedAppletsTrustingListPanel(File globalSettings, 
>> File customSettings, DeploymentConfiguration conf) {
>> +        customBackEnd = new 
>> UnsignedAppletActionStorageOperator(customSettings);
>> +        globalBackEnd = new 
>> UnsignedAppletActionStorageOperator(globalSettings);
>> +        customModel = new 
>> UnsignedAppletActionTableModel(customBackEnd);
>> +        globalModel = new 
>> UnsignedAppletActionTableModel(globalBackEnd);
>> +        initComponents();
>> +        this.conf = conf;
>> +        AppletSecurityLevel gs = AppletSecurityLevel.getDefault();
>> +        String s = 
>> conf.getProperty(DeploymentConfiguration.KEY_SECURITY_LEVEL);
>> +        if (s != null) {
>> +            gs = AppletSecurityLevel.fromString(s);
>> +        }
>> +        jComboBox1.setSelectedItem(gs);
>> + jTable1.getSelectionModel().addListSelectionListener(new 
>> SingleSelectionListenerImpl(jTable1));
>> + jTable2.getSelectionModel().addListSelectionListener(new 
>> SingleSelectionListenerImpl(jTable2));
>> +        currentTable = jTable1;
>> +        currentModel = customModel;
>> +        setButtons((!currentModel.back.isReadOnly()));
>> +    }
>> +
>> +    public String 
>> appletItemsToCaption(List<UnsignedAppletActionEntry> ii, String 
>> caption) {
>> +        StringBuilder sb = new StringBuilder();
>> +        for (UnsignedAppletActionEntry i : ii) {
>> +            sb.append(appletItemToCaption(i, caption)).append("\n");
>> +        }
>> +        return sb.toString();
>> +    }
>> +
>> +    public String appletItemToCaption(UnsignedAppletActionEntry i, 
>> String caption) {
>> +        return Translator.R("APPEXTSECguiPanelAppletInfoHederPart1", 
>> caption, i.getDocumentBase().getFilteredRegEx())
>> +                + "\n  (" + 
>> Translator.R("APPEXTSECguiPanelAppletInfoHederPart2", 
>> i.getUnsignedAppletAction(), 
>> DateFormat.getInstance().format(i.getTimeStamp()))
>> +                + "\n    " + 
>> Translator.R("APPEXTSECguiTableModelTableColumnDocumentBase") + ": " 
>> + i.getDocumentBase().getFilteredRegEx()
>> +                + "\n    " + 
>> Translator.R("APPEXTSECguiTableModelTableColumnCodeBase") + ": " + 
>> i.getCodeBase().getFilteredRegEx()
>> +                + "\n    " + 
>> Translator.R("APPEXTSECguiTableModelTableColumnMainClass") + ": " + 
>> ((i.getMainClass() == null) ? "" : i.getMainClass())
>> +                + "\n    " + 
>> Translator.R("APPEXTSECguiTableModelTableColumnArchives") + ": " + 
>> UnsignedAppletActionEntry.createArchivesString(i.getArchives());
>> +    }
>> +
>> +    public void removeSelectedFromTable(JTable table) {
>> +        int[] originalIndexes = table.getSelectedRows();
>> +        List<Integer> newIndexes = new 
>> ArrayList<Integer>(originalIndexes.length);
>> +        for (int i = 0; i < originalIndexes.length; i++) {
>> +            //we need to remap values first
>> +            int modelRow = 
>> table.convertRowIndexToModel(originalIndexes[i]);
>> +            newIndexes.add(modelRow);
>> +        }
>> +        //now to sort so we can incrementaly dec safely
>> +        Collections.sort(newIndexes);
>> +        if (jCheckBox1.isSelected()) {
>> +            String s = 
>> Translator.R("APPEXTSECguiPanelConfirmDeletionOf", newIndexes.size()) 
>> + ": \n";
>> +            UnsignedAppletActionEntry[] items = 
>> currentModel.back.toArray();
>> +            for (int i = 0; i < newIndexes.size(); i++) {
>> +                Integer integer = newIndexes.get(i);
>> +                s += appletItemToCaption(items[integer], "  ") + "\n";
>> +            }
>> +            int a = JOptionPane.showConfirmDialog(this, s);
>> +            if (a != JOptionPane.OK_OPTION) {
>> +                return;
>> +            }
>> +        }
>> +        int sub = 0;
>> +        for (int i = 0; i < newIndexes.size(); i++) {
>> +            Integer integer = newIndexes.get(i);
>> +            currentModel.removeRow(integer.intValue() + sub);
>> +            sub--;
>> +        }
>> +    }
>> +
>> +    public void removeAllItemsFromTable(JTable table, 
>> UnsignedAppletActionTableModel model) {
>> +        table.clearSelection();
>> +
>> +        if (jCheckBox1.isSelected()) {
>> +            UnsignedAppletActionEntry[] items = model.back.toArray();
>> +            String s = 
>> Translator.R("APPEXTSECguiPanelConfirmDeletionOf", items.length) + ": 
>> \n";
>> +            for (int i = 0; i < items.length; i++) {
>> +                s += appletItemToCaption(items[i], "  ") + "\n";
>> +            }
>> +            int a = JOptionPane.showConfirmDialog(this, s);
>> +            if (a != JOptionPane.OK_OPTION) {
>> +                return;
>> +            }
>> +        }
>> +        model.clear();
>> +    }
>> +
>> +    private void initComponents() {
>> +
>> +        jScrollPane1 = new javax.swing.JScrollPane();
>> +        jScrollPane2 = new javax.swing.JScrollPane();
>> +        jTable1 = createTbale(customModel);
>> +        jTable2 = createTbale(globalModel);
>> +        jButton1 = new javax.swing.JButton();
>> +        jComboBox1 = new JComboBox(new AppletSecurityLevel[]{
>> +                    AppletSecurityLevel.DENY_ALL,
>> +                    AppletSecurityLevel.DENY_UNSIGNED,
>> +                    AppletSecurityLevel.ASK_UNSIGNED,
>> +                    AppletSecurityLevel.ALLOW_UNSIGNED
>> +                });
>> + jComboBox1.setSelectedItem(AppletSecurityLevel.getDefault());
>> +        jLabel2 = new javax.swing.JLabel();
>> +        jLabel1 = new javax.swing.JLabel();
>> +        jComboBox2 = new javax.swing.JComboBox();
>> +        jButton2 = new javax.swing.JButton();
>> +        jButton5 = new javax.swing.JButton();
>> +        jButton3 = new javax.swing.JButton();
>> +        jButton4 = new javax.swing.JButton();
>> +        jCheckBox1 = new javax.swing.JCheckBox();
>> +        jCheckBox2 = new javax.swing.JCheckBox();
>> +        jButton6 = new javax.swing.JButton();
>> +        jButton7 = new javax.swing.JButton();
>> +        jButton8 = new javax.swing.JButton();
>> +        jTabPane1 = new javax.swing.JTabbedPane();
>> +
>> +        jScrollPane1.setViewportView(jTable1);
>> +
>> +        jScrollPane2.setViewportView(jTable2);
>> +
>> + jButton1.setText(Translator.R("APPEXTSECguiPanelHelpButton"));
>> +        jButton1.addActionListener(new 
>> java.awt.event.ActionListener() {
>> +            @Override
>> +            public void actionPerformed(java.awt.event.ActionEvent 
>> evt) {
>> +                jButton1ActionPerformed(evt);
>> +            }
>> +        });
>> +
>> +        jComboBox1.addActionListener(new 
>> java.awt.event.ActionListener() {
>> +            @Override
>> +            public void actionPerformed(java.awt.event.ActionEvent 
>> evt) {
>> +                jComboBox1ActionPerformed(evt);
>> +            }
>> +        });
>> +
>> + jLabel2.setText(Translator.R("APPEXTSECguiPanelSecurityLevel"));
>> +
>> + 
>> jLabel1.setText(Translator.R("APPEXTSECguiPanelGlobalBehaviourCaption"));
>> +
>> +        jComboBox2.setModel(new javax.swing.DefaultComboBoxModel(new 
>> String[]{
>> + Translator.R("APPEXTSECguiPanelDeleteMenuSelected"),
>> + Translator.R("APPEXTSECguiPanelDeleteMenuAllA"),
>> + Translator.R("APPEXTSECguiPanelDeleteMenuAllN"),
>> + Translator.R("APPEXTSECguiPanelDeleteMenuAlly"),
>> + Translator.R("APPEXTSECguiPanelDeleteMenuAlln"),
>> + Translator.R("APPEXTSECguiPanelDeleteMenuAllAll")}));
>> +
>> + jButton2.setText(Translator.R("APPEXTSECguiPanelDeleteButton"));
>> +        jButton2.addActionListener(new 
>> java.awt.event.ActionListener() {
>> +            @Override
>> +            public void actionPerformed(java.awt.event.ActionEvent 
>> evt) {
>> +                jButton2ActionPerformed(evt);
>> +            }
>> +        });
>> +
>> + jButton5.setText(Translator.R("APPEXTSECguiPanelTestUrlButton"));
>> +        jButton5.addActionListener(new 
>> java.awt.event.ActionListener() {
>> +            @Override
>> +            public void actionPerformed(java.awt.event.ActionEvent 
>> evt) {
>> +                jButton5ActionPerformed(evt);
>> +            }
>> +        });
>> +
>> + jButton3.setText(Translator.R("APPEXTSECguiPanelAddRowButton"));
>> +        jButton3.addActionListener(new 
>> java.awt.event.ActionListener() {
>> +            @Override
>> +            public void actionPerformed(java.awt.event.ActionEvent 
>> evt) {
>> +                jButton3ActionPerformed(evt);
>> +            }
>> +        });
>> +
>> + 
>> jButton4.setText(Translator.R("APPEXTSECguiPanelValidateTableButton"));
>> +        jButton4.addActionListener(new 
>> java.awt.event.ActionListener() {
>> +            @Override
>> +            public void actionPerformed(java.awt.event.ActionEvent 
>> evt) {
>> +                jButton4ActionPerformed(evt);
>> +            }
>> +        });
>> +
>> +        jCheckBox1.setSelected(true);
>> + 
>> jCheckBox1.setText(Translator.R("APPEXTSECguiPanelAskeforeActionBox"));
>> +
>> + jCheckBox2.setText(Translator.R("APPEXTSECguiPanelShowRegExesBox"));
>> +        jCheckBox2.addActionListener(new 
>> java.awt.event.ActionListener() {
>> +            @Override
>> +            public void actionPerformed(java.awt.event.ActionEvent 
>> evt) {
>> +                jCheckBox2ActionPerformed(evt);
>> +            }
>> +        });
>> +
>> + jButton6.setText(Translator.R("APPEXTSECguiPanelInverSelection"));
>> +        jButton6.addActionListener(new 
>> java.awt.event.ActionListener() {
>> +            @Override
>> +            public void actionPerformed(java.awt.event.ActionEvent 
>> evt) {
>> +                jButton6ActionPerformed(evt);
>> +            }
>> +        });
>> +
>> + jButton7.setText(Translator.R("APPEXTSECguiPanelMoveRowUp"));
>> +        jButton7.setEnabled(false);
>> +        jButton7.addActionListener(new 
>> java.awt.event.ActionListener() {
>> +            @Override
>> +            public void actionPerformed(java.awt.event.ActionEvent 
>> evt) {
>> +                jButton7ActionPerformed(evt);
>> +            }
>> +        });
>> +
>> + jButton8.setText(Translator.R("APPEXTSECguiPanelMoveRowDown"));
>> +        jButton8.setEnabled(false);
>> +        jButton8.addActionListener(new 
>> java.awt.event.ActionListener() {
>> +            @Override
>> +            public void actionPerformed(java.awt.event.ActionEvent 
>> evt) {
>> +                jButton8ActionPerformed(evt);
>> +            }
>> +        });
>> +
>> +        javax.swing.GroupLayout layout = new 
>> javax.swing.GroupLayout(this);
>> +        this.setLayout(layout);
>> +        layout.setHorizontalGroup(
>> + 
>> layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGroup(javax.swing.GroupLayout.Alignment.TRAILING, 
>> layout.createSequentialGroup().addContainerGap().addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING).addComponent(jTabPane1, 
>> javax.swing.GroupLayout.Alignment.LEADING, 
>> javax.swing.GroupLayout.DEFAULT_SIZE, 583, 
>> Short.MAX_VALUE).addComponent(jLabel1, 
>> javax.swing.GroupLayout.Alignment.LEADING).addGroup(javax.swing.GroupLayout.Alignment.LEADING, 
>> layout.createSequentialGroup().addComponent(jLabel2).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(jComboBox1, 
>> 0, 474, 
>> Short.MAX_VALUE)).addGroup(javax.swing.GroupLayout.Alignment.LEADING, 
>> layout.createSequentialGroup().addComponent(jButton3).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED).addComponent(jButton4).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED).addComponent(jButton5).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 
>> 94, 
>> Short.MAX_VALUE).addComponent(jButton8).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(jButton7)).addGroup(layout.createSequentialGroup().addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGroup(layout.createSequentialGroup().addComponent(jButton2).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(jComboBox2, 
>> javax.swing.GroupLayout.PREFERRED_SIZE, 
>> javax.swing.GroupLayout.DEFAULT_SIZE, 
>> javax.swing.GroupLayout.PREFERRED_SIZE).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(jButton6)).addGroup(layout.createSequentialGroup().addComponent(jCheckBox1).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(jCheckBox2))).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 
>> 93, Short.MAX_VALUE).addComponent(jButton1, 
>> javax.swing.GroupLayout.PREFERRED_SIZE, 108, 
>> javax.swing.GroupLayout.PREFERRED_SIZE))).addContainerGap()));
>> +        layout.setVerticalGroup(
>> + 
>> layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGroup(layout.createSequentialGroup().addContainerGap().addComponent(jLabel1).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addComponent(jLabel2).addComponent(jComboBox1, 
>> javax.swing.GroupLayout.PREFERRED_SIZE, 
>> javax.swing.GroupLayout.DEFAULT_SIZE, 
>> javax.swing.GroupLayout.PREFERRED_SIZE)).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING).addGroup(layout.createSequentialGroup().addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, 
>> false).addComponent(jButton2).addComponent(jComboBox2).addComponent(jButton6, 
>> javax.swing.GroupLayout.DEFAULT_SIZE, 
>> javax.swing.GroupLayout.DEFAULT_SIZE, 
>> Short.MAX_VALUE)).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE).addComponent(jCheckBox1).addComponent(jCheckBox2))).addComponent(jButton1, 
>> javax.swing.GroupLayout.PREFERRED_SIZE, 53, 
>> javax.swing.GroupLayout.PREFERRED_SIZE)).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED).addComponent(jTabPane1, 
>> javax.swing.GroupLayout.DEFAULT_SIZE, 161, 
>> Short.MAX_VALUE).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE).addComponent(jButton3).addComponent(jButton4).addComponent(jButton5).addComponent(jButton7).addComponent(jButton8)).addContainerGap()));
>> +
>> +        JPanel pane1 = new JPanel(new BorderLayout());
>> +        JPanel pane2 = new JPanel(new BorderLayout());
>> +        pane1.add(jScrollPane1);
>> +        pane2.add(jScrollPane2);
>> +        jTabPane1.add(pane1);
>> +        jTabPane1.add(pane2);
>> +        jTabPane1.setTitleAt(0, 
>> Translator.R("APPEXTSECguiPanelCustomDefs"));
>> +        jTabPane1.setTitleAt(1, 
>> Translator.R("APPEXTSECguiPanelGlobalDefs"));
>> +        jTabPane1.setToolTipTextAt(0, 
>> DeploymentConfiguration.getAppletTrustCustomSettingsPath().getAbsolutePath());
>> +        jTabPane1.setToolTipTextAt(1, 
>> DeploymentConfiguration.getAppletTrustGlobalSettingsPath().getAbsolutePath());
>> +        jTabPane1.addChangeListener(new ChangeListener() {
>> +            @Override
>> +            public void stateChanged(ChangeEvent e) {
>> +                switch (jTabPane1.getSelectedIndex()) {
>> +                    case 0:
>> +                        currentModel = customModel;
>> +                        currentTable = jTable1;
>> +                        break;
>> +                    case 1:
>> +                        currentModel = globalModel;
>> +                        currentTable = jTable2;
>> +                        break;
>> +                }
>> +                setButtons((!currentModel.back.isReadOnly()));
>> +            }
>> +        });
>> +    }
>> +
>> +    private void 
>> jComboBox1ActionPerformed(java.awt.event.ActionEvent evt) {
>> +        try {
>> + conf.setProperty(DeploymentConfiguration.KEY_SECURITY_LEVEL, 
>> ((AppletSecurityLevel) jComboBox1.getSelectedItem()).toChars());
>> +            conf.save();
>> +        } catch (Exception ex) {
>> +            ex.printStackTrace();
>> +            JOptionPane.showMessageDialog(this, ex);
>> +        }
>> +    }
>> +
>> +    private void jButton2ActionPerformed(java.awt.event.ActionEvent 
>> evt) {
>> +
>> +        if (jComboBox2.getSelectedIndex() == 0) {
>> +            removeSelectedFromTable(currentTable);
>> +        }
>> +        if (jComboBox2.getSelectedIndex() == 1) {
>> +            removeByBehaviour(UnsignedAppletAction.ALWAYS);
>> +        }
>> +        if (jComboBox2.getSelectedIndex() == 2) {
>> +            removeByBehaviour(UnsignedAppletAction.NEVER);
>> +        }
>> +        if (jComboBox2.getSelectedIndex() == 3) {
>> +            removeByBehaviour(UnsignedAppletAction.YES);
>> +        }
>> +        if (jComboBox2.getSelectedIndex() == 4) {
>> +            removeByBehaviour(UnsignedAppletAction.NO);
>> +        }
>> +        if (jComboBox2.getSelectedIndex() == 5) {
>> +            removeAllItemsFromTable(currentTable, customModel);
>> +        }
>> +    }
>> +
>> +    private void jButton5ActionPerformed(java.awt.event.ActionEvent 
>> evt) {
>> +
>> +        String s1 = 
>> JOptionPane.showInputDialog(Translator.R("APPEXTSECguiPanelDocTest"), 
>> lastDoc);
>> +        String s2 = 
>> JOptionPane.showInputDialog(Translator.R("APPEXTSECguiPanelCodeTest"), lastCode); 
>>
>> +        lastDoc = s1;
>> +        lastCode = s2;
>> +        try {
>> +            List<UnsignedAppletActionEntry> i = 
>> currentModel.back.getMatchingItems(s1, s2, null, null);
>> +            if (i == null || i.isEmpty()) {
>> +                JOptionPane.showMessageDialog(this, 
>> Translator.R("APPEXTSECguiPanelNoMatch"));
>> +            } else {
>> +                JOptionPane.showMessageDialog(this, 
>> Translator.R("APPEXTSECguiPanelMatchingNote") + "\n" + 
>> appletItemsToCaption(i, Translator.R("APPEXTSECguiPanelMatched") + ": 
>> "));
>> +            }
>> +        } catch (Exception ex) {
>> +            ex.printStackTrace();
>> +            JOptionPane.showMessageDialog(this, 
>> Translator.R("APPEXTSECguiPanelMatchingError", ex));
>> +        }
>> +
>> +    }
>> +
>> +    private void jButton3ActionPerformed(java.awt.event.ActionEvent 
>> evt) {
>> +
>> +        currentModel.addRow();
>> +    }
>> +
>> +    private void jButton4ActionPerformed(java.awt.event.ActionEvent 
>> evt) {
>> +
>> +        File f = null;
>> +        try {
>> +            f = File.createTempFile("appletTable", "validation");
>> +        } catch (Exception ex) {
>> +            ex.printStackTrace();
>> +            JOptionPane.showMessageDialog(this, 
>> Translator.R("APPEXTSECguiPanelCanNOtValidate", ex.toString()));
>> +            return;
>> +        }
>> +        try {
>> +            currentModel.back.writeContentsLocked();
>> +            BufferedWriter bw = new BufferedWriter(new 
>> OutputStreamWriter(new FileOutputStream(f), "UTF-8"));
>> +            currentModel.back.writeContent(bw);
>> +            bw.flush();
>> +            bw.close();
>> +            UnsignedAppletActionStorageOperator copy = new 
>> UnsignedAppletActionStorageOperator(f);
>> +            UnsignedAppletActionEntry[] items = copy.toArray();
>> +            for (int i = 0; i < items.length; i++) {
>> +                UnsignedAppletActionEntry unsignedAppletActionEntry 
>> = items[i];
>> +                if (unsignedAppletActionEntry.getDocumentBase() != 
>> null && 
>> !unsignedAppletActionEntry.getDocumentBase().getRegEx().trim().isEmpty()) 
>> {
>> +                    Pattern p = 
>> Pattern.compile(unsignedAppletActionEntry.getDocumentBase().getRegEx());
>> +                    p.matcher("someInput").find();
>> +                } else {
>> +                    throw new 
>> RuntimeException(Translator.R("APPEXTSECguiPanelEmptyDoc"));
>> +                }
>> +                if (unsignedAppletActionEntry.getCodeBase() != null 
>> && 
>> !unsignedAppletActionEntry.getCodeBase().getRegEx().trim().isEmpty()) {
>> +                    Pattern p = 
>> Pattern.compile(unsignedAppletActionEntry.getCodeBase().getRegEx());
>> +                    p.matcher("someInput").find();
>> +                } else {
>> +                    throw new 
>> RuntimeException(Translator.R("APPEXTSECguiPanelEmptyCode"));
>> +                }
>> + 
>> UnsignedAppletActionEntry.createArchivesString(UnsignedAppletActionEntry.createArchivesList(UnsignedAppletActionEntry.createArchivesString(unsignedAppletActionEntry.getArchives())));
>> +
>> +            }
>> +            JOptionPane.showMessageDialog(this, 
>> Translator.R("APPEXTSECguiPanelTableValid"));
>> +        } catch (Exception ex) {
>> +            ex.printStackTrace();
>> +            JOptionPane.showMessageDialog(this, 
>> Translator.R("APPEXTSECguiPanelTableInvalid ", ex.toString()));
>> +        } finally {
>> +            f.delete();
>> +        }
>> +
>> +    }
>> +
>> +    private void 
>> jCheckBox2ActionPerformed(java.awt.event.ActionEvent evt) {
>> +
>> +        reloadTable();
>> +    }
>> +
>> +    private void jButton6ActionPerformed(java.awt.event.ActionEvent 
>> evt) {
>> +        int[] selectedIndexs = currentTable.getSelectedRows();
>> +        currentTable.selectAll();
>> +
>> +        for (int i = 0; i < currentTable.getRowCount(); i++) {
>> +            for (int selectedIndex : selectedIndexs) {
>> +                if (selectedIndex == i) {
>> +                    currentTable.removeRowSelectionInterval(i, i);
>> +                    break;
>> +                }
>> +            }
>> +        }
>> +    }
>> +
>> +    private void jButton7ActionPerformed(java.awt.event.ActionEvent 
>> evt) {
>> +        int orig = currentTable.getSelectedRow();
>> +        int i = currentTable.convertRowIndexToModel(orig);
>> +        currentModel.moveUp(i);
>> +        reloadTable();
>> +        if (orig >= 1) {
>> + currentTable.getSelectionModel().setSelectionInterval(orig - 1, 
>> orig - 1);
>> +        }
>> +    }
>> +
>> +    private void jButton8ActionPerformed(java.awt.event.ActionEvent 
>> evt) {
>> +        int orig = currentTable.getSelectedRow();
>> +        int i = currentTable.convertRowIndexToModel(orig);
>> +        currentModel.moveDown(i);
>> +        reloadTable();
>> +        if (orig < currentModel.getRowCount()) {
>> + currentTable.getSelectionModel().setSelectionInterval(orig + 1, 
>> orig + 1);
>> +        }
>> +    }
>> +
>> +    private void jButton1ActionPerformed(java.awt.event.ActionEvent 
>> evt) {
>> +    }
>> +
>> +    private void setButtons(boolean b) {
>> +        jButton2.setEnabled(b);
>> +        jButton3.setEnabled(b);
>> +        jButton6.setEnabled(b);
>> +        jButton7.setEnabled(b);
>> +        jButton8.setEnabled(b);
>> +    }
>> +
>> +    private JTable createTbale(final TableModel model) {
>> +        JTable jt = new JTable() {
>> +            @Override
>> +            public TableCellEditor getCellEditor(int row, int column) {
>> +                if (column == 0) {
>> +                    return new DefaultCellEditor(new JComboBox(new 
>> UnsignedAppletAction[]{UnsignedAppletAction.ALWAYS, 
>> UnsignedAppletAction.NEVER, UnsignedAppletAction.YES, 
>> UnsignedAppletAction.NO}));
>> +                }
>> +                if (column == 2) {
>> +                    return new DefaultCellEditor(new 
>> MyTextField((UrlRegEx) (model.getValueAt(row, column))));
>> +                }
>> +                if (column == 3) {
>> +                    return new DefaultCellEditor(new 
>> MyTextField((UrlRegEx) (model.getValueAt(row, column))));
>> +                }
>> +                return super.getCellEditor(row, column);
>> +            }
>> +
>> +            @Override
>> +            public TableCellRenderer getCellRenderer(int row, int 
>> column) {
>> +                if (column == 1) {
>> +                    return new 
>> UrlRegexCellRenderer.MyDateCellRenderer((Date) (model.getValueAt(row, 
>> column)));
>> +                }
>> +                if (column == 2) {
>> +                    if (!jCheckBox2.isSelected()) {
>> +                        return new UrlRegexCellRenderer((UrlRegEx) 
>> (model.getValueAt(row, column)));
>> +                    }
>> +                }
>> +                if (column == 3) {
>> +                    if (!jCheckBox2.isSelected()) {
>> +                        return new UrlRegexCellRenderer((UrlRegEx) 
>> (model.getValueAt(row, column)));
>> +                    }
>> +                }
>> +                return super.getCellRenderer(row, column);
>> +            }
>> +        };
>> +        jt.setRowHeight(jt.getRowHeight() + jt.getRowHeight() / 2);
>> +        jt.setModel(model);
>> +        return jt;
>> +
>> +    }
>> +
>> +    private void reloadTable() {
>> +        currentTable.setModel(new DefaultTableModel());
>> +        currentTable.setModel(currentModel);
>> +
>> +    }
>> +
>> +    private void removeByBehaviour(UnsignedAppletAction 
>> unsignedAppletAction) {
>> +        UnsignedAppletActionEntry[] items = 
>> currentModel.back.toArray();
>> +        if (jCheckBox1.isSelected()) {
>> +            List<UnsignedAppletActionEntry> toBeDeleted = new 
>> ArrayList();
>> +            for (int i = 0; i < items.length; i++) {
>> +                UnsignedAppletActionEntry unsignedAppletActionEntry 
>> = items[i];
>> +                if 
>> (unsignedAppletActionEntry.getUnsignedAppletAction() == 
>> unsignedAppletAction) {
>> +                    toBeDeleted.add(unsignedAppletActionEntry);
>> +                }
>> +
>> +            }
>> +            String s = 
>> Translator.R("APPEXTSECguiPanelConfirmDeletionOf", 
>> toBeDeleted.size())+": \n";
>> +            for (int i = 0; i < toBeDeleted.size(); i++) {
>> +                s += appletItemToCaption(toBeDeleted.get(i), " ") + 
>> "\n";
>> +            }
>> +            int a = JOptionPane.showConfirmDialog(this, s);
>> +            if (a != JOptionPane.OK_OPTION) {
>> +                return;
>> +            }
>> +        }
>> +        currentModel.removeByBehaviour(unsignedAppletAction);
>> +    }
>> +
>> +    public static final class MyTextField extends JTextField {
>> +
>> +        private final UrlRegEx keeper;
>> +
>> +        private MyTextField(UrlRegEx urlRegEx) {
>> +            if (urlRegEx == null) {
>> +                keeper = new UrlRegEx("");
>> +            } else {
>> +                this.keeper = urlRegEx;
>> +            }
>> +            setText(keeper.getFilteredRegEx());
>> +        }
>> +
>> +        @Override
>> +        public void setText(String t) {
>> +            super.setText(keeper.getRegEx());
>> +        }
>> +    }
>> +
>> +    public static final class UrlRegexCellRenderer extends 
>> DefaultTableCellRenderer {
>> +
>> +        private final UrlRegEx keeper;
>> +
>> +        private UrlRegexCellRenderer(UrlRegEx urlRegEx) {
>> +            if (urlRegEx == null) {
>> +                keeper = new UrlRegEx("");
>> +            } else {
>> +                this.keeper = urlRegEx;
>> +            }
>> +            setText(keeper.getFilteredRegEx());
>> +        }
>> +
>> +        @Override
>> +        public void setText(String t) {
>> +            if (keeper == null) {
>> +                super.setText("");
>> +            } else {
>> +                super.setText(keeper.getFilteredRegEx());
>> +            }
>> +        }
>> +
>> +        public static final class MyDateCellRenderer extends 
>> DefaultTableCellRenderer {
>> +
>> +            private final Date keeper;
>> +
>> +            private MyDateCellRenderer(Date d) {
>> +                this.keeper = d;
>> +                setText(DateFormat.getInstance().format(d));
>> +            }
>> +
>> +            @Override
>> +            public void setText(String t) {
>> +                if (keeper == null) {
>> +                    super.setText("");
>> +                } else {
>> + super.setText(DateFormat.getInstance().format(keeper));
>> +                }
>> +            }
>> +        }
>> +    }
>> +
>> +    private class SingleSelectionListenerImpl implements 
>> ListSelectionListener {
>> +
>> +        private final JTable table;
>> +
>> +        public SingleSelectionListenerImpl(JTable table) {
>> +            this.table = table;
>> +        }
>> +
>> +        @Override
>> +        public void valueChanged(ListSelectionEvent e) {
>> +            if (table.getSelectedRows().length == 1) {
>> +                jButton7.setEnabled(true);
>> +                jButton8.setEnabled(true);
>> +            } else {
>> +                jButton7.setEnabled(false);
>> +                jButton8.setEnabled(false);
>> +            }
>> +        }
>> +    }
>> +}
>> diff -r e631770d76ba 
>> netx/net/sourceforge/jnlp/resources/Messages.properties
>> --- a/netx/net/sourceforge/jnlp/resources/Messages.properties Thu Jan 
>> 31 11:12:35 2013 +0100
>> +++ b/netx/net/sourceforge/jnlp/resources/Messages.properties Fri Feb 
>> 01 20:55:48 2013 +0100
>> @@ -474,3 +474,57 @@
>>  SPLASHerrorInInformation = Error during loading of information 
>> element, verify source rather
>>  SPLASHmissingInformation = Information element is missing, verify 
>> source rather
>>  SPLASHchainWas = This is the list of exceptions that occurred 
>> launching your applet. Please note, those exceptions can be from 
>> multiple applets. For a good bug report, be sure to run only one applet.
>> +
>> +APPEXTSECappletSecurityLevelExtraHighId=Extra High Security
>
> IMO Extra High Security is better here as simply 'Disable Applets'. It 
> is a bit odd to say you're securing Java by turning it off.
>
>> +APPEXTSECappletSecurityLevelVeryHighId=Very High Security
>> +APPEXTSECappletSecurityLevelHighId=High Security
>> +APPEXTSECappletSecurityLevelLowId=Low Security
>> +APPEXTSECappletSecurityLevelExtraHighExplanation=No applet will be run
>> +APPEXTSECappletSecurityLevelVeryHighExplanation=No unsigned applets 
>> will be run
>> +APPEXTSECappletSecurityLevelHighExplanation=User will be prompted 
>> for each unsigned applet
>> +APPEXTSECappletSecurityLevelLowExplanation=All, even untrusted, 
>> applets will be run
>> +APPEXTSECunsignedAppletActionAlways=Always trust this applet(s)
>> +APPEXTSECunsignedAppletActionNever=Never trust this applet(s)
>> +APPEXTSECunsignedAppletActionYes=This applet was visited and allowed
>> +APPEXTSECunsignedAppletActionNo=This applet was visited and denied
>> +APPEXTSECControlPanelExtendedAppletSecurityTitle=Extended applet 
>> security
>> +APPEXTSECguiTableModelTableColumnAction=Action
>> +APPEXTSECguiTableModelTableColumnDateOfAction=Date of action
>> +APPEXTSECguiTableModelTableColumnDocumentBase=Document-base
>> +APPEXTSECguiTableModelTableColumnCodeBase=Code-base
>> +APPEXTSECguiTableModelTableColumnMainClass=Main class
>> +APPEXTSECguiTableModelTableColumnArchives=Archives
>> +APPEXTSECguiPanelAppletInfoHederPart1={0} {1}
>> +APPEXTSECguiPanelAppletInfoHederPart2={0} from  {1}
>> +APPEXTSECguiPanelConfirmDeletionOf=Are you sure you want to delete 
>> following {0} items
>> +APPEXTSECguiPanelHelpButton=Help
>> +APPEXTSECguiPanelSecurityLevel=Security Level
>> +APPEXTSECguiPanelGlobalBehaviourCaption=Settings of global behaviour 
>> for applets
>> +APPEXTSECguiPanelDeleteMenuSelected=selected
>> +APPEXTSECguiPanelDeleteMenuAllA=all allowed (A)
>> +APPEXTSECguiPanelDeleteMenuAllN=all forbidden (N)
>> +APPEXTSECguiPanelDeleteMenuAlly=all approved (y)
>> +APPEXTSECguiPanelDeleteMenuAlln=all disaprooved (n)
>> +APPEXTSECguiPanelDeleteMenuAllAll=absolute all
>> +APPEXTSECguiPanelDeleteButton=Delete
>> +APPEXTSECguiPanelTestUrlButton=Test url
>> +APPEXTSECguiPanelAddRowButton=Add new row
>> +APPEXTSECguiPanelValidateTableButton=Validate table
>> +APPEXTSECguiPanelAskeforeActionBox=Ask me before action
>> +APPEXTSECguiPanelShowRegExesBox=Show full regular expressions
>> +APPEXTSECguiPanelInverSelection=Invert selection
>> +APPEXTSECguiPanelMoveRowUp=Move row up
>> +APPEXTSECguiPanelMoveRowDown=Move row down
>> +APPEXTSECguiPanelCustomDefs=Custom definitions
>> +APPEXTSECguiPanelGlobalDefs=Global definitions
>> +APPEXTSECguiPanelDocTest=Type document base URL
>> +APPEXTSECguiPanelCodeTest=Type code base URL
>> +APPEXTSECguiPanelNoMatch=Nothing matched
>> +APPEXTSECguiPanelMatchingNote=Please note, that only first matched 
>> result will be considered as result.
>> +APPEXTSECguiPanelMatched=Matched
>> +APPEXTSECguiPanelMatchingError=Error during matching: {0}
>> +APPEXTSECguiPanelCanNotValidate=Can not validate, can not create tmp 
>> file - {0}
>> +APPEXTSECguiPanelEmptyDoc=All document-bases must be full
>> +APPEXTSECguiPanelEmptyCode=All code-bases must be full
>> +APPEXTSECguiPanelTableValid=Table looks valid
>> +APPEXTSECguiPanelTableInvalid=Invalid with following error: {0}
>> \ No newline at end of file
>> diff -r e631770d76ba 
>> netx/net/sourceforge/jnlp/util/lockingfile/LockingFile.java
>> --- /dev/null    Thu Jan 01 00:00:00 1970 +0000
>> +++ b/netx/net/sourceforge/jnlp/util/lockingfile/LockingFile.java Fri 
>> Feb 01 20:55:48 2013 +0100
>> @@ -0,0 +1,156 @@
>> +/*
>> +Copyright (C) 2013 Red Hat, Inc.
>> +
>> +This file is part of IcedTea.
>> +
>> +IcedTea is free software; you can redistribute it and/or
>> +modify it under the terms of the GNU General Public License as 
>> published by
>> +the Free Software Foundation, version 2.
>> +
>> +IcedTea is distributed in the hope that it will be useful,
>> +but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> +General Public License for more details.
>> +
>> +You should have received a copy of the GNU General Public License
>> +along with IcedTea; see the file COPYING.  If not, write to
>> +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 
>> Boston, MA
>> +02110-1301 USA.
>> +
>> +Linking this library statically or dynamically with other modules is
>> +making a combined work based on this library.  Thus, the terms and
>> +conditions of the GNU General Public License cover the whole
>> +combination.
>> +
>> +As a special exception, the copyright holders of this library give you
>> +permission to link this library with independent modules to produce an
>> +executable, regardless of the license terms of these independent
>> +modules, and to copy and distribute the resulting executable under
>> +terms of your choice, provided that you also meet, for each linked
>> +independent module, the terms and conditions of the license of that
>> +module.  An independent module is a module which is not derived from
>> +or based on this library.  If you modify this library, you may extend
>> +this exception to your version of the library, but you are not
>> +obligated to do so.  If you do not wish to do so, delete this
>> +exception statement from your version.
>> + */
>> +
>> +package net.sourceforge.jnlp.util.lockingfile;
>> +
>> +import java.nio.channels.FileChannel;
>> +
>> +import java.io.RandomAccessFile;
>> +
>> +import java.io.File;
>> +import java.io.IOException;
>> +import java.nio.channels.FileLock;
>> +import java.util.Map;
>> +import java.util.WeakHashMap;
>> +import java.util.concurrent.locks.ReentrantLock;
>> +
>> +/*
>> + * Process & thread locked access to a file. Creates file if it does 
>> not already exist.
>> + */
>> +public class LockingFile {
>> +
>> +    // The file for access
>> +    private RandomAccessFile randomAccessFile;
>> +    private FileChannel fileChannel;
>> +    private File file;
>> +
>> +    // A file lock will protect against locks for multiple
>> +    // processes, while a thread lock is still needed within a 
>> single JVM.
>> +
>> +    private FileLock processLock = null;
>> +    private ReentrantLock threadLock = new ReentrantLock();
>> +    private boolean readOnly;
>> +
>> +    private LockingFile(File file) {
>> +        this.file = file;
>> +        try{
>> +            //just try to ctreate
>> +            this.file.createNewFile();
>> +        }catch(Exception ex){
>> +            //intentionaly silent
>> +        }
>> +        if (!this.file.isFile() && file.getParentFile()!=null && 
>> !file.getParentFile().canWrite() ){
>> +            readOnly=true;
>> +        } else{
>> +        this.readOnly = !file.canWrite();
>> +        if (!readOnly && file.getParentFile()!=null && 
>> !file.getParentFile().canWrite()){
>> +                readOnly=true;
>> +        }
>> +        }
>> +    }
>> +
>> +    public boolean isReadOnly() {
>> +        return readOnly;
>> +    }
>> +
>> +
>> +
>> +    // Provide shared access to LockedFile's via weak map
>> +    static private final Map<File, LockingFile> instanceCache = new 
>> WeakHashMap<File, LockingFile>();
>> +
>> +    /**
>> +     * Get a LockingFile for a given File.
>> +     * Ensures that we share the same instance for all threads
>> +     * @param file the file to lock
>> +     * @return a LockingFile instance
>> +     */
>> +    synchronized public static LockingFile getInstance(File file) {
>> +        if (!instanceCache.containsKey(file)) {
>> +            instanceCache.put(file, new LockingFile(file));
>> +        }
>> +
>> +        return instanceCache.get(file);
>> +    }
>> +
>> +    /**
>> +     * Get the file being locked.
>> +     *
>> +     * @return the file
>> +     */
>> +    public File getFile() {
>> +        return file;
>> +    }
>> +
>> +    /**
>> +     * Lock access to the file. Lock is reentrant.
>> +     */
>> +    public void lock() throws IOException {
>> +        // Create if does not already exist, cannot lock 
>> non-existing file
>> +        if (!readOnly){
>> +            this.file.createNewFile();
>> +        }
>> +
>> +        this.threadLock.lock();
>> +        String rw="rws";
>> +        if (isReadOnly()){
>> +            rw="r";
>> +        }
>> +        if (file.exists()){
>> +        this.randomAccessFile = new RandomAccessFile(this.file, rw);
>> +        this.fileChannel = randomAccessFile.getChannel();
>> +        }
>> +
>> +        if (!isReadOnly()) this.processLock = this.fileChannel.lock();
>> +    }
>> +
>> +    /**
>> +     * Unlock access to the file. Lock is reentrant.
>> +     */
>> +    public void unlock() throws IOException {
>> +        boolean releaseProcessLock = (this.threadLock.getHoldCount() 
>> == 1);
>> +        try {
>> +            if (releaseProcessLock && this.processLock != null) {
>> +                this.processLock.release();
>> +                this.randomAccessFile.close();
>> +                this.fileChannel.close();
>> +            }
>> +        } finally {
>> +            this.processLock = null;
>> +        }
>> +        this.threadLock.unlock();
>> +    }
>> +}
>> \ No newline at end of file
>> diff -r e631770d76ba 
>> netx/net/sourceforge/jnlp/util/lockingfile/LockingReaderWriter.java
>> --- /dev/null    Thu Jan 01 00:00:00 1970 +0000
>> +++ 
>> b/netx/net/sourceforge/jnlp/util/lockingfile/LockingReaderWriter.java 
>>  Fri Feb 01 20:55:48 2013 +0100
>> @@ -0,0 +1,201 @@
>> +/*
>> +Copyright (C) 2013 Red Hat, Inc.
>> +
>> +This file is part of IcedTea.
>> +
>> +IcedTea is free software; you can redistribute it and/or
>> +modify it under the terms of the GNU General Public License as 
>> published by
>> +the Free Software Foundation, version 2.
>> +
>> +IcedTea is distributed in the hope that it will be useful,
>> +but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> +General Public License for more details.
>> +
>> +You should have received a copy of the GNU General Public License
>> +along with IcedTea; see the file COPYING.  If not, write to
>> +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 
>> Boston, MA
>> +02110-1301 USA.
>> +
>> +Linking this library statically or dynamically with other modules is
>> +making a combined work based on this library.  Thus, the terms and
>> +conditions of the GNU General Public License cover the whole
>> +combination.
>> +
>> +As a special exception, the copyright holders of this library give you
>> +permission to link this library with independent modules to produce an
>> +executable, regardless of the license terms of these independent
>> +modules, and to copy and distribute the resulting executable under
>> +terms of your choice, provided that you also meet, for each linked
>> +independent module, the terms and conditions of the license of that
>> +module.  An independent module is a module which is not derived from
>> +or based on this library.  If you modify this library, you may extend
>> +this exception to your version of the library, but you are not
>> +obligated to do so.  If you do not wish to do so, delete this
>> +exception statement from your version.
>> + */
>> +package net.sourceforge.jnlp.util.lockingfile;
>> +
>> +import java.io.BufferedWriter;
>> +
>> +import java.io.BufferedReader;
>> +import java.io.File;
>> +import java.io.FileInputStream;
>> +import java.io.FileOutputStream;
>> +import java.io.IOException;
>> +import java.io.InputStreamReader;
>> +import java.io.OutputStreamWriter;
>> +
>> +/**
>> + * Process-locked string storage backed by a file.
>> + * Each string is stored on its own line.
>> + * Any new-lines must be encoded somehow if they are to be stored.
>> + */
>> +public abstract class LockingReaderWriter {
>> +
>> +    private LockingFile lockedFile;
>> +
>> +    /**
>> +     * Create locking file-backed storage.
>> +     * @param file the storage file
>> +     */
>> +    public LockingReaderWriter(File file) {
>> +        this.lockedFile = LockingFile.getInstance(file);
>> +    }
>> +
>> +    /**
>> +     * Get the underlying file.
>> +     * Any access to this file should use lock() & unlock().
>> +     *
>> +     * @return the file
>> +     */
>> +    public File getBackingFile() {
>> +        return this.lockedFile.getFile();
>> +    }
>> +
>> +    public boolean isReadOnly() {
>> +        return this.lockedFile.isReadOnly();
>> +    }
>> +
>> +    /**
>> +     * Lock the underlying storage. Lock is reentrant.
>> +     */
>> +    public void lock() {
>> +        try {
>> +            lockedFile.lock();
>> +        } catch (IOException e) {
>> +            throw new StorageIoException(e);
>> +        }
>> +    }
>> +
>> +    /**
>> +     * Unlock the underlying storage. Lock is reentrant.
>> +     */
>> +    public void unlock() {
>> +        try {
>> +            lockedFile.unlock();
>> +        } catch (IOException e) {
>> +            throw new StorageIoException(e);
>> +        }
>> +    }
>> +
>> +    /**
>> +     * Writes stored contents to file. Assumes lock is held.
>> +     * @throws IOException
>> +     */
>> +    protected void writeContents() throws IOException {
>> +        if (!getBackingFile().isFile()){
>> +            return;
>> +        }
>> +        if (isReadOnly()){
>> +            return;
>> +        }
>
> This silent failure is not acceptable IMO! It ignores the intent of 
> the method completely, and it was causing me a lot of confusion when I 
> was playing with the code. We ought to error-out here.
>
>> +        BufferedWriter writer = null;
>> +        try {
>> +            writer = new BufferedWriter(new OutputStreamWriter(
>> +                    new FileOutputStream(getBackingFile()), "UTF-8"));
>> +            writeContent(writer);
>> +            writer.flush();
>> +        } finally {
>> +            if (writer != null) {
>> +                writer.close();
>> +            }
>> +        }
>> +    }
>> +
>> +    protected abstract void writeContent(BufferedWriter writer) 
>> throws IOException;
>> +
>> +    /**
>> +     * Reads contents from file. Assumes lock is held.
>> +     * @throws IOException
>> +     */
>> +    protected void readContents() throws IOException {
>> +        if (!getBackingFile().isFile()){
>> +            return;
>> +        }
>
> Same here, we shouldn't be backed by an invalid file in the first place.
> Note that I believe this will cause problems with how you are 
> currently handling non-existant global configurations -- but that 
> needs a more robust solution than this anyway.
>
>> +        BufferedReader reader = null;
>> +        try {
>> +            reader = new BufferedReader(new InputStreamReader(
>> +                    new FileInputStream(getBackingFile()), "UTF-8"));
>> +
>> +            while (true) {
>> +                String line = reader.readLine();
>> +                if (line == null) {
>> +                    break;
>> +                }
>> +                readLine(line);
>> +            }
>> +        } finally {
>> +            if (reader != null) {
>> +                reader.close();
>> +            }
>> +        }
>> +    }
>> +
>> +    /**
>> +     * Reads contents from file. creating is lock .
>> +     * @throws IOException
>> +     */
>> +    protected synchronized void readContentsLocked() throws 
>> IOException {
>> +        doLocked(new Runnable() {
>> +
>> +            @Override
>> +            public void run() {
>> +                try {
>> +                    readContents();
>> +                } catch (IOException ex) {
>> +                    throw new StorageIoException(ex);
>> +                }
>> +            }
>> +        });
>> +    }
>> +
>> +    /**
>> +     * Reads contents from file. creating is lock .
>> +     * @throws IOException
>> +     */
>> +    protected synchronized void writeContentsLocked() throws 
>> IOException {
>> +        doLocked(new Runnable() {
>> +
>> +            public void run() {
>> +                try {
>> +                    writeContents();
>> +                } catch (IOException ex) {
>> +                    throw new StorageIoException(ex);
>> +                }
>> +            }
>> +        });
>> +
>> +    }
>> +
>> +    protected void doLocked(Runnable r) {
>> +        lock();
>> +        try {
>> +            r.run();
>> +        } finally {
>> +            unlock();
>> +        }
>> +    }
>> +
>> +    protected abstract void readLine(String line);
>> +}
>> diff -r e631770d76ba 
>> netx/net/sourceforge/jnlp/util/lockingfile/StorageIoException.java
>> --- /dev/null    Thu Jan 01 00:00:00 1970 +0000
>> +++ 
>> b/netx/net/sourceforge/jnlp/util/lockingfile/StorageIoException.java 
>>  Fri Feb 01 20:55:48 2013 +0100
>> @@ -0,0 +1,22 @@
>> +package net.sourceforge.jnlp.util.lockingfile;
>> +
>> +/**
>> + * Thrown when an exception occurs using the storage (namely 
>> IOException)
>
> (May as well say 'occurs while using a LockingReaderWriter', seeing as 
> this class is now coupled with it.)
>
>> + */
>> +public class StorageIoException extends RuntimeException {
>> +
>> +    LockingReaderWriter outer;
>> +
>> +    public StorageIoException(Exception e) {
>> +        super(e);
>> +    }
>> +
>> +    public StorageIoException(String e) {
>> +        super(e);
>> +    }
>> +
>> +     public StorageIoException(Exception e, LockingReaderWriter 
>> outer) {
>> +        super(e);
>> +        this.outer = outer;
>> +    }
>> +}
>> diff -r e631770d76ba 
>> tests/netx/unit/net/sourceforge/appletextendedsecurity/impl/UnsignedAppletActionStorageImplTest.java
>> --- /dev/null    Thu Jan 01 00:00:00 1970 +0000
>> +++ 
>> b/tests/netx/unit/net/sourceforge/appletextendedsecurity/impl/UnsignedAppletActionStorageImplTest.java 
>>  Fri Feb 01 20:55:48 2013 +0100
>> @@ -0,0 +1,97 @@
>> +/*
>> +Copyright (C) 2013 Red Hat
>> +
>> +This program is free software; you can redistribute it and/or modify
>> +it under the terms of the GNU General Public License as published by
>> +the Free Software Foundation; either version 2 of the License, or
>> +(at your option) any later version.
>> +
>> +This program is distributed in the hope that it will be useful, but
>> +WITHOUT ANY WARRANTY; without even the implied warranty of
>> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
>> +General Public License for more details.
>> +
>> +You should have received a copy of the GNU General Public License
>> +along with this program; if not, write to the Free Software
>> +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
>> +*/
>> +
>> +package net.sourceforge.appletextendedsecurity.impl;
>> +
>> +import 
>> net.sourceforge.appletextendedsecurity.UnsignedAppletActionEntry;
>> +import java.io.File;
>> +import java.io.IOException;
>> +import java.util.Arrays;
>> +import net.sourceforge.jnlp.ServerAccess;
>> +import org.junit.Assert;
>> +import org.junit.BeforeClass;
>> +import org.junit.Test;
>> +
>> +public class UnsignedAppletActionStorageImplTest {
>> +
>> +    private static File f1;
>> +    private static File f2;
>> +    private static File f3;
>> +    private static File f4;
>> +
>> +    @BeforeClass
>> +    public static void preapreTestFiles() throws IOException {
>> +        f1 = File.createTempFile("itwMatching", "testFile1");
>> +        f2 = File.createTempFile("itwMatching", "testFile2");
>> +        f3 = File.createTempFile("itwMatching", "testFile3");
>> +        f4 = File.createTempFile("itwMatching", "testFile4");
>> +        ServerAccess.saveFile("A 123456 .* .* main jar1;jar2", f1);
>> +        ServerAccess.saveFile("A 123456 .* \\Qbla\\E main 
>> jar1;jar2", f2);
>> +    }
>> +
>> +    @Test
>> +    public void allMatchingDocAndCode() {
>> +        UnsignedAppletActionStorageImpl i1 = new 
>> UnsignedAppletActionStorageImpl(f1);
>> +        UnsignedAppletActionEntry r1 = i1.getMatchingItem("bla", 
>> "blaBla", "main", Arrays.asList(new String[]{"jar1", "jar2"}));
>> +        Assert.assertNotNull("r1 should be found", r1);
>> +        UnsignedAppletActionEntry r3 = i1.getMatchingItem("blah", 
>> "blaBla", "main", Arrays.asList(new String[]{"jar2", "jar1"}));
>> +        Assert.assertNotNull("r3 should be found", r1);
>> +        UnsignedAppletActionEntry r2 = i1.getMatchingItem("bla", 
>> "blaBlam", "wrong_main", Arrays.asList(new String[]{"jar1", "jar2"}));
>> +        Assert.assertNull("r2 should NOT be found", r2);
>> +        UnsignedAppletActionEntry r4 = i1.getMatchingItem("blha", 
>> "blaBlam", "main", Arrays.asList(new String[]{"jar2", "wrong_jar"}));
>> +        Assert.assertNull("r4 should NOT be found", r4);
>> +        UnsignedAppletActionEntry r5 = i1.getMatchingItem("blaBla", 
>> "blaBlaBla", "main", Arrays.asList(new String[]{"jar2"}));
>> +        Assert.assertNull("r5 should NOT be found", r5);
>> +
>> +    }
>> +
>> +     @Test
>> +    public void allMatchingDocAndStrictCode() {
>> +        UnsignedAppletActionStorageImpl i1 = new 
>> UnsignedAppletActionStorageImpl(f2);
>> +        UnsignedAppletActionEntry r1 = 
>> i1.getMatchingItem("whatever", "bla", "main", Arrays.asList(new 
>> String[]{"jar1", "jar2"}));
>> +        Assert.assertNotNull("r1 should be found", r1);
>> +        UnsignedAppletActionEntry r3 = 
>> i1.getMatchingItem("whatever", null, "main", Arrays.asList(new 
>> String[]{"jar2", "jar1"}));
>> +        Assert.assertNotNull("r3 should be found", r1);
>> +        UnsignedAppletActionEntry r2 = i1.getMatchingItem("bla", 
>> "blaBlam", "main", Arrays.asList(new String[]{"jar1", "jar2"}));
>> +        Assert.assertNull("r2 should NOT be found", r2);
>> +        UnsignedAppletActionEntry r4 = i1.getMatchingItem(null, 
>> "blaBlam", null, null);
>> +        Assert.assertNull("r4 should NOT be found", r4);
>> +
>> +    }
>> +
>> +     @Test
>> +    public void allMatchingDocAndCodeWithNulls() {
>> +        UnsignedAppletActionStorageImpl i1 = new 
>> UnsignedAppletActionStorageImpl(f1);
>> +        UnsignedAppletActionEntry r1 = i1.getMatchingItem("bla", 
>> "blaBla", null, null);
>> +        Assert.assertNotNull("r1 should be found", r1);
>> +        UnsignedAppletActionEntry r3 = i1.getMatchingItem("bla", 
>> "whatever", "", null);
>> +        Assert.assertNotNull("r3 should be found", r1);
>> +        UnsignedAppletActionEntry r2 = i1.getMatchingItem("bla", 
>> "blaBla", null, Arrays.asList(new String[]{"jar2", "jar1"}));
>> +        Assert.assertNotNull("r2 should be found", r2);
>> +        UnsignedAppletActionEntry r4 = i1.getMatchingItem("bla", 
>> "blaBla", "main", null);
>> +        Assert.assertNotNull("r4 should be found", r4);
>> +        UnsignedAppletActionEntry r5 = i1.getMatchingItem("", 
>> "blaBla", "main", Arrays.asList(new String[]{"jar2", "jar1"}));
>> +        Assert.assertNotNull("r5 should be found", r5);
>> +        UnsignedAppletActionEntry r6 = i1.getMatchingItem(null, 
>> null, "main", Arrays.asList(new String[]{"jar2", "jar1"}));
>> +        Assert.assertNotNull("r6 should be found", r6);
>> +        UnsignedAppletActionEntry r7 = i1.getMatchingItem(null, 
>> null, "main", Arrays.asList(new String[]{"jar2", "jar11"}));
>> +        Assert.assertNull("r7 should NOT be found", r7);
>> +
>> +
>> +    }
>> +}
>> diff -r e631770d76ba 
>> tests/netx/unit/net/sourceforge/jnlp/util/lockingfile/LockingStringListStorageTest.java
>> --- /dev/null    Thu Jan 01 00:00:00 1970 +0000
>> +++ 
>> b/tests/netx/unit/net/sourceforge/jnlp/util/lockingfile/LockingStringListStorageTest.java 
>>  Fri Feb 01 20:55:48 2013 +0100
>> @@ -0,0 +1,385 @@
>> +/*
>> +Copyright (C) 2013 Red Hat
>> +
>> +This program is free software; you can redistribute it and/or modify
>> +it under the terms of the GNU General Public License as published by
>> +the Free Software Foundation; either version 2 of the License, or
>> +(at your option) any later version.
>> +
>> +This program is distributed in the hope that it will be useful, but
>> +WITHOUT ANY WARRANTY; without even the implied warranty of
>> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
>> +General Public License for more details.
>> +
>> +You should have received a copy of the GNU General Public License
>> +along with this program; if not, write to the Free Software
>> +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
>> +*/
>> +
>> +package net.sourceforge.jnlp.util.lockingfile;
>> +
>> +import static org.junit.Assert.assertFalse;
>> +import static org.junit.Assert.assertTrue;
>> +import org.junit.Before;
>> +import java.io.BufferedWriter;
>> +import java.io.File;
>> +import java.io.IOException;
>> +import java.util.ArrayList;
>> +import java.util.List;
>> +import org.junit.Assert;
>> +import org.junit.Test;
>> +
>> +
>> +
>> +public  class LockingStringListStorageTest {
>> +
>> +/**
>> + * Process-locked string storage backed by a file.
>> + * Each string is stored on its own line.
>> + * Any new-lines must be encoded somehow if they are to be stored.
>> + */
>> +public static class LockingStringListStorage extends 
>> LockingReaderWriter {
>> +
>> +    private List<String> cachedContents = new ArrayList<String>();
>> +
>> +    //To sutisfy testengine, void constructor and dummy testmethod
>> +    public LockingStringListStorage() throws IOException {
>> +        this(createTmpBackend());
>> +    }
>> +
>> +    @Test
>> +    public void lockingStringListStorageCanBeInstantiated(){
>> +        Assert.assertNotNull(this);
>> +    }
>> +
>> +
>> +    private static File createTmpBackend() throws IOException{
>> +        File f = File.createTempFile("forTests","emptyConstructor");
>> +        f.deleteOnExit();
>> +        return f;
>> +    }
>> +
>> +
>> +    /**
>> +     * Create locking file-backed storage.
>> +     * @param file the storage file
>> +     */
>> +    public LockingStringListStorage(File file) {
>> +        super(file);
>> +    }
>> +
>> +    /**
>> +     *  Get the underlying string list cache. Should lock
>> +     *  before using.
>> +     *  @return the cache
>> +     */
>> +    final protected List<String> getCachedContents() {
>> +        return cachedContents;
>> +    }
>> +
>> +    @Override
>> +    public void writeContent(BufferedWriter writer) throws 
>> IOException {
>> +        for (String string : cachedContents) {
>> +            writer.write(string);
>> +            writer.newLine();
>> +        }
>> +    }
>> +
>> +    @Override
>> +    protected void readLine(String line) {
>> +        this.cachedContents.add(line);
>> +    }
>> +
>> +    @Override
>> +    protected void readContents() throws IOException {
>> +        cachedContents.clear();
>> +        super.readContents();
>> +    }
>> +
>> +    /*
>> +     * Atomic container abstraction methods. These all allow the 
>> file-backed
>> +     * string list to be treated as a convenient in-memory object.
>> +     */
>> +    /**
>> +     * Appends the specified line to the end of the storage.
>> +     *
>> +     * @param line the line to add
>> +     */
>> +    synchronized public void add(final String line) {
>> +        doLocked(new Runnable() {
>> +
>> +            public void run() {
>> +                try {
>> +                    readContents();
>> +                    cachedContents.add(line);
>> +                    writeContents();
>> +                } catch (IOException ex) {
>> +                    throw new StorageIoException(ex);
>> +                }
>> +            }
>> +        });
>> +    }
>> +
>> +    /**
>> +     * Returns an array containing all of the lines in the storage.
>> +     *
>> +     * @return an array of the stored strings
>> +     */
>> +    synchronized public String[] toArray() {
>> +        lock();
>> +        try {
>> +            readContents();
>> +            return cachedContents.toArray(new String[]{});
>> +        } catch (IOException e) {
>> +            throw new StorageIoException(e);
>> +        } finally {
>> +            unlock();
>> +        }
>> +    }
>> +
>> +    /**
>> +     * Returns amount of lines in the storage.
>> +     *
>> +     * @return amount of stored strings
>> +     */
>> +    synchronized public int size() {
>> +        lock();
>> +        try {
>> +            readContents();
>> +            return cachedContents.size();
>> +        } catch (IOException e) {
>> +            throw new StorageIoException(e);
>> +        } finally {
>> +            unlock();
>> +        }
>> +    }
>> +
>> +    /**
>> +     * Returns 'i'th line
>> +     *
>> +     * @return the line
>> +     */
>> +    synchronized public String get(int i) {
>> +        lock();
>> +        try {
>> +            readContents();
>> +            return cachedContents.get(i);
>> +        } catch (IOException e) {
>> +            throw new StorageIoException(e);
>> +        } finally {
>> +            unlock();
>> +        }
>> +    }
>> +
>> +    /**
>> +     * Set the 'i'th line
>> +     *
>> +     * @param line the new line
>> +     */
>> +    synchronized public String set(int i, String line) {
>> +        lock();
>> +        try {
>> +            readContents();
>> +            return cachedContents.set(i, line);
>> +        } catch (IOException e) {
>> +            throw new StorageIoException(e);
>> +        } finally {
>> +            unlock();
>> +        }
>> +    }
>> +
>> +    /**
>> +     * Returns a copied list containing all of the lines in the 
>> storage.
>> +     *
>> +     * @return a list of the stored strings
>> +     */
>> +    synchronized public List<String> toList() {
>> +        lock();
>> +        try {
>> +            readContents();
>> +            return new ArrayList<String>(cachedContents);
>> +        } catch (IOException e) {
>> +            throw new StorageIoException(e);
>> +        } finally {
>> +            unlock();
>> +        }
>> +    }
>> +
>> +    /**
>> +     * Returns <tt>true</tt> if the storage contains the specified 
>> element.
>> +     *
>> +     * @param line
>> +     * @return <tt>true</tt> if the storage contains the line
>> +     */
>> +    synchronized public boolean contains(String line) {
>> +        lock();
>> +        try {
>> +            readContents();
>> +            return cachedContents.contains(line);
>> +        } catch (IOException e) {
>> +            throw new StorageIoException(e);
>> +        } finally {
>> +            unlock();
>> +        }
>> +    }
>> +
>> +    /**
>> +     * Empty the storage.
>> +     */
>> +    synchronized public void clear() {
>> +        lock();
>> +        try {
>> +            cachedContents.clear();
>> +            writeContents();
>> +        } catch (IOException e) {
>> +            throw new StorageIoException(e);
>> +        } finally {
>> +            unlock();
>> +        }
>> +    }
>> +
>> +    /**
>> +     * Removes the first occurrence of the specified element from 
>> this list,
>> +     * if it is present (optional operation).  If this list does not 
>> contain
>> +     * the element, it is unchanged.
>> +     *
>> +     * @param line string to be removed from this list, if present
>> +     * @return <tt>true</tt> if the storage contained the line
>> +     */
>> +    synchronized public boolean remove(String line) {
>> +        boolean didRemove;
>> +
>> +        lock();
>> +        try {
>> +            readContents();
>> +            didRemove = cachedContents.remove(line);
>> +            writeContents();
>> +        } catch (IOException e) {
>> +            throw new StorageIoException(e);
>> +        } finally {
>> +            unlock();
>> +        }
>> +
>> +        return didRemove;
>> +    }
>> +}
>> +
>> +
>> +    private static File storagefile;
>> +
>> +    private static LockingStringListStorage newInstance() {
>> +        return new LockingStringListStorage(storagefile);
>> +    }
>> +
>> +    @Before
>> +    public void setUp() throws IOException {
>> +        storagefile = File.createTempFile("foo", "bar");
>> +    }
>> +
>> +    @Test
>> +    public void testSimpleActions() throws IOException {
>> +        LockingStringListStorage storage = newInstance();
>> +
>> +        storage.add("teststring");
>> +        assertTrue(storage.contains("teststring"));
>> +        storage.remove("teststring");
>> +        assertFalse(storage.contains("teststring"));
>> +    }
>> +
>> +    @Test
>> +    public void testInterleavedActions() throws IOException {
>> +        LockingStringListStorage storage1 = newInstance();
>> +        LockingStringListStorage storage2 = newInstance();
>> +
>> +        storage1.add("teststring");
>> +        assertTrue(storage2.contains("teststring"));
>> +        storage2.remove("teststring");
>> +        assertFalse(storage1.contains("teststring"));
>> +    }
>> +
>> +    static class TestThread extends Thread {
>> +        String testString;
>> +        int iterations;
>> +        Throwable error = null;
>> +
>> +        TestThread(String testString, int iterations) {
>> +            this.testString = testString;
>> +            this.iterations = iterations;
>> +        }
>> +
>> +        @Override
>> +        public void run() {
>> +            try {
>> +                LockingStringListStorage storage = newInstance();
>> +                for (int i = 0; i < iterations; i++) {
>> + assertTrue(storage.contains(this.testString));
>> +                    storage.add(this.testString);
>> +                    storage.remove(this.testString);
>> + assertTrue(storage.contains(this.testString));
>> +                }
>> +            } catch (Throwable error) {
>> +                error.printStackTrace();
>> +                this.error = error;
>> +            }
>> +        }
>> +    }
>> +
>> +    private void concurrentReadWrites(int threadAmount, int iterations,
>> +            String testString) throws InterruptedException {
>> +        LockingStringListStorage storage = newInstance();
>> +
>> +        storage.add(testString);
>> +
>> +        List<TestThread> testThreads = new ArrayList<TestThread>();
>> +
>> +        for (int i = 0; i < threadAmount; i++) {
>> +            TestThread thread = new TestThread(testString, iterations);
>> +            testThreads.add(thread);
>> +            thread.start();
>> +        }
>> +
>> +        for (int i = 0; i < threadAmount; i++) {
>> +            testThreads.get(i).join();
>> +        }
>> +
>> +        assertTrue(storage.contains(testString));
>> +        storage.remove(testString);
>> +
>> +        // So long as number adds == number writes, we should be 
>> left with
>> +        // nothing at end.
>> +        assertFalse(storage.contains(testString));
>> +    }
>> +
>> +    // Long testing string, the contents are not important
>> +    private String makeLongTestString() {
>> +        StringBuilder sb = new StringBuilder();
>> +        for (int i = 0; i < 1000; i++) {
>> +            sb.append(Integer.toString(i));
>> +        }
>> +        return sb.toString();
>> +    }
>> +
>> +    @Test
>> +    public void testManyReadWrite() throws Exception {
>> +        int oneThread = 1;
>> +        String shortString = "teststring";
>> +
>> +        // This was causing 'too many open files' because 
>> FileUtils#getFileLock
>> +        // leaks file descriptors. No longer used.
>> +        concurrentReadWrites(oneThread, 500 /* iterations */,
>> +                shortString);
>> +    }
>> +
>> +    @Test
>> +    public void testManyThreads() throws Exception {
>> +        int threadAmount = 25;
>> +        String shortString = "teststring";
>> +        String longString = makeLongTestString();
>> +
>> +        concurrentReadWrites(threadAmount, 10 /* per-thread 
>> iterations */,
>> +                shortString);
>> +        concurrentReadWrites(threadAmount, 2 /* per-thread 
>> iterations */,
>> +                longString);
>> +    }
>> +
>> +}
>
> In conclusion, nits are nits, but I really want to see this in HEAD 
> :-)) Great work on the icedtea-web-settings parts.
>
> Happy hacking,
> -Adam

Actually maybe hold off until Omair comments on the patch, and all the 
typos have been worked out.

1) Some additional comments:

- The delete pop-up is awkward, doing 'delete all' could easily create a 
pop-up that stretches more than the screen, hiding the confirmation.
- Additionally the delete button is hard to visually locate. I think 
selecting things and pressing 'delete' should delete them.
- Help button does nothing ATM, correct ? (its OK for now but just 
double checking)

2) I have attached my patch, although take note of the following:
- I had to fix an issue where applets without any jars said they were 
'signing', not showing the dialogue. Note though that they were still 
not given any permissions, so there was no security hole.
- Your LockingFile as-was did not recursively lock correctly. I had to 
fix it so I could recursively lock to make sure that updating the 
dialogue is an atomic operation. However I still prefer my refactoring 
of Locking File.
-Even still it seems like multiple applet entries can sneak their way 
in, making it impossible to properly update the stored action (because 
simple 'yes/no's are stored...). Might need to put the most recent 
decision at the top of the list, or match starting from the bottom, just 
in case duplicates do sneak in. I am in favour though of dropping simple 
yes/no's being stored.

3) We need to decide if we are going to backport this:
- This patch will have some odd behaviour unless my initialization fixes 
go in, but I fear they cannot all be backported.

- The other option is to, for backporting purposes, instead of trying to 
show one pop-up for all the applets that share a classloader, we can 
show one pop-up for each applet (as Oracle does). That will circumvent 
the issues with classloader initialization/deinitialization.

Thanks,
-Adam
-------------- next part --------------
A non-text attachment was scrubbed...
Name: integrated-dialogue.patch
Type: text/x-patch
Size: 29098 bytes
Desc: not available
Url : http://mail.openjdk.java.net/pipermail/distro-pkg-dev/attachments/20130204/0cbc87ff/integrated-dialogue.patch 


More information about the distro-pkg-dev mailing list