Plugins with GUI

Discussion in 'Java' started by Ross, Jan 21, 2011.

  1. Ross

    Ross Guest

    Hi all.

    I'm building an application which I wish to have a plugin
    architecture.

    At present I'm following what I believe to be the typical approach. I
    have a directory called "PLUGINS" into which I can put .class files.
    One .class file being a plugin, and all of these classes implementing
    an interface called "Plugin". When the program loads, I look through
    the directory, read the class files as byte arrays, use a custom class
    that extends ClassLoader to create a Class object, create an object
    with newInstance(), check that it's an instance of Plugin, and if so,
    add it to the software.

    This all works fine. But, I want my plugins to extend JFrame, and be
    GUIs. This works fine as long as I have a single class. But, if I want
    to create anonymous inner classes to handle GUI events, it falls over.
    I get a message about there already being a class called (e.g.)
    "TestPlugin" when TestPlugin$1.class is loaded. Or something that
    produces a similar effect.

    What techniques can I use to do this? Is there some way that I could
    package up classes that make a plugin into a .jar file

    At present I'm writing JFrame classes that don't use any anonymous
    inner classes. So I have classes that extend JFrame, and also
    implement ActionListener. I keep track of all JButtons and JMenuItems
    in fields, and in public void actionPerformed( ActionEvent ae ), have
    a big if statement to work out which JButton, JMenuItem, JTextField
    etc. caused the event.

    This works, but it's not exactly a nice way of writing plugins.

    Any advice?
    Ross, Jan 21, 2011
    #1
    1. Advertising

  2. Ross

    Graham Cox Guest

    Ross <> writes:

    > What techniques can I use to do this? Is there some way that I could
    > package up classes that make a plugin into a .jar file


    Have a look at ClassLoaders.

    One way to achieve what you want would be to have each plugin be a JAR
    file containing all of your code and a required class with a well known
    name and implementing a standard interface. You then load a plugin by
    creating a new URLClassLoader for the JAR file, and using the ClassLoader
    to create an instance of this well known named class. This then acts as
    your interface into the plugin - so it could be your Plugin class, or a
    PluginFactory that is then used to startup the Plugin, or something like
    that...

    Because each JAR file has it's own separate ClassLoader there shouldn't be
    able to be any clashes of class names or resources between different
    plugins.
    --
    Graham Cox
    Graham Cox, Jan 21, 2011
    #2
    1. Advertising

  3. Ross

    markspace Guest

    On 1/21/2011 2:42 AM, Ross wrote:

    > But, if I want
    > to create anonymous inner classes to handle GUI events, it falls over.
    > I get a message about there already being a class called (e.g.)
    > "TestPlugin" when TestPlugin$1.class is loaded.



    It should work, I see no reason why not. You likely have an error in
    your code. ClassLoaders can be tricky.


    > Or something that
    > produces a similar effect.



    We'll need the EXACT error message or we can't help you. Better yet,
    give us an SSCCE which reproduces the error.

    http://sscce.org/
    markspace, Jan 21, 2011
    #3
  4. On 21/01/11 10:42, Ross wrote:
    > When the program loads, I look through
    > the directory, read the class files as byte arrays, use a custom class
    > that extends ClassLoader to create a Class object, create an object
    > with newInstance(), check that it's an instance of Plugin, and if so,
    > add it to the software.
    >
    > This all works fine. But, I want my plugins to extend JFrame, and be
    > GUIs. This works fine as long as I have a single class. But, if I want
    > to create anonymous inner classes to handle GUI events, it falls over.
    > I get a message about there already being a class called (e.g.)
    > "TestPlugin" when TestPlugin$1.class is loaded. Or something that
    > produces a similar effect.


    As others have suggested, it sounds like ClassLoader trouble. AIUI, if
    TestPlugin requires TestPlugin$1, TestPlugin's ClassLoader will be used
    to load it. Can your custom CL find the extra class? I don't see a lot
    of benefit of writing your own ClassLoader, when a properly configured
    URLClassLoader should do the job.

    (I'm sure this group discussed a built-in JDK framework for plugins a
    few months ago, that did all this work for you, but I forget what it was
    called...)
    Steven Simpson, Jan 22, 2011
    #4
  5. Ross

    Ian Shef Guest

    Steven Simpson <> wrote in news:ap1r08-1i3.ln1
    @news.simpsonst.f2s.com:

    <snip>
    >
    > (I'm sure this group discussed a built-in JDK framework for plugins a
    > few months ago, that did all this work for you, but I forget what it was
    > called...)
    >


    Perhaps you are referring to java.util.ServiceLoader ?
    Ian Shef, Jan 25, 2011
    #5
  6. On 25/01/11 19:18, Ian Shef wrote:
    > Steven Simpson <> wrote in news:ap1r08-1i3.ln1
    > @news.simpsonst.f2s.com:
    >> (I'm sure this group discussed a built-in JDK framework for plugins a
    >> few months ago, that did all this work for you, but I forget what it was
    >> called...)

    > Perhaps you are referring to java.util.ServiceLoader ?


    That's the chap.
    <http://download.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html>

    So, starting with a folder full of plugin jars, create a URLClassLoader
    for each jar, create a ServiceLoader<Plugin> for each URLClassLoader,
    and iterate over each of those to get Plugin instances. Metadata in
    each jar names the implementing class(es).
    Steven Simpson, Jan 25, 2011
    #6
  7. On 25-01-2011 15:52, Steven Simpson wrote:
    > On 25/01/11 19:18, Ian Shef wrote:
    >> Steven Simpson<> wrote in news:ap1r08-1i3.ln1
    >> @news.simpsonst.f2s.com:
    >>> (I'm sure this group discussed a built-in JDK framework for plugins a
    >>> few months ago, that did all this work for you, but I forget what it was
    >>> called...)

    >> Perhaps you are referring to java.util.ServiceLoader ?

    >
    > That's the chap.
    > <http://download.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html>
    >
    > So, starting with a folder full of plugin jars, create a URLClassLoader
    > for each jar, create a ServiceLoader<Plugin> for each URLClassLoader,
    > and iterate over each of those to get Plugin instances. Metadata in
    > each jar names the implementing class(es).


    Or if the real big hammer is needed: OSGI.

    Arne
    Arne Vajhøj, Jan 26, 2011
    #7
  8. Ross

    Roedy Green Guest

    On Fri, 21 Jan 2011 02:42:51 -0800 (PST), Ross <>
    wrote, quoted or indirectly quoted someone who said :

    >What techniques can I use to do this? Is there some way that I could
    >package up classes that make a plugin into a .jar file


    I have plug-ins in an some of my apps. You can see how I handled it
    in http://mindprod.com/products1.html#HTMLMACROS

    The core code looks like this. You could do it more simply without a
    cache.


    Each plugin extends the abstract class Macro with an expand method.

    // get the Macro-implementing class instance that will process the
    macro.
    // It implements the Macro interface with the expand method.
    // Hopefully already loaded from previous use. Will throw
    exception on trouble. Should not return null.
    final Macro macroDelegate =
    LoadCodeToProcessMacro.getMacroProcessorInstance( macroName );
    assert macroDelegate != null : "null delegate to process macro
    " + macroName;
    // F I N A L L Y ! _ E X P A N D _ T H E _ M A C R O !
    String expansion = macroDelegate.expandMacro( parms,
    fileBeingProcessed, quiet, verbose );





    ---------------------------------------------

    /*
    * @(#)LoadCodeToProcessMacro.java
    *
    * Summary: Loads code to process a given custom macro.
    *
    * Copyright: (c) 2008-2011 Roedy Green, Canadian Mind Products,
    http://mindprod.com
    *
    * Licence: This software may be copied and used freely for any
    purpose but military.
    * http://mindprod.com/contact/nonmil.html
    *
    * Requires: JDK 1.6+
    *
    * Created with: IntelliJ IDEA IDE.
    *
    * Version History:
    * 1.0 2008-07-26 - initial version. Extract and expand code in
    Include and Replacer.
    * Now does cache and looks first in custom package.
    */
    /**
    * Loads code to process a given custom macro.
    *
    * @author Roedy Green, Canadian Mind Products
    * @version 1.0 2008-07-26 - initial version. Extract and expand code
    in Include and Replacer.
    * Now does cache and looks first in custom package.
    * @since 2008-07-26
    */
    package com.mindprod.htmlmacros;

    import java.util.HashMap;

    /**
    * Loads code to process a given custom macro.
    * <p/>
    * Deals with loading the Class to process a macro, creating a fresh
    instance for each time a macro needs to be expanded.
    * It maintains a cache of previously loaded Macro Classes, not Macro
    Instances.
    * Used by Include and Replacer only.
    * <p/>
    * created with Intellij Idea
    *
    * @author Roedy Green, Canadian Mind Products
    * @version 1.0 2008-07-26 initial version. Extract and expand code in
    Include and Replacer.
    * Now does cache and looks first in custom package.
    */
    class LoadCodeToProcessMacro
    {
    // ------------------------------ CONSTANTS
    ------------------------------

    /**
    * how many macros max we might load
    */
    private static final int MACRO_CACHE_CAPACITY = 200;

    /**
    * cache of previously loaded Macro processing code classes. We
    create a fresh instance for each Macro processed.
    * Look up Class object(not Macro instance) via unqualified macro
    name.
    * We could have used the System's cache of loaded classes
    accessible via
    * ClassLoader.findLoadedClass(String) but the code would be a tad
    more complicated.
    */
    private static final HashMap<String, Class<? extends Macro>>
    macroClassCache = new HashMap<String, Class<? extends Macro>>(
    MACRO_CACHE_CAPACITY );

    // -------------------------- STATIC METHODS
    --------------------------

    /**
    * find class to process macro. Look in three places, cache,
    custom package and main package.
    *
    * @param macroName Single word Macro name. Same as class name to
    process macro.
    *
    * @return class handle to class to process the macro. Null if
    does not exist.
    */
    private static Class<? extends Macro>
    findMacroClass( String macroName )
    {
    Class<? extends Macro> macroClass = getCachedMacroClass( macroName
    );
    if ( macroClass != null )
    {
    return macroClass;
    }
    // not in custom package, look in main package.
    return loadMacroClass( macroName, "com.mindprod.htmlmacros" );
    // return with possibly null result.
    }

    /**
    * get class to process macro from cache of previously loaded
    classes.
    *
    * @param macroName Single word Macro name. Same as class name to
    process macro.
    *
    * @return class handle to class to process the macro. Null if not
    in cache.
    */
    private static Class<? extends Macro> getCachedMacroClass( String
    macroName )
    {
    return macroClassCache.get( macroName );
    }

    /**
    * get fresh instance of Class to process this macro. May have to
    load the class dynamically.
    *
    * @param macroName Single word Macro name. Same as class name to
    process macro.
    * Code may live in either
    com.mindprod.htmlmacros package or CUSTOM_MACROS_PACKAGE.
    *
    * @return interface handle to instance of the class to process
    the macro.
    * @throws InstantiationException if macro class refuses to
    Instantiate.
    * @throws IllegalAccessException if class does not have public
    access.
    * @throws ClassNotFoundException if code for class cannot be
    found or if it does not implement Macro.
    */
    static Macro getMacroProcessorInstance( String macroName ) throws
    InstantiationException, IllegalAccessException, ClassNotFoundException
    {
    Class<? extends Macro> macroClass = findMacroClass( macroName );
    if ( macroClass == null )
    {
    if ( !( macroName.length() > 0
    && Character.isUpperCase( macroName.charAt( 0 ) ) ) )
    {
    throw new IllegalArgumentException( "macro "
    +
    macroName
    +
    " should start with an
    upper case letter. Possible missing macro name." );
    }
    else
    {
    throw new ClassNotFoundException( "No such macro " +
    macroName + " or possible coding bug: The code that implements the
    Macro interface to process " + macroName + " could not be found." );
    }
    }
    try
    {
    // This cast will fail if the loaded Macro code does not
    implement Macro.
    return macroClass.newInstance();
    }
    catch ( ClassCastException e )
    {
    throw new ClassNotFoundException( "Coding bug: The code to
    process macro " + macroName + " does not implement the Macro
    interface." );
    }
    catch ( InstantiationException e )
    {
    // macro is screwed up if it won't instantiate.
    throw new InstantiationException( "Coding bug: The code to
    process macro " + macroName + " would not instantiate. It needs a
    public no-arg constructor." );
    }
    catch ( IllegalAccessException e )
    {
    // macro is screwed up if if does not have no-arg public
    constructor.
    throw new IllegalAccessException( "Coding bug: The code to
    process macro " + macroName + " refused access. It needs a public
    no-arg constructor." );
    }
    }

    /**
    * load class to process macro.
    *
    * @param macroName Single word Macro name. Same as class name
    to process macro.
    * @param packageName name of package where to look for code for
    this Macro class.
    *
    * @return class handle to class to process the macro. Null if
    does not exist.
    */
    private static Class<? extends Macro> loadMacroClass( String
    macroName, String packageName )
    {
    try
    {
    // e.g. parm to Class.forName looks like:
    "com.mindprod.htmlmacros.Measure"
    final String binaryClassName = packageName + "." + macroName;
    // Make sure the class we load implements Macro.
    final Class<? extends Macro> macroClass = Class.forName(
    binaryClassName ).asSubclass( Macro.class );
    if ( macroClass != null )
    {
    // save copy of class object for future use.
    macroClassCache.put( macroName, macroClass );
    }
    return macroClass;
    }
    catch ( ClassCastException e )
    {
    // macro is screwed up, but the code exists.
    throw new ClassCastException( "Coding bug: The code to process
    macro " + macroName + " refused access. It needs a public no-arg
    constructor." );
    }
    catch ( Exception e )
    {
    // might have been ClassNotFoundException,
    NoClassDefFoundException
    // Any problem is a failure.
    return null;
    }
    }
    }
    --
    Roedy Green Canadian Mind Products
    http://mindprod.com
    To err is human, but to really foul things up requires a computer.
    ~ Farmer's Almanac
    It is breathtaking how a misplaced comma in a computer program can
    shred megabytes of data in seconds.
    Roedy Green, Jan 26, 2011
    #8
  9. Ross

    Lew Guest

    On 01/26/2011 09:27 AM, Roedy Green wrote:
    > On Fri, 21 Jan 2011 02:42:51 -0800 (PST), Ross<>
    > wrote, quoted or indirectly quoted someone who said :
    >
    >> What techniques can I use to do this? Is there some way that I could
    >> package up classes that make a plugin into a .jar file

    >
    > I have plug-ins in an some of my apps. You can see how I handled it
    > in http://mindprod.com/products1.html#HTMLMACROS
    >
    > The core code looks like this. You could do it more simply without a
    > cache.
    >
    >
    > Each plugin extends the abstract class Macro with an expand method.
    >
    > // get the Macro-implementing class instance that will process the
    > macro.
    > // It implements the Macro interface with the expand method.
    > // Hopefully already loaded from previous use. Will throw
    > exception on trouble. Should not return null.
    > final Macro macroDelegate =
    > LoadCodeToProcessMacro.getMacroProcessorInstance( macroName );
    > assert macroDelegate != null : "null delegate to process macro
    > " + macroName;
    > // F I N A L L Y ! _ E X P A N D _ T H E _ M A C R O !
    > String expansion = macroDelegate.expandMacro( parms,
    > fileBeingProcessed, quiet, verbose );
    >
    >
    >
    >
    >
    > ---------------------------------------------
    >
    > /*
    > * @(#)LoadCodeToProcessMacro.java
    > *
    > * Summary: Loads code to process a given custom macro.
    > *
    > * Copyright: (c) 2008-2011 Roedy Green, Canadian Mind Products,
    > http://mindprod.com
    > *
    > * Licence: This software may be copied and used freely for any
    > purpose but military.
    > * http://mindprod.com/contact/nonmil.html
    > *
    > * Requires: JDK 1.6+
    > *
    > * Created with: IntelliJ IDEA IDE.
    > *
    > * Version History:
    > * 1.0 2008-07-26 - initial version. Extract and expand code in
    > Include and Replacer.
    > * Now does cache and looks first in custom package.
    > */
    > /**
    > * Loads code to process a given custom macro.
    > *
    > * @author Roedy Green, Canadian Mind Products
    > * @version 1.0 2008-07-26 - initial version. Extract and expand code
    > in Include and Replacer.
    > * Now does cache and looks first in custom package.
    > * @since 2008-07-26
    > */
    > package com.mindprod.htmlmacros;
    >
    > import java.util.HashMap;
    >
    > /**
    > * Loads code to process a given custom macro.
    > *<p/>
    > * Deals with loading the Class to process a macro, creating a fresh
    > instance for each time a macro needs to be expanded.
    > * It maintains a cache of previously loaded Macro Classes, not Macro
    > Instances.
    > * Used by Include and Replacer only.
    > *<p/>
    > * created with Intellij Idea
    > *
    > * @author Roedy Green, Canadian Mind Products
    > * @version 1.0 2008-07-26 initial version. Extract and expand code in
    > Include and Replacer.
    > * Now does cache and looks first in custom package.
    > */
    > class LoadCodeToProcessMacro
    > {
    > // ------------------------------ CONSTANTS
    > ------------------------------
    >
    > /**
    > * how many macros max we might load
    > */
    > private static final int MACRO_CACHE_CAPACITY = 200;
    >
    > /**
    > * cache of previously loaded Macro processing code classes. We
    > create a fresh instance for each Macro processed.
    > * Look up Class object(not Macro instance) via unqualified macro
    > name.
    > * We could have used the System's cache of loaded classes
    > accessible via
    > * ClassLoader.findLoadedClass(String) but the code would be a tad
    > more complicated.
    > */
    > private static final HashMap<String, Class<? extends Macro>>
    > macroClassCache = new HashMap<String, Class<? extends Macro>>(
    > MACRO_CACHE_CAPACITY );
    >
    > // -------------------------- STATIC METHODS
    > --------------------------
    >
    > /**
    > * find class to process macro. Look in three places, cache,
    > custom package and main package.
    > *
    > * @param macroName Single word Macro name. Same as class name to
    > process macro.
    > *
    > * @return class handle to class to process the macro. Null if
    > does not exist.
    > */
    > private static Class<? extends Macro>
    > findMacroClass( String macroName )
    > {
    > Class<? extends Macro> macroClass = getCachedMacroClass( macroName
    > );
    > if ( macroClass != null )
    > {
    > return macroClass;
    > }
    > // not in custom package, look in main package.
    > return loadMacroClass( macroName, "com.mindprod.htmlmacros" );
    > // return with possibly null result.
    > }
    >
    > /**
    > * get class to process macro from cache of previously loaded
    > classes.
    > *
    > * @param macroName Single word Macro name. Same as class name to
    > process macro.
    > *
    > * @return class handle to class to process the macro. Null if not
    > in cache.
    > */
    > private static Class<? extends Macro> getCachedMacroClass( String
    > macroName )
    > {
    > return macroClassCache.get( macroName );
    > }
    >
    > /**
    > * get fresh instance of Class to process this macro. May have to
    > load the class dynamically.
    > *
    > * @param macroName Single word Macro name. Same as class name to
    > process macro.
    > * Code may live in either
    > com.mindprod.htmlmacros package or CUSTOM_MACROS_PACKAGE.
    > *
    > * @return interface handle to instance of the class to process
    > the macro.
    > * @throws InstantiationException if macro class refuses to
    > Instantiate.
    > * @throws IllegalAccessException if class does not have public
    > access.
    > * @throws ClassNotFoundException if code for class cannot be
    > found or if it does not implement Macro.
    > */
    > static Macro getMacroProcessorInstance( String macroName ) throws
    > InstantiationException, IllegalAccessException, ClassNotFoundException
    > {
    > Class<? extends Macro> macroClass = findMacroClass( macroName );
    > if ( macroClass == null )
    > {
    > if ( !( macroName.length()> 0
    > && Character.isUpperCase( macroName.charAt( 0 ) ) ) )
    > {
    > throw new IllegalArgumentException( "macro "
    > +
    > macroName
    > +
    > " should start with an
    > upper case letter. Possible missing macro name." );
    > }
    > else
    > {
    > throw new ClassNotFoundException( "No such macro " +
    > macroName + " or possible coding bug: The code that implements the
    > Macro interface to process " + macroName + " could not be found." );
    > }
    > }
    > try
    > {
    > // This cast will fail if the loaded Macro code does not
    > implement Macro.
    > return macroClass.newInstance();
    > }
    > catch ( ClassCastException e )
    > {
    > throw new ClassNotFoundException( "Coding bug: The code to
    > process macro " + macroName + " does not implement the Macro
    > interface." );
    > }
    > catch ( InstantiationException e )
    > {
    > // macro is screwed up if it won't instantiate.
    > throw new InstantiationException( "Coding bug: The code to
    > process macro " + macroName + " would not instantiate. It needs a
    > public no-arg constructor." );
    > }
    > catch ( IllegalAccessException e )
    > {
    > // macro is screwed up if if does not have no-arg public
    > constructor.
    > throw new IllegalAccessException( "Coding bug: The code to
    > process macro " + macroName + " refused access. It needs a public
    > no-arg constructor." );
    > }
    > }
    >
    > /**
    > * load class to process macro.
    > *
    > * @param macroName Single word Macro name. Same as class name
    > to process macro.
    > * @param packageName name of package where to look for code for
    > this Macro class.
    > *
    > * @return class handle to class to process the macro. Null if
    > does not exist.
    > */
    > private static Class<? extends Macro> loadMacroClass( String
    > macroName, String packageName )
    > {
    > try
    > {
    > // e.g. parm to Class.forName looks like:
    > "com.mindprod.htmlmacros.Measure"
    > final String binaryClassName = packageName + "." + macroName;
    > // Make sure the class we load implements Macro.
    > final Class<? extends Macro> macroClass = Class.forName(
    > binaryClassName ).asSubclass( Macro.class );
    > if ( macroClass != null )
    > {
    > // save copy of class object for future use.
    > macroClassCache.put( macroName, macroClass );
    > }
    > return macroClass;
    > }
    > catch ( ClassCastException e )
    > {
    > // macro is screwed up, but the code exists.
    > throw new ClassCastException( "Coding bug: The code to process
    > macro " + macroName + " refused access. It needs a public no-arg
    > constructor." );
    > }
    > catch ( Exception e )
    > {
    > // might have been ClassNotFoundException,
    > NoClassDefFoundException
    > // Any problem is a failure.
    > return null;
    > }
    > }
    > }


    That is a nice use of 'assert' and one I wouldn't have thought of.

    It's a widely-ignored truism that software must be deployed to be useful.
    It's hard to bridge deployment aspects to code; this use of 'assert' provides
    an elegant and sturdy bridge.

    --
    Lew
    Ceci n'est pas une pipe.
    Lew, Jan 26, 2011
    #9
  10. On 26/01/2011 16:03, Lew allegedly wrote:
    > On 01/26/2011 09:27 AM, Roedy Green wrote:
    >> On Fri, 21 Jan 2011 02:42:51 -0800 (PST), Ross<>
    >> wrote, quoted or indirectly quoted someone who said :
    >>
    >>> What techniques can I use to do this? Is there some way that I could
    >>> package up classes that make a plugin into a .jar file

    >>
    >> I have plug-ins in an some of my apps. You can see how I handled it
    >> in http://mindprod.com/products1.html#HTMLMACROS
    >>
    >> The core code looks like this. You could do it more simply without a
    >> cache.
    >>
    >>
    >> Each plugin extends the abstract class Macro with an expand method.
    >>
    >> // get the Macro-implementing class instance that will process the
    >> macro.
    >> // It implements the Macro interface with the expand method.
    >> // Hopefully already loaded from previous use. Will throw
    >> exception on trouble. Should not return null.
    >> final Macro macroDelegate =
    >> LoadCodeToProcessMacro.getMacroProcessorInstance( macroName );
    >> assert macroDelegate != null : "null delegate to process macro
    >> " + macroName;
    >> // F I N A L L Y ! _ E X P A N D _ T H E _ M A C R O !
    >> String expansion = macroDelegate.expandMacro( parms,
    >> fileBeingProcessed, quiet, verbose );
    >>
    >>
    >>
    >>
    >>
    >> ---------------------------------------------
    >>
    >> /*
    >> * @(#)LoadCodeToProcessMacro.java
    >> *
    >> * Summary: Loads code to process a given custom macro.
    >> *
    >> * Copyright: (c) 2008-2011 Roedy Green, Canadian Mind Products,
    >> http://mindprod.com
    >> *
    >> * Licence: This software may be copied and used freely for any
    >> purpose but military.
    >> * http://mindprod.com/contact/nonmil.html
    >> *
    >> * Requires: JDK 1.6+
    >> *
    >> * Created with: IntelliJ IDEA IDE.
    >> *
    >> * Version History:
    >> * 1.0 2008-07-26 - initial version. Extract and expand code in
    >> Include and Replacer.
    >> * Now does cache and looks first in custom package.
    >> */
    >> /**
    >> * Loads code to process a given custom macro.
    >> *
    >> * @author Roedy Green, Canadian Mind Products
    >> * @version 1.0 2008-07-26 - initial version. Extract and expand code
    >> in Include and Replacer.
    >> * Now does cache and looks first in custom package.
    >> * @since 2008-07-26
    >> */
    >> package com.mindprod.htmlmacros;
    >>
    >> import java.util.HashMap;
    >>
    >> /**
    >> * Loads code to process a given custom macro.
    >> *<p/>
    >> * Deals with loading the Class to process a macro, creating a fresh
    >> instance for each time a macro needs to be expanded.
    >> * It maintains a cache of previously loaded Macro Classes, not Macro
    >> Instances.
    >> * Used by Include and Replacer only.
    >> *<p/>
    >> * created with Intellij Idea
    >> *
    >> * @author Roedy Green, Canadian Mind Products
    >> * @version 1.0 2008-07-26 initial version. Extract and expand code in
    >> Include and Replacer.
    >> * Now does cache and looks first in custom package.
    >> */
    >> class LoadCodeToProcessMacro
    >> {
    >> // ------------------------------ CONSTANTS
    >> ------------------------------
    >>
    >> /**
    >> * how many macros max we might load
    >> */
    >> private static final int MACRO_CACHE_CAPACITY = 200;
    >>
    >> /**
    >> * cache of previously loaded Macro processing code classes. We
    >> create a fresh instance for each Macro processed.
    >> * Look up Class object(not Macro instance) via unqualified macro
    >> name.
    >> * We could have used the System's cache of loaded classes
    >> accessible via
    >> * ClassLoader.findLoadedClass(String) but the code would be a tad
    >> more complicated.
    >> */
    >> private static final HashMap<String, Class<? extends Macro>>
    >> macroClassCache = new HashMap<String, Class<? extends Macro>>(
    >> MACRO_CACHE_CAPACITY );
    >>
    >> // -------------------------- STATIC METHODS
    >> --------------------------
    >>
    >> /**
    >> * find class to process macro. Look in three places, cache,
    >> custom package and main package.
    >> *
    >> * @param macroName Single word Macro name. Same as class name to
    >> process macro.
    >> *
    >> * @return class handle to class to process the macro. Null if
    >> does not exist.
    >> */
    >> private static Class<? extends Macro>
    >> findMacroClass( String macroName )
    >> {
    >> Class<? extends Macro> macroClass = getCachedMacroClass( macroName
    >> );
    >> if ( macroClass != null )
    >> {
    >> return macroClass;
    >> }
    >> // not in custom package, look in main package.
    >> return loadMacroClass( macroName, "com.mindprod.htmlmacros" );
    >> // return with possibly null result.
    >> }
    >>
    >> /**
    >> * get class to process macro from cache of previously loaded
    >> classes.
    >> *
    >> * @param macroName Single word Macro name. Same as class name to
    >> process macro.
    >> *
    >> * @return class handle to class to process the macro. Null if not
    >> in cache.
    >> */
    >> private static Class<? extends Macro> getCachedMacroClass( String
    >> macroName )
    >> {
    >> return macroClassCache.get( macroName );
    >> }
    >>
    >> /**
    >> * get fresh instance of Class to process this macro. May have to
    >> load the class dynamically.
    >> *
    >> * @param macroName Single word Macro name. Same as class name to
    >> process macro.
    >> * Code may live in either
    >> com.mindprod.htmlmacros package or CUSTOM_MACROS_PACKAGE.
    >> *
    >> * @return interface handle to instance of the class to process
    >> the macro.
    >> * @throws InstantiationException if macro class refuses to
    >> Instantiate.
    >> * @throws IllegalAccessException if class does not have public
    >> access.
    >> * @throws ClassNotFoundException if code for class cannot be
    >> found or if it does not implement Macro.
    >> */
    >> static Macro getMacroProcessorInstance( String macroName ) throws
    >> InstantiationException, IllegalAccessException, ClassNotFoundException
    >> {
    >> Class<? extends Macro> macroClass = findMacroClass( macroName );
    >> if ( macroClass == null )
    >> {
    >> if ( !( macroName.length()> 0
    >> && Character.isUpperCase( macroName.charAt( 0 ) ) ) )
    >> {
    >> throw new IllegalArgumentException( "macro "
    >> +
    >> macroName
    >> +
    >> " should start with an
    >> upper case letter. Possible missing macro name." );
    >> }
    >> else
    >> {
    >> throw new ClassNotFoundException( "No such macro " +
    >> macroName + " or possible coding bug: The code that implements the
    >> Macro interface to process " + macroName + " could not be found." );
    >> }
    >> }
    >> try
    >> {
    >> // This cast will fail if the loaded Macro code does not
    >> implement Macro.
    >> return macroClass.newInstance();
    >> }
    >> catch ( ClassCastException e )
    >> {
    >> throw new ClassNotFoundException( "Coding bug: The code to
    >> process macro " + macroName + " does not implement the Macro
    >> interface." );
    >> }
    >> catch ( InstantiationException e )
    >> {
    >> // macro is screwed up if it won't instantiate.
    >> throw new InstantiationException( "Coding bug: The code to
    >> process macro " + macroName + " would not instantiate. It needs a
    >> public no-arg constructor." );
    >> }
    >> catch ( IllegalAccessException e )
    >> {
    >> // macro is screwed up if if does not have no-arg public
    >> constructor.
    >> throw new IllegalAccessException( "Coding bug: The code to
    >> process macro " + macroName + " refused access. It needs a public
    >> no-arg constructor." );
    >> }
    >> }
    >>
    >> /**
    >> * load class to process macro.
    >> *
    >> * @param macroName Single word Macro name. Same as class name
    >> to process macro.
    >> * @param packageName name of package where to look for code for
    >> this Macro class.
    >> *
    >> * @return class handle to class to process the macro. Null if
    >> does not exist.
    >> */
    >> private static Class<? extends Macro> loadMacroClass( String
    >> macroName, String packageName )
    >> {
    >> try
    >> {
    >> // e.g. parm to Class.forName looks like:
    >> "com.mindprod.htmlmacros.Measure"
    >> final String binaryClassName = packageName + "." + macroName;
    >> // Make sure the class we load implements Macro.
    >> final Class<? extends Macro> macroClass = Class.forName(
    >> binaryClassName ).asSubclass( Macro.class );
    >> if ( macroClass != null )
    >> {
    >> // save copy of class object for future use.
    >> macroClassCache.put( macroName, macroClass );
    >> }
    >> return macroClass;
    >> }
    >> catch ( ClassCastException e )
    >> {
    >> // macro is screwed up, but the code exists.
    >> throw new ClassCastException( "Coding bug: The code to process
    >> macro " + macroName + " refused access. It needs a public no-arg
    >> constructor." );
    >> }
    >> catch ( Exception e )
    >> {
    >> // might have been ClassNotFoundException,
    >> NoClassDefFoundException
    >> // Any problem is a failure.
    >> return null;
    >> }
    >> }
    >> }

    >
    > That is a nice use of 'assert' and one I wouldn't have thought of.
    >
    > It's a widely-ignored truism that software must be deployed to be
    > useful. It's hard to bridge deployment aspects to code; this use of
    > 'assert' provides an elegant and sturdy bridge.
    >


    Wait, wat?

    This?
    > assert macroDelegate != null : "null delegate to process macro
    > " + macroName;


    How so?
    Daniele Futtorovic, Jan 26, 2011
    #10
  11. Ross

    Lew Guest

    Lew allegedly wrote:
    >> That is a nice use of 'assert' and one I wouldn't have thought of.
    >>
    >> It's a widely-ignored truism that software must be deployed to be
    >> useful. It's hard to bridge deployment aspects to code; this use of
    >> 'assert' provides an elegant and sturdy bridge.


    Daniele Futtorovic wrote:
    > Wait, wat?
    >
    > This?
    >> assert macroDelegate != null : "null delegate to process macro
    >> " + macroName;


    Yes. It *is* the sole and only use of 'assert' in the quoted post, so it
    could hardly be anything else, could it?

    > How so?


    First of all, let's restore the context. Roedy Green wrote:
    > final Macro macroDelegate =
    > LoadCodeToProcessMacro.getMacroProcessorInstance( macroName );
    > assert macroDelegate != null : "null delegate to process macro
    > " + macroName;


    This is a pattern quite prevalent in dependency injection (DI), JNDI lookups,
    JDBC connections and the like. The 'getFooInstance()' factory or declarative
    equivalent pulls in some deployment-specific delegate or handler, just as in
    this example, often specified in a resource file or deployment descriptor.

    If that delegate is not correctly deployed or specified, you will get back a
    'null' or throw an exception. (Something like this will usually be
    exception-enforced.)

    If there is an issue, this gets rectified pretty much once per deployment.
    After that, it just works - the correct delegate is deployed where it belongs.

    A check 'if ( macroDelegate == null )' at that point is not helpful. It's
    just wasted cycles. A production instance can shut off 'assert' checking
    (globally or just for the class), safe in the assertion that once fixed, the
    problem is gone. Poof! No overhead.

    If the problem recurs, turn on 'assert' and repeat. This should be rare.

    So it's elegant - one small 'assert' prevents a raft of difficulties. It's
    sturdy - you can guarantee success across a range of scenarios.

    Q.E. effing D.

    --
    Lew
    Ceci n'est pas une pipe.
    Lew, Jan 26, 2011
    #11
  12. On 26/01/2011 21:12, Lew allegedly wrote:
    > Lew allegedly wrote:
    >>> That is a nice use of 'assert' and one I wouldn't have thought of.
    >>>
    >>> It's a widely-ignored truism that software must be deployed to be
    >>> useful. It's hard to bridge deployment aspects to code; this use of
    >>> 'assert' provides an elegant and sturdy bridge.

    >
    > Daniele Futtorovic wrote:
    >> Wait, wat?
    >>
    >> This?
    >>> assert macroDelegate != null : "null delegate to process macro
    >>> " + macroName;

    >
    > Yes. It *is* the sole and only use of 'assert' in the quoted post, so it
    > could hardly be anything else, could it?


    Indeed. That's what got me so puzzled. What other kind of use of
    "assert" have you ever encountered?

    >> How so?

    >
    > First of all, let's restore the context. Roedy Green wrote:
    >> final Macro macroDelegate =
    >> LoadCodeToProcessMacro.getMacroProcessorInstance( macroName );
    >> assert macroDelegate != null : "null delegate to process macro
    >> " + macroName;

    >
    > This is a pattern quite prevalent in dependency injection (DI), JNDI
    > lookups, JDBC connections and the like. The 'getFooInstance()' factory
    > or declarative equivalent pulls in some deployment-specific delegate or
    > handler, just as in this example, often specified in a resource file or
    > deployment descriptor.
    >
    > If that delegate is not correctly deployed or specified, you will get
    > back a 'null' or throw an exception. (Something like this will usually
    > be exception-enforced.)
    >
    > If there is an issue, this gets rectified pretty much once per
    > deployment. After that, it just works - the correct delegate is deployed
    > where it belongs.
    >
    > A check 'if ( macroDelegate == null )' at that point is not helpful.
    > It's just wasted cycles. A production instance can shut off 'assert'
    > checking (globally or just for the class), safe in the assertion that
    > once fixed, the problem is gone. Poof! No overhead.
    >
    > If the problem recurs, turn on 'assert' and repeat. This should be rare.
    >
    > So it's elegant - one small 'assert' prevents a raft of difficulties.
    > It's sturdy - you can guarantee success across a range of scenarios.
    >
    > Q.E. effing D.


    Hardly.

    You mention deployment. Either the producer of that code has control
    over the JVM parameters, or they haven't.
    If they haven't, then they cannot assert that assertions are enabled.
    If they have, then they can. But how do they switch them off if they're
    switched on as part of deployment? If they're not switched off, they're
    no better than a check and a throw instruction. If they want to switch
    them off after, say, the "tuning phase", then they need either some
    post-deployment intervention (and hence to exceed the scope you have
    set), or a supplemental mechanism that turns them off based on some
    action of an admin -- some configuration utility for instance. How could
    that possibly be less a bloat that an added null check?
    Furthermore, if what the service provider kind of code needs to succeed
    is dynamic, part of the environment, then I don't see at which point you
    could ever decide that the check isn't necessary anymore. The
    environment can change at any time, due to migrations, restores, updates
    or whatever.

    In other words, the "assert" in this particular case is at best no
    better than an if, and at worst useless, in upon or after deployment.

    I would argue that "assert" is destined to enforce code invariants and
    is most useful during the development phase. It is especially useful at
    the boundaries of different components interacting with each other, to
    enforce component contracts (especially in light of possible future
    modifications of a component by cow-workers).

    df.
    Daniele Futtorovic, Jan 26, 2011
    #12
  13. Ross

    Lew Guest

    On 01/26/2011 05:22 PM, Daniele Futtorovic wrote:
    > On 26/01/2011 21:12, Lew allegedly wrote:
    >> Lew allegedly wrote:
    >>>> That is a nice use of 'assert' and one I wouldn't have thought of.
    >>>>
    >>>> It's a widely-ignored truism that software must be deployed to be
    >>>> useful. It's hard to bridge deployment aspects to code; this use of
    >>>> 'assert' provides an elegant and sturdy bridge.

    >>
    >> Daniele Futtorovic wrote:
    >>> Wait, wat?
    >>>
    >>> This?
    >>>> assert macroDelegate != null : "null delegate to process macro
    >>>> " + macroName;

    >>
    >> Yes. It *is* the sole and only use of 'assert' in the quoted post, so it
    >> could hardly be anything else, could it?

    >
    > Indeed. That's what got me so puzzled. What other kind of use of
    > "assert" have you ever encountered?
    >
    >>> How so?

    >>
    >> First of all, let's restore the context. Roedy Green wrote:
    >>> final Macro macroDelegate =
    >>> LoadCodeToProcessMacro.getMacroProcessorInstance( macroName );
    >>> assert macroDelegate != null : "null delegate to process macro
    >>> " + macroName;

    >>
    >> This is a pattern quite prevalent in dependency injection (DI), JNDI
    >> lookups, JDBC connections and the like. The 'getFooInstance()' factory
    >> or declarative equivalent pulls in some deployment-specific delegate or
    >> handler, just as in this example, often specified in a resource file or
    >> deployment descriptor.
    >>
    >> If that delegate is not correctly deployed or specified, you will get
    >> back a 'null' or throw an exception. (Something like this will usually
    >> be exception-enforced.)
    >>
    >> If there is an issue, this gets rectified pretty much once per
    >> deployment. After that, it just works - the correct delegate is deployed
    >> where it belongs.
    >>
    >> A check 'if ( macroDelegate == null )' at that point is not helpful.
    >> It's just wasted cycles. A production instance can shut off 'assert'
    >> checking (globally or just for the class), safe in the assertion that
    >> once fixed, the problem is gone. Poof! No overhead.
    >>
    >> If the problem recurs, turn on 'assert' and repeat. This should be rare.
    >>
    >> So it's elegant - one small 'assert' prevents a raft of difficulties.
    >> It's sturdy - you can guarantee success across a range of scenarios.
    >>
    >> Q.E. effing D.

    >
    > Hardly.
    >
    > You mention deployment. Either the producer of that code has control
    > over the JVM parameters, or they haven't.


    They don't, but that's not the point.

    They know that the deployer has control, and can instrument the code, with
    'assert', for their use.

    > If they haven't, then they cannot assert that assertions are enabled.


    Nor need to.

    > If they have, then they can. But how do they switch them off if they're


    The ops personnel do that, duh.

    > switched on as part of deployment? If they're not switched off, they're
    > no better than a check and a throw instruction. If they want to switch


    They are different from check and throw, neither better nor worse. The
    purpose of exceptions differs from that of assertions.

    > them off after, say, the "tuning phase", then they need either some
    > post-deployment intervention (and hence to exceed the scope you have


    Well, duh, again. That's my point - the ops personnel switch assertions on
    and off according to need. Have them on the first day while you make sure
    everything is in place, then turn them off when the configuration is correct.

    > set), or a supplemental mechanism that turns them off based on some
    > action of an admin -- some configuration utility for instance. How could
    > that possibly be less a bloat that an added null check?


    It's not "bloat", since "bloat" is defined as excess code that doesn't serve a
    purpose. This is a tiny injection of code that confirms the presence of an
    algorithmic necessity. It's the exact opposite of "bloat".

    As for your so-called "supplemental mechanism", I'm not positing any mechanism
    beyond that provided by the JVM itself, which of course is always under the
    control of an admin's actions. What else would it be?

    > Furthermore, if what the service provider kind of code needs to succeed
    > is dynamic, part of the environment, then I don't see at which point you
    > could ever decide that the check isn't necessary anymore. The
    > environment can change at any time, due to migrations, restores, updates
    > or whatever.


    I'm not proposing that this technique, or any other, is universally
    applicable. Like any other tool in the programmer's kit, it must be
    considered within the boundaries of its use case. I spoke only of the use
    case where the environmental configuration has roughly deployment lifespan.
    You do speak of a different use case where the technique might not apply so
    neatly.

    > In other words, the "assert" in this particular case is at best no
    > better than an if, and at worst useless, in upon or after deployment.


    It is better than an 'if', in the first place because the overhead of the 'if'
    can be eliminated by turning off 'assert', in the second because 'assert' and
    'if' serve different purposes. The 'if' would enforce the invariant, the
    'assert' proves it. Not the same.

    In the more conventional application of asserting algorithmic invariants,
    you'll usually see both an 'if' and an 'assert'.

    > I would argue that "assert" is destined to enforce code invariants and
    > is most useful during the development phase. It is especially useful at


    Also the troubleshooting phase in production when something goes wrong. You
    can turn on 'assert' and see if an invariant broke.

    With this new application inspired by Roedy's example, you can also do that
    with environmental invariants.

    > the boundaries of different components interacting with each other, to
    > enforce component contracts (especially in light of possible future
    > modifications of a component by cow-workers).


    Or now, in light of possible future modifications of a component's environment.

    --
    Lew
    Ceci n'est pas une pipe.
    Lew, Jan 26, 2011
    #13
  14. On 27/01/2011 00:34, Lew allegedly wrote:
    > On 01/26/2011 05:22 PM, Daniele Futtorovic wrote:
    >> On 26/01/2011 21:12, Lew allegedly wrote:
    >>> First of all, let's restore the context. Roedy Green wrote:
    >>>> final Macro macroDelegate =
    >>>> LoadCodeToProcessMacro.getMacroProcessorInstance( macroName );
    >>>> assert macroDelegate != null : "null delegate to process macro
    >>>> " + macroName;
    >>>
    >>> This is a pattern quite prevalent in dependency injection (DI), JNDI
    >>> lookups, JDBC connections and the like. The 'getFooInstance()' factory
    >>> or declarative equivalent pulls in some deployment-specific delegate or
    >>> handler, just as in this example, often specified in a resource file or
    >>> deployment descriptor.
    >>>
    >>> If that delegate is not correctly deployed or specified, you will get
    >>> back a 'null' or throw an exception. (Something like this will usually
    >>> be exception-enforced.)
    >>>
    >>> If there is an issue, this gets rectified pretty much once per
    >>> deployment. After that, it just works - the correct delegate is deployed
    >>> where it belongs.
    >>>
    >>> A check 'if ( macroDelegate == null )' at that point is not helpful.
    >>> It's just wasted cycles. A production instance can shut off 'assert'
    >>> checking (globally or just for the class), safe in the assertion that
    >>> once fixed, the problem is gone. Poof! No overhead.
    >>>
    >>> If the problem recurs, turn on 'assert' and repeat. This should be rare.
    >>>
    >>> So it's elegant - one small 'assert' prevents a raft of difficulties.
    >>> It's sturdy - you can guarantee success across a range of scenarios.
    >>>
    >>> Q.E. effing D.

    >>
    >> Hardly.
    >>
    >> You mention deployment. Either the producer of that code has control
    >> over the JVM parameters, or they haven't.

    >
    > They don't, but that's not the point.
    >
    > They know that the deployer has control, and can instrument the code,
    > with 'assert', for their use.
    >
    >> If they haven't, then they cannot assert that assertions are enabled.

    >
    > Nor need to.
    >
    >> If they have, then they can. But how do they switch them off if they're

    >
    > The ops personnel do that, duh.
    >
    >> switched on as part of deployment? If they're not switched off, they're
    >> no better than a check and a throw instruction. If they want to switch

    >
    > They are different from check and throw, neither better nor worse. The
    > purpose of exceptions differs from that of assertions.


    An assertion is nothing but a check and throw you can neatly turn off.


    >> them off after, say, the "tuning phase", then they need either some
    >> post-deployment intervention (and hence to exceed the scope you have

    >
    > Well, duh, again. That's my point - the ops personnel switch assertions
    > on and off according to need. Have them on the first day while you make
    > sure everything is in place, then turn them off when the configuration
    > is correct.
    >
    >> set), or a supplemental mechanism that turns them off based on some
    >> action of an admin -- some configuration utility for instance. How could
    >> that possibly be less a bloat that an added null check?

    >
    > It's not "bloat", since "bloat" is defined as excess code that doesn't
    > serve a purpose. This is a tiny injection of code that confirms the
    > presence of an algorithmic necessity. It's the exact opposite of "bloat".


    I accept that definition. Point granted.


    > As for your so-called "supplemental mechanism", I'm not positing any
    > mechanism beyond that provided by the JVM itself, which of course is
    > always under the control of an admin's actions. What else would it be?


    A UI for editing some kind of startup script or service definition, for
    instance. But we digress.


    >> Furthermore, if what the service provider kind of code needs to succeed
    >> is dynamic, part of the environment, then I don't see at which point you
    >> could ever decide that the check isn't necessary anymore. The
    >> environment can change at any time, due to migrations, restores, updates
    >> or whatever.

    >
    > I'm not proposing that this technique, or any other, is universally
    > applicable. Like any other tool in the programmer's kit, it must be
    > considered within the boundaries of its use case. I spoke only of the
    > use case where the environmental configuration has roughly deployment
    > lifespan. You do speak of a different use case where the technique might
    > not apply so neatly.
    >
    >> In other words, the "assert" in this particular case is at best no
    >> better than an if, and at worst useless, in upon or after deployment.

    >
    > It is better than an 'if', in the first place because the overhead of
    > the 'if' can be eliminated by turning off 'assert', in the second
    > because 'assert' and 'if' serve different purposes. The 'if' would
    > enforce the invariant, the 'assert' proves it. Not the same.


    if + throw proves it too.


    > In the more conventional application of asserting algorithmic
    > invariants, you'll usually see both an 'if' and an 'assert'.
    >
    >> I would argue that "assert" is destined to enforce code invariants and
    >> is most useful during the development phase. It is especially useful at

    >
    > Also the troubleshooting phase in production when something goes wrong.
    > You can turn on 'assert' and see if an invariant broke.
    >
    > With this new application inspired by Roedy's example, you can also do
    > that with environmental invariants.
    >
    >> the boundaries of different components interacting with each other, to
    >> enforce component contracts (especially in light of possible future
    >> modifications of a component by cow-workers).

    >
    > Or now, in light of possible future modifications of a component's
    > environment.


    This is precisely what I don't agree with. I'm grateful that you express
    it so pointedly, as it allows us to focus the discussion.

    So how's what you're proposing going to play out?
    Our guys go to the client and install our app. They enable assertions,
    wiggle it a bit and, satisfied with what they see, switch them off and
    go home.
    Two weeks later, we get a call from the client about some obscure error
    message or stack trace in the log, or, far worse, a testimony to the
    effect that something just "doesn't work". And I'm supposed to tell
    them: "hey wait, please enable assertions, please put your _production
    system_ in a state I suspect is _broken_ (and how am I supposed to know
    that this broken state won't lead to the corruption of some persistent
    data?) and tell me if you see a big error message"?

    No. Bloody. Way. The app's environment, by definition, is something that
    is subject to change and outside of my control. If _my_ app makes
    assumptions with respects to its environment, and these assumptions
    don't hold, then I want it to *break reliably*.
    I'll often explicitly throw AssertionErrors in such cases. Yes, within
    an if block. I don't give a shit about that added JMP. It's well worth
    it and anyway I've already got those checks all over the place, whenever
    I manipulate arrays or in dozens of methods, many of which I'll have
    written myself.

    The assert functionality does not provide the kind of reliability I
    demand for that purpose. It is a very useful mechanism, but only before
    delivery, during development and integration.

    df.
    Daniele Futtorovic, Jan 27, 2011
    #14
  15. Ross

    Roedy Green Guest

    On Thu, 27 Jan 2011 01:53:13 +0100, Daniele Futtorovic
    <> wrote, quoted or indirectly quoted
    someone who said :

    >On 27/01/2011 00:34, Lew allegedly wrote:
    >> On 01/26/2011 05:22 PM, Daniele Futtorovic wrote:
    >>> On 26/01/2011 21:12, Lew allegedly wrote:
    >>>> First of all, let's restore the context. Roedy Green wrote:
    >>>>> final Macro macroDelegate =
    >>>>> LoadCodeToProcessMacro.getMacroProcessorInstance( macroName );
    >>>>> assert macroDelegate != null : "null delegate to process macro
    >>>>> " + macroName;
    >>>>
    >>>> This is a pattern quite prevalent in dependency injection (DI), JNDI
    >>>> lookups, JDBC connections and the like. The 'getFooInstance()' factory
    >>>> or declarative equivalent pulls in some deployment-specific delegate or
    >>>> handler, just as in this example, often specified in a resource file or
    >>>> deployment descriptor.
    >>>>
    >>>> If that delegate is not correctly deployed or specified, you will get
    >>>> back a 'null' or throw an exception. (Something like this will usually
    >>>> be exception-enforced.)
    >>>>
    >>>> If there is an issue, this gets rectified pretty much once per
    >>>> deployment. After that, it just works - the correct delegate is deployed
    >>>> where it belongs.
    >>>>
    >>>> A check 'if ( macroDelegate == null )' at that point is not helpful.
    >>>> It's just wasted cycles. A production instance can shut off 'assert'
    >>>> checking (globally or just for the class), safe in the assertion that
    >>>> once fixed, the problem is gone. Poof! No overhead.
    >>>>
    >>>> If the problem recurs, turn on 'assert' and repeat. This should be rare.
    >>>>
    >>>> So it's elegant - one small 'assert' prevents a raft of difficulties.
    >>>> It's sturdy - you can guarantee success across a range of scenarios.
    >>>>
    >>>> Q.E. effing D.
    >>>
    >>> Hardly.
    >>>
    >>> You mention deployment. Either the producer of that code has control
    >>> over the JVM parameters, or they haven't.

    >>
    >> They don't, but that's not the point.
    >>
    >> They know that the deployer has control, and can instrument the code,
    >> with 'assert', for their use.
    >>
    >>> If they haven't, then they cannot assert that assertions are enabled.

    >>
    >> Nor need to.
    >>
    >>> If they have, then they can. But how do they switch them off if they're

    >>
    >> The ops personnel do that, duh.
    >>
    >>> switched on as part of deployment? If they're not switched off, they're
    >>> no better than a check and a throw instruction. If they want to switch

    >>
    >> They are different from check and throw, neither better nor worse. The
    >> purpose of exceptions differs from that of assertions.

    >
    >An assertion is nothing but a check and throw you can neatly turn off.
    >
    >
    >>> them off after, say, the "tuning phase", then they need either some
    >>> post-deployment intervention (and hence to exceed the scope you have

    >>
    >> Well, duh, again. That's my point - the ops personnel switch assertions
    >> on and off according to need. Have them on the first day while you make
    >> sure everything is in place, then turn them off when the configuration
    >> is correct.
    >>
    >>> set), or a supplemental mechanism that turns them off based on some
    >>> action of an admin -- some configuration utility for instance. How could
    >>> that possibly be less a bloat that an added null check?

    >>
    >> It's not "bloat", since "bloat" is defined as excess code that doesn't
    >> serve a purpose. This is a tiny injection of code that confirms the
    >> presence of an algorithmic necessity. It's the exact opposite of "bloat".

    >
    >I accept that definition. Point granted.
    >
    >
    >> As for your so-called "supplemental mechanism", I'm not positing any
    >> mechanism beyond that provided by the JVM itself, which of course is
    >> always under the control of an admin's actions. What else would it be?

    >
    >A UI for editing some kind of startup script or service definition, for
    >instance. But we digress.
    >
    >
    >>> Furthermore, if what the service provider kind of code needs to succeed
    >>> is dynamic, part of the environment, then I don't see at which point you
    >>> could ever decide that the check isn't necessary anymore. The
    >>> environment can change at any time, due to migrations, restores, updates
    >>> or whatever.

    >>
    >> I'm not proposing that this technique, or any other, is universally
    >> applicable. Like any other tool in the programmer's kit, it must be
    >> considered within the boundaries of its use case. I spoke only of the
    >> use case where the environmental configuration has roughly deployment
    >> lifespan. You do speak of a different use case where the technique might
    >> not apply so neatly.
    >>
    >>> In other words, the "assert" in this particular case is at best no
    >>> better than an if, and at worst useless, in upon or after deployment.

    >>
    >> It is better than an 'if', in the first place because the overhead of
    >> the 'if' can be eliminated by turning off 'assert', in the second
    >> because 'assert' and 'if' serve different purposes. The 'if' would
    >> enforce the invariant, the 'assert' proves it. Not the same.

    >
    >if + throw proves it too.
    >
    >
    >> In the more conventional application of asserting algorithmic
    >> invariants, you'll usually see both an 'if' and an 'assert'.
    >>
    >>> I would argue that "assert" is destined to enforce code invariants and
    >>> is most useful during the development phase. It is especially useful at

    >>
    >> Also the troubleshooting phase in production when something goes wrong.
    >> You can turn on 'assert' and see if an invariant broke.
    >>
    >> With this new application inspired by Roedy's example, you can also do
    >> that with environmental invariants.
    >>
    >>> the boundaries of different components interacting with each other, to
    >>> enforce component contracts (especially in light of possible future
    >>> modifications of a component by cow-workers).

    >>
    >> Or now, in light of possible future modifications of a component's
    >> environment.

    >
    >This is precisely what I don't agree with. I'm grateful that you express
    >it so pointedly, as it allows us to focus the discussion.
    >
    >So how's what you're proposing going to play out?
    >Our guys go to the client and install our app. They enable assertions,
    >wiggle it a bit and, satisfied with what they see, switch them off and
    >go home.
    >Two weeks later, we get a call from the client about some obscure error
    >message or stack trace in the log, or, far worse, a testimony to the
    >effect that something just "doesn't work". And I'm supposed to tell
    >them: "hey wait, please enable assertions, please put your _production
    >system_ in a state I suspect is _broken_ (and how am I supposed to know
    >that this broken state won't lead to the corruption of some persistent
    >data?) and tell me if you see a big error message"?
    >
    >No. Bloody. Way. The app's environment, by definition, is something that
    >is subject to change and outside of my control. If _my_ app makes
    >assumptions with respects to its environment, and these assumptions
    >don't hold, then I want it to *break reliably*.
    >I'll often explicitly throw AssertionErrors in such cases. Yes, within
    >an if block. I don't give a shit about that added JMP. It's well worth
    >it and anyway I've already got those checks all over the place, whenever
    >I manipulate arrays or in dozens of methods, many of which I'll have
    >written myself.
    >
    >The assert functionality does not provide the kind of reliability I
    >demand for that purpose. It is a very useful mechanism, but only before
    >delivery, during development and integration.
    >
    >df.


    This particular case, pretty much the same thing happens whether the
    assert is enabled on not. You get an exception. The advantage of the
    assert being turned on is the error message is a tad more helpful.

    If an assert fails, presumably something is wrong with a program or
    one of its resources -- not data input files and keyed input. That is
    why it SHOULD be safe to turn it off for production.

    Sometimes I catch myself using assert to detect flawed data. There is
    no way the compiler knows.
    --
    Roedy Green Canadian Mind Products
    http://mindprod.com
    To err is human, but to really foul things up requires a computer.
    ~ Farmer's Almanac
    It is breathtaking how a misplaced comma in a computer program can
    shred megabytes of data in seconds.
    Roedy Green, Feb 2, 2011
    #15
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. paul.foreman

    GUI - GUI value passing

    paul.foreman, Oct 22, 2004, in forum: Java
    Replies:
    5
    Views:
    731
    Michael Rauscher
    Oct 25, 2004
  2. ulysses
    Replies:
    4
    Views:
    748
    Werner Schiendl
    Oct 22, 2003
  3. Andrew Lapidas

    PyGTK GUI update without signals from GUI

    Andrew Lapidas, Apr 13, 2008, in forum: Python
    Replies:
    0
    Views:
    398
    Andrew Lapidas
    Apr 13, 2008
  4. Stefan Ram
    Replies:
    3
    Views:
    476
    Arne Vajhøj
    Nov 20, 2011
  5. Hal Fulton

    GUI and non-GUI data

    Hal Fulton, Aug 5, 2004, in forum: Ruby
    Replies:
    1
    Views:
    127
    Austin Ziegler
    Aug 5, 2004
Loading...

Share This Page