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

Adam Domurad adomurad at redhat.com
Mon Feb 4 10:02:50 PST 2013


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
-------------- next part --------------
A non-text attachment was scrubbed...
Name: LockingReaderWriterTest.java
Type: text/x-java
Size: 7399 bytes
Desc: not available
Url : http://mail.openjdk.java.net/pipermail/distro-pkg-dev/attachments/20130204/bab82ac8/LockingReaderWriterTest.java 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: LockingFile.java
Type: text/x-java
Size: 4538 bytes
Desc: not available
Url : http://mail.openjdk.java.net/pipermail/distro-pkg-dev/attachments/20130204/bab82ac8/LockingFile.java 


More information about the distro-pkg-dev mailing list