Ralph, if you're getting compile errors with that code, A) there's a copy-paste/transposition error, or B) there's something wrong with your (non-standard?) compiler. Given:
abstract class A { ... } This is perfectly legal in Java 5+: A a = new A() { }; That's an anonymous inner class extending A. I think that should clear up several of your problems. If your compiler won't compile that, something is very wrong. Removing the ordinal would make it less enum-y. The synchronization could, however, be improved. A reentrant read-write lock seems like a better approach to me. Nick Sent from my iPhone, so please forgive brief replies and frequent typos > On Jan 25, 2014, at 22:49, Ralph Goers <ralph.go...@dslextreme.com> wrote: > > As I am working on this I just want to point out a number of issues with the > code below: > > 1. The class is abstract. The static block is doing a bunch of new Level() > invocations which obviously generate compile errors on an abstract class. I > had to make it be a non-abstract class. > 2. As I pointed out before there is no way to access the “standard” levels as > an enum. I have addressed that. > 3. Although the constructor is synchronized access to the Map is not. Trying > to get from the map while a Level is being added will result in a > ConcurrentModificationException. I am using a ConcurrentMap instead. > 3. The constructor requires synchronization because it is modifying both the > map and the ordinal. However, since this isn’t an enum the ordinal value is > of dubious value. Removing that would allow the removal of the > synchronization in the constructor. I am considering that but I haven’t done > it yet. > 4. Your example of creating the extension shows doing a new Level(). This > doesn’t work because a) the class is abstract and b) the constructor is > protected. I am leaving the constructor protected so extension will require > doing new ExtendedLevel(name, value) and creating a constructor. Not > requiring that means applications can do a new Level() anywhere and I am > opposed to allowing that. > > Ralph > >> On Jan 23, 2014, at 12:42 AM, Nick Williams <nicho...@nicholaswilliams.net> >> wrote: >> >> Okay, I finally got a minute to read all of these emails, and... >> >> EVERYBODY FREEZE! >> >> What if I could get you an extensible enum that required no interface >> changes and no binary-incompatible changes at all? Sound too good to be >> true? I proposed this months ago (LOG4J2-41) and it got shot down multiple >> times, but as of now I've heard THREE people say "extensible enum" in this >> thread, so here it is, an extensible enum: >> >> public abstract class Level implements Comparable<Level>, Serializable { >> public static final Level OFF; >> public static final Level FATAL; >> public static final Level ERROR; >> public static final Level WARN; >> public static final Level INFO; >> public static final Level DEBUG; >> public static final Level TRACE; >> public static final Level ALL; >> >> >> private static final long serialVersionUID = 0L; >> private static final Hashtable<String, Level> map; >> private static final TreeMap<Integer, Level> values; >> private static final Object constructorLock; >> >> >> static { >> // static variables must be constructed in certain order >> constructorLock = new Object(); >> map = new Hashtable<String, Level>(); >> values = new TreeMap<Integer, Level>(); >> OFF = new Level("OFF", 0) {}; >> FATAL = new Level("FATAL", 100) {}; >> ERROR = new Level("ERROR", 200) {}; >> WARN = new Level("WARN", 300) {}; >> INFO = new Level("INFO", 400) {}; >> DEBUG = new Level("DEBUG", 500) {}; >> TRACE = new Level("TRACE", 600) {}; >> ALL = new Level("ALL", Integer.MAX_VALUE) {}; >> } >> >> >> private static int ordinals; >> >> >> private final String name; >> private final int intLevel; >> private final int ordinal; >> >> >> protected Level(String name, int intLevel) { >> if(name == null || name.length() == 0) >> throw new IllegalArgumentException("Illegal null Level constant"); >> if(intLevel < 0) >> throw new IllegalArgumentException("Illegal Level int less than >> zero."); >> synchronized (Level.constructorLock) { >> if(Level.map.containsKey(name.toUpperCase())) >> throw new IllegalArgumentException("Duplicate Level constant >> [" + name + "]."); >> if(Level.values.containsKey(intLevel)) >> throw new IllegalArgumentException("Duplicate Level int [" + >> intLevel + "]."); >> this.name = name; >> this.intLevel = intLevel; >> this.ordinal = Level.ordinals++; >> Level.map.put(name.toUpperCase(), this); >> Level.values.put(intLevel, this); >> } >> } >> >> >> public int intLevel() { >> return this.intLevel; >> } >> >> >> public boolean isAtLeastAsSpecificAs(final Level level) { >> return this.intLevel <= level.intLevel; >> } >> >> >> public boolean isAtLeastAsSpecificAs(final int level) { >> return this.intLevel <= level; >> } >> >> >> public boolean lessOrEqual(final Level level) { >> return this.intLevel <= level.intLevel; >> } >> >> >> public boolean lessOrEqual(final int level) { >> return this.intLevel <= level; >> } >> >> >> @Override >> @SuppressWarnings("CloneDoesntCallSuperClone") >> public Level clone() throws CloneNotSupportedException { >> throw new CloneNotSupportedException(); >> } >> >> >> @Override >> public int compareTo(Level other) { >> return intLevel < other.intLevel ? -1 : (intLevel > other.intLevel ? 1 >> : 0); >> } >> >> >> @Override >> public boolean equals(Object other) { >> return other instanceof Level && other == this; >> } >> >> >> public Class<Level> getDeclaringClass() { >> return Level.class; >> } >> >> >> @Override >> public int hashCode() { >> return this.name.hashCode(); >> } >> >> >> public String name() { >> return this.name; >> } >> >> >> public int ordinal() { >> return this.ordinal; >> } >> >> >> @Override >> public String toString() { >> return this.name; >> } >> >> >> public static Level toLevel(String name) { >> return Level.toLevel(name, Level.DEBUG); >> } >> >> >> public static Level toLevel(String name, Level defaultLevel) { >> if(name == null) >> return defaultLevel; >> name = name.toUpperCase(); >> if(Level.map.containsKey(name)) >> return Level.map.get(name); >> return defaultLevel; >> } >> >> >> public static Level[] values() { >> return Level.values.values().toArray(new Level[Level.values.size()]); >> } >> >> >> public static Level valueOf(String name) { >> if(name == null) >> throw new IllegalArgumentException("Unknown level constant [" + >> name + "]."); >> name = name.toUpperCase(); >> if(Level.map.containsKey(name)) >> return Level.map.get(name); >> throw new IllegalArgumentException("Unknown level constant [" + name + >> "]."); >> } >> >> >> public static <T extends Enum<T>> T valueOf(Class<T> enumType, String >> name) { >> return Enum.valueOf(enumType, name); >> } >> >> >> // for deserialization >> protected final Object readResolve() throws ObjectStreamException { >> return Level.valueOf(this.name); >> } >> } >> >> Extending it is easy: >> >> public final class ExtendedLevels { >> public static final Level MY_LEVEL = new Level("MY_LEVEL", 250) {}; >> } >> >> I still and have ALWAYS believed this was the best option. If we used this >> option, I would be fine with not adding any new Levels because I could add >> them myself. >> >> Nick >> >>> On Jan 22, 2014, at 7:04 PM, Remko Popma wrote: >>> >>> This is only a problem for webapps, right? >>> Putting log4j jars in WEB-INF/lib avoids that problem (different class >>> loader). >>> Apps that really want to share log4j jars with other apps would need to >>> play nice. Such apps would do well to use a naming convention like Gary >>> suggests. >>> Otherwise, the last to register would overwrite any previous level with the >>> same name. (Should probably emit a StatusLogger warning.) >>> >>> Same intLevel for different names should not be a problem. >>> >>> >>> On Thursday, January 23, 2014, Gary Gregory <garydgreg...@gmail.com> wrote: >>> Playing devils advocate: >>> >>> What happens when different apps register levels with the same name and >>> different intLevels? >>> What happens when different apps register levels with the same intLevel and >>> different names? >>> Should there be a convention that custom level names be FQNs? >>> >>> Gary >>> >>> >>> On Wed, Jan 22, 2014 at 10:05 PM, Paul Benedict <pbened...@apache.org> >>> wrote: >>> As Gary wanted, a new thread.... >>> >>> First, each enum needs an inherit strength. This would be part of the >>> interface. Forgive me if the word "strength" is wrong; but it's the 100, >>> 200, 300, etc. number that triggers the log level. So make sure the >>> interface contains the intLevel() method. >>> >>> Second, we need to know the name, right? The name probably requires a new >>> method since it can't be extracted from the enum anymore. >>> >>> public interface Level { >>> int intLevel(); >>> String name(); >>> } >>> >>> PS: The intStrength() name seems hackish. What about strength() or >>> treshold()? >>> >>> Third, the registration can be done manually by providing a static method >>> (as your did Remko) that the client needs to invoke, or you could have a >>> class-path scanning mechanism. For the latter, you could introduce a new >>> annotation to be placed on the enum class. >>> >>> @CustomLevels >>> public enum MyCustomEnums { >>> } >>> >>> Paul >>> >>> On Wed, Jan 22, 2014 at 8:52 PM, Remko Popma <remko.po...@gmail.com> wrote: >>> Paul, can you give a bit more detail? >>> >>> I tried this: copy the current Level enum to a new enum called "Levels" in >>> the same package (other name would be fine too). Then change Level to an >>> interface (removing the constants and static methods, keeping only the >>> non-static methods). Finally make the Levels enum implement the Level >>> interface. >>> >>> After this, we need to do a find+replace for the references to >>> Level.CONSTANT to Levels.CONSTANT and Level.staticMethod() to >>> Levels.staticMethod(). >>> >>> Finally, the interesting part: how do users add or register their custom >>> levels and how do we enable the Levels.staticLookupMethod(String, Level) to >>> recognize these custom levels? >>> >>> >>> >>> On Thursday, January 23, 2014, Paul Benedict <pbened...@apache.org> wrote: >>> Agreed. This is not an engineering per se, but really more about if the >>> feature set makes sense. >>> >>> Well if you guys ever look into the interface idea, you'll give log4j the >>> feature of getting enums to represent custom levels. That's pretty cool, >>> IMO. I don't know if any other logging framework has that and that would >>> probably get some positive attention. It shouldn't be so hard to do a >>> find+replace on the code that accepts Level and replace it with another >>> name. Yes, there will be some minor refactoring that goes with it, but >>> hard? It shouldn't be. >>> >>> A name I propose for the interface is LevelDefinition. >>> >>> Paul >>> >>> >>> On Wed, Jan 22, 2014 at 6:48 PM, Gary Gregory <garydgreg...@gmail.com> >>> wrote: >>> Hi, I do not see this as an engineering problem but more a feature set >>> definition issue. So while there may be lots of more or less internally >>> complicated ways of solving this with interfaces, makers and whatnots, the >>> built in levels are the most user friendly. >>> >>> I have have lots of buttons, knobs and settings on my sound system that I >>> do not use, just like I do not use all the methods in all the classes in >>> the JRE... >>> >>> Gary >>> >>> >>> >>> >>> -- >>> E-Mail: garydgreg...@gmail.com | ggreg...@apache.org >>> Java Persistence with Hibernate, Second Edition >>> JUnit in Action, Second Edition >>> Spring Batch in Action >>> Blog: http://garygregory.wordpress.com >>> Home: http://garygregory.com/ >>> Tweet! http://twitter.com/GaryGregory >> >> >> --------------------------------------------------------------------- >> To unsubscribe, e-mail: log4j-dev-unsubscr...@logging.apache.org >> For additional commands, e-mail: log4j-dev-h...@logging.apache.org > > > --------------------------------------------------------------------- > To unsubscribe, e-mail: log4j-dev-unsubscr...@logging.apache.org > For additional commands, e-mail: log4j-dev-h...@logging.apache.org > --------------------------------------------------------------------- To unsubscribe, e-mail: log4j-dev-unsubscr...@logging.apache.org For additional commands, e-mail: log4j-dev-h...@logging.apache.org