Plugins with GUI

R

Ross

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?
 
G

Graham Cox

Ross 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

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.
 
M

markspace

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/
 
S

Steven Simpson

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...)
 
I

Ian Shef

@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 ?
 
R

Roedy Green

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.
 
L

Lew

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.
 
D

Daniele Futtorovic

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?
 
L

Lew

Daniele said:
Wait, wat?

This?

Yes. It *is* the sole and only use of 'assert' in the quoted post, so it
could hardly be anything else, could it?
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.
 
D

Daniele Futtorovic

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?
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.
 
L

Lew

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


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.
 
D

Daniele Futtorovic

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.


Nor need to.


The ops personnel do that, duh.


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.

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.


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.

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.


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'.


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.


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.
 
R

Roedy Green

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



I accept that definition. Point granted.



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



if + throw proves it too.



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.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top