On Fri, 21 Jan 2011 02:42:51 -0800 (PST), Ross<rossclement@gmail.com>
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.
an elegant and sturdy bridge.