Oh, and of course the annotation added to the wrapper looks like:

...

@ResourceWrapper(handles =  CheckHealthCommand.class)

public final class LibvirtCheckHealthCommandWrapper

...


maybe 'wraps' or 'wrapperfor' would be better than 'handles' in your naming
scheme. You get the idea.

On Thu, Apr 30, 2015 at 11:59 PM, Marcus <shadow...@gmail.com> wrote:

> I agree, this wrapper is a good step forward. It's totally fine to
> continue on that path because it is obviously better and makes it easy to
> switch to autodetection anytime later by simply adding the annotation.
> Sorry if I got a bit passionate about that, but as you mention I also get
> tired of adding things in multiple places, and the annotations have worked
> well in the API and provide a good model to emulate for consistency.
>
> I can't share code, because these extensions to LibvirtComputingResource
> that I've provided for other companies have not been open sourced. I can
> speak more generically though about methods.
>
> To answer question "a", reflection allows you to do something like:
>
> Reflections reflections = new
> Reflections("com.cloud.hypervisor.kvm.resource.wrapper");
> Set<Class<? extends CommandWrapper>> wrappers =
> reflections.getSubTypesOf(CommandWrapper.class);
>
> So here in "new Reflections" we are automatically filtering for just the
> wrappers that would apply to the KVM plugin.
> Then to finish it off, you iterate through the wrappers and do:
>
> ResourceWrapper annotation = wrapper.getAnnotation(ResourceWrapper.class);
> citrixCommands.put(annotation.handles(), wrapper.newInstance());
>
> Sorry, I guess that's four lines, plus the relevant for loop. And probably
> a null check or something for the annotation. You also have to add the
> annotation class itself, and add a line for the annotation in each wrapper,
> but in the end when we add new Commands, we won't have to touch anything
> but the new class that handles the command.
>
> public @interface ResourceWrapper {
>
>     Class<? extends Command> handles();
>
> }
>
> There's an example of something similar to this in
> KVMStoragePoolManager.java (annotation is StoragePoolInfo.java). This
> example has actually been adapted from that. Also to a lesser extent in the
> API server, but it is spread across a bunch of classes.
>
> On Thu, Apr 30, 2015 at 10:41 PM, Wilder Rodrigues <
> wrodrig...@schubergphilis.com> wrote:
>
>>  Hi Marcus,
>>
>>  Thanks for the email… I’m always in for improvements. But why can’t you
>> share the code?
>>
>>  Few points below:
>>
>>  1. I added an subclassing example of LibvirtComputingResource because
>> you mentioned it in a previous email:
>>
>>   On 23 Apr 2015, at 17:26, Marcus <shadow...@gmail.com> wrote:
>>
>>
>>   I mentioned the reflection model because that's how I tend to handle
>> the commands when subclassing LibvirtComputingResource.
>>
>>
>>  2. Current situation with LibvirtComputingResource on Master is:
>>
>>  a. 67 IFs
>> b. 67 private/protected methods that are used only there
>> c. If a new Command is added it means we will have a new IF and a new
>> private method
>> e. Maintenance is hell, test is close to zero and code quality is below
>> expectations
>>
>>  That being said, the main idea with the refactor is to change structure
>> only, not behaviour. So what I’m doing is to simply move the code out the
>> LibvirtCompRes and write tests for it, keeping the behaviour the same - to
>> be done in a next phase.
>> If you look at the changes you will see that some wrappers are already
>> 100% covered. However, some others have 4% or 8% (not that much though). I
>> would like to refactor that as well, but that could change behaviour
>> (mentioned above) which I don’t want to touch now.
>>
>>  3. With the new situation:
>>
>>  a. No IFs
>> b. All methods wrapped by other classes (command wrappers) - loosely
>> coupled, easier to test and maintain
>>  c. If a new Command is added we would have to add a command wrapper and
>> 1 line in the request wrapper implementation ( I know, it hurts you a bit)
>> - but please bear with me for the good news.
>>
>>  4. the warnings are due to that:
>>     Hashtable<Class<? extends Command>, CommandWrapper>()
>>
>>      No big deal.
>>
>>  As I understood from  your first paragraph we would have to annotated
>> the commands classes, right? I mean, all of them.
>>
>>  That’s something I wouldn’t do in this phase, to be honest. It might
>> seem harmless to do, but I like to break things down a bit and have more
>> isolation in my changes.
>>
>>  What’s next: I will finish the refactor with the request wrapper as it
>> is. For me it is no problem do add the lines now and remove them in 1 week.
>> Most of the work is concentrated in the tests, which I’m trying as hard as
>> I can to get them in the best way possible. Once it’s done and pushed to
>> master, I will analyse what we would need to apply the annotation.
>>
>>  But before I go to bring the kids to school, just one question:
>>
>>  a. The “handle” value, in the annotation, would have the wrapper class
>> that would be used for that command, right?  Now let’s get 1 command as
>> example: CheckHealthCommand. Its wrapper implementation differs per
>> hypervisor (just like all the other wrapper commands do). I’m not taking
>> the time to really think about it now, but how would we annotated the
>> different wrappers per command?
>>
>>  Thanks again for your time.
>>
>>  Cheers,
>> Wilder
>>
>>
>>  On 30 Apr 2015, at 22:52, Marcus <shadow...@gmail.com> wrote:
>>
>> Ok. I wish I could share some code, because it isn't really as big of
>> a deal as it sounds from your reasoning. It is literally just 3 lines
>> on startup that fetch anything with the '@AgentExecutor' annotation
>> and stores it in a hash whose key is the value from @AgentExecutor's
>> 'handles' property. Then when a *Command comes it it is passed to the
>> appropriate Executor class.
>>
>> Looking at CitrixRequestWrapper, the 3 lines I mention are almost
>> identical in function to your init method, just that it uses the
>> annotation to find all of the commands, rather than hardcoding them.
>> We use the same annotation design for the api side of the code on the
>> management server, which allows the api commands to be easier to write
>> and self-contained (you don't have to update other code to add a new
>> api call). It makes things easier for novice developers.
>>
>> This implementation is no less typesafe than the previous design (the
>> one with all of the instanceof). It didn't require any casting or
>> warning suppression, either, as the wrapper does.
>>
>> Extending LibvirtComputingResource is not ideal, and doesn't work if
>> multiple third parties are involved. Granted, there hasn't been a lot
>> of demand for this, nevertheless it's particularly important for KVM,
>> where the Command classes are executed on the hypervisor it's not
>> really feasible to just dump the code in your management server-side
>> plugin like some plugins do.
>>
>> In reviewing the code, the two implementations are really very close.
>> If you just updated init to fetch the wrappers based on either an
>> annotation or the class they extend, or something along those lines so
>> this method doesn't have to be edited every time a command is added,
>> that would be more or less the same thing. The the KVM agent would be
>> pluggable like the management server side is.
>>
>> On Thu, Apr 30, 2015 at 12:55 PM, Wilder Rodrigues
>> <wrodrig...@schubergphilis.com> wrote:
>>
>> Hi Marcus,
>>
>> Apologies for taking so much time to reply to your email, but was, and
>> still
>> am, quite busy. :)
>>
>> I would only use reflection if that was the only way to do it. The use of
>> reflection usually makes the code more complex, which is not good when we
>> have java developers in all different levels (from jr. do sr) working with
>> cloudstack. It also makes us lose the type safety, which might also harm
>> the
>> exception handling if not done well. In addition, if we need to refactor
>> something, the IDE is no longer going to do few things because the
>> refection
>> code cannot be found.
>>
>> If someone will need to extend the LibvirtComputingResource that would be
>> no
>> problem with the approach I’m using. The CitrixResourceBase also has quite
>> few sub-classes and it works just fine.
>>
>> I will document on the wiki page how it should be done when sub-classing
>> the
>> LibvirtComputingResource class.
>>
>> In a quick note/snippet, one would do:
>>
>> public class EkhoComputingResource extends LibvirtComputingResource {
>>
>>       @Override
>>    public Answer executeRequest(final Command cmd) {
>>
>>        final LibvirtRequestWrapper wrapper =
>> LibvirtRequestWrapper.getInstance();
>>        try {
>>            return wrapper.execute(cmd, this);
>>        } catch (final Exception e) {
>>            return Answer.createUnsupportedCommandAnswer(cmd);
>>        }
>>    }
>> }
>>
>>
>> In the flyweight where I keep the wrapper we could have ():
>>
>>        final Hashtable<Class<? extends Command>, CommandWrapper>
>> linbvirtCommands = new Hashtable<Class<? extends Command>,
>> CommandWrapper>();
>>        linbvirtCommands.put(StopCommand.class, new
>> LibvirtStopCommandWrapper());
>>
>>        final Hashtable<Class<? extends Command>, CommandWrapper>
>> ekhoCommands = new Hashtable<Class<? extends Command>, CommandWrapper>();
>>        linbvirtCommands.put(StopCommand.class, new
>> EkhoStopCommandWrapper());
>>
>>        resources.put(LibvirtComputingResource.class, linbvirtCommands);
>>        resources.put(EkhoComputingResource.class, ekhoCommands);
>>
>> But that is needed only if the StopCommand has a different behaviour for
>> the
>> EkhoComputingResource.
>>
>> Once a better version of the documentation is on the wiki, I will let you
>> know.
>>
>> On other matters, I’m also adding unit tests for all the changes. We
>> already
>> went from 4% to 13.6% coverage in the KVM hypervisor plugin. The code I
>> already refactored has 56% of coverage.
>>
>> You can see all the commits here:
>>
>> https://github.com/schubergphilis/cloudstack/tree/refactor/libvirt_resource
>>
>> Cheers,
>> Wilder
>>
>> On 23 Apr 2015, at 17:26, Marcus <shadow...@gmail.com> wrote:
>>
>> Great to see someone working on it. What sorts of roadblocks came out
>> of reflection? How does the wrapper design solve the pluggability
>> issue? This is pretty important to me, since I've worked with several
>> companies now that end up subclassing LibvirtComputingResource in
>> order to handle their own Commands on the hypervisor from their
>> server-side plugins, and changing their 'resource' to that in
>> agent.properties. Since the main agent class needs to be set at agent
>> join, this is harder to manage than it should be.
>>
>> I mentioned the reflection model because that's how I tend to handle
>> the commands when subclassing LibvirtComputingResource. I haven't had
>> any problems with it, but then again I haven't tried to refactor 5500
>> lines into that model, either.
>>
>> On Thu, Apr 23, 2015 at 1:17 AM, Wilder Rodrigues
>> <wrodrig...@schubergphilis.com> wrote:
>>
>> Hi Marcus,
>>
>> I like the annotation idea, but reflection is trick because it hides some
>> information about the code.
>>
>> Please, have a look at the CitrixResourceBase after the refactor I did. It
>> became quite smaller and test coverage was improved.
>>
>> URL:
>>
>> https://cwiki.apache.org/confluence/display/CLOUDSTACK/Refactoring+XenServer+Hypervisor+Plugin
>>
>> The same patter is being about to Libvirt stuff. The coverage on the KVM
>> hypervisor plugin already went from 4 to 10.5% after refactoring 6
>> commands
>>
>> Cheers,
>> Wilder
>>
>> On 22 Apr 2015, at 23:06, Marcus <shadow...@gmail.com> wrote:
>>
>> Kind of a tangent, but I'd actually like to see some work done to
>> clean up LibvirtComputing resource. One model I've prototyped that
>> seems to work is to create an annotation, such as
>> 'KVMCommandExecutor', with a 'handles' property. With this annotation,
>> you implement a class that handles, e.g. StartCommand, etc. Then in
>> LibvirtComputingResource, the 'configure' method fetches all of these
>> executors via reflection and stores them in an object. Then, instead
>> of having all of the 'instanceof' lines in LibvirtComputingResource,
>> the executeRequest method fetches the executor that handles the
>> incoming command and runs it.
>>
>> I think this would break up LibvirtComputingResource into smaller,
>> more testable and manageable chunks, and force things like config and
>> utility methods to move to a more sane location, as well. As a bonus,
>> this model makes things pluggable. Someone could ship KVM plugin code
>> containing standalone command executors that are discovered at runtime
>> for things they need to run at the hypervisor level.
>>
>> On Tue, Apr 21, 2015 at 6:27 AM, Wilder Rodrigues
>> <wrodrig...@schubergphilis.com> wrote:
>>
>> Hi all,
>>
>> Yesterday I started working on the LibvirtComputingResource class in order
>> to apply the same patterns I used in the CitrixResourceBase + add more
>> unit
>> tests to it After 10 hours of work I got a bit stuck with the 1st test,
>> which would cover the refactored LibvirtStopCommandWrapper. Why did I get
>> stuck? The class used a few static methods that call native libraries,
>> which
>> I would like to mock. However, when writing the tests I faced problems
>> with
>> the current Mockito/PowerMock we are using: they are simply not enough for
>> the task.
>>
>> What did I do then? I added a dependency to EasyMock and
>> PowerMock-EasyMock
>> API. It worked almost fine, but I had to add a “-noverify” to both my
>> Eclipse Runtime configuration and also to the
>> cloud-plugin-hypervisor-kvm/pom.xml file. I agree that’s not nice, but was
>> my first attempt of getting it to work. After trying to first full build I
>> faced more problems related to ClassDefNotFoundExpcetion which were
>> complaining about Mockito classes. I then found out that adding the
>> PowerMockRunner to all the tests classes was going to be a heavy burden
>> and
>> would also mess up future changes (e.g. the -noverify flag was removed
>> from
>> Java 8, thus adding it now would be a problem soon).
>>
>> Now that the first 2 paragraphs explain a bit about the problem, let’s get
>> to the solution: Java 8
>>
>> The VerifyError that I was getting was due to the use of the latest
>> EasyMock
>> release (3.3.1). I tried to downgrade it to 3.1/3.2 but it also did not
>> work. My decision: do not refactor if the proper tests cannot be added.
>> This
>> left me with one action: migrate to Java 8.
>>
>> There were mentions about Java 8 in february[1] and now I will put some
>> energy in making it happen.
>>
>> What is your opinion on it?
>>
>> Thanks in advance.
>>
>> Cheers,
>> Wilder
>>
>>
>> http://mail-archives.apache.org/mod_mbox/cloudstack-dev/201502.mbox/%3c54eef6be.5040...@shapeblue.com%3E
>> <http://mail-archives.apache.org/mod_mbox/cloudstack-dev/201502.mbox/<
>> 54eef6be.5040...@shapeblue.com>>
>>
>>
>>
>>
>>
>

Reply via email to