Hm... Doesn't it become nearly impossible to protect against malicious intent, the moment we make it extensible?
On Sunday, January 26, 2014, Ralph Goers <ralph.go...@dslextreme.com> wrote: > A malicious app could do > > for (int i=0; i < 100000; ++i) { > new Level(“Level” + i, 1000 + i){}; > } > > Sure idiots can do lots of bad things but I don’t think Levels should be > quite that flexible. > > Ralph > > On Jan 25, 2014, at 9:39 PM, Remko Popma <remko.po...@gmail.com> wrote: > > I don't think client code can do new Level(){} as the constructor requires > String and int arguments. > > By the way, I am unclear on what went wrong with the enum approach you > originally took. > You said: > StdLevel isn’t a Level because it can’t extend it if it is an enum, > so I can’t initialize the levels using that. > > I don't understand. StdLevel implements the Level interface, right? So > what do you mean by "it can't extend it"? > > The reason I ask is that for the code generation, "real" java enums may be > easier to deal with than the extensible enum approach. > > > On Sun, Jan 26, 2014 at 2:30 PM, Ralph Goers > <ralph.go...@dslextreme.com>wrote: > > Out of curiosity, what exactly is the benefit of declaring the class > abstract when it has a protected constructor? It seems like all you are > accomplishing is making the instantiation syntax uglier. It also bothers me > that open code can just do a new Level(){} - which will do nothing but > cause problems. I’m beginning to think that we should require an > annotation on the Level declaration of the extension class to avoid that. > > Ralph > > On Jan 25, 2014, at 9:13 PM, Remko Popma <remko.po...@gmail.com> wrote: > > Ralph, > I copied Nick's code _as is_ and had no compile errors. > The class is abstract, but instances are defined in the static block as: > OFF = new Level("OFF", 0) {}; // note the {}: this creates an anonymous > concrete subclass > > I agree that read access needs to be synchronized as well, not just write > access (the constructor). > I experimented with several options: > * synchronizing on plain Object in both constructor and when accessing the > static Map(s) > * a ReentrantReadWriteLock > * a lock-free implementation > > I decided against ReentrantReadWriteLock as it has more overhead than > plain synchronized access and the write access (in the constructors) is > going to be extremely rare: not worth paying the overhead in the more > common reads. It is also cumbersome to code. > > The lock-free implementation uses an AtomicInteger for the ordinals, and > an AtomicReference for the Map<String, Level>. > In the constructor, create a new Map<String, Level> instance based on the > old copy, add the new instance, and try to call compareAndSet to replace > the old instance with the new instance. Retry on failure. > > Finally, simply synchronizing on the constructorLock object in the > Level.toLevel() and Level.values() methods may be simplest. > > Which of the last two is best depends on how often the toLevel() and > values() levels are called. > It turns out they are only called during reconfiguration, so no real need > to optimize these methods. > I would argue that simple synchronization may be best in this case. > > Remko > > > > On Sun, Jan 26, 2014 at 1:49 PM, 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 > >