Return-Path: X-Original-To: apmail-cloudstack-dev-archive@www.apache.org Delivered-To: apmail-cloudstack-dev-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id B54B810C42 for ; Wed, 26 Feb 2014 22:03:25 +0000 (UTC) Received: (qmail 79731 invoked by uid 500); 26 Feb 2014 22:03:24 -0000 Delivered-To: apmail-cloudstack-dev-archive@cloudstack.apache.org Received: (qmail 79683 invoked by uid 500); 26 Feb 2014 22:03:24 -0000 Mailing-List: contact dev-help@cloudstack.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cloudstack.apache.org Delivered-To: mailing list dev@cloudstack.apache.org Received: (qmail 79670 invoked by uid 99); 26 Feb 2014 22:03:23 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 26 Feb 2014 22:03:23 +0000 X-ASF-Spam-Status: No, hits=1.5 required=5.0 tests=HTML_MESSAGE,RCVD_IN_DNSWL_LOW,SPF_PASS,WEIRD_PORT X-Spam-Check-By: apache.org Received-SPF: pass (athena.apache.org: domain of antonio.fornie@gmail.com designates 209.85.220.51 as permitted sender) Received: from [209.85.220.51] (HELO mail-pa0-f51.google.com) (209.85.220.51) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 26 Feb 2014 22:03:19 +0000 Received: by mail-pa0-f51.google.com with SMTP id kq14so1408381pab.38 for ; Wed, 26 Feb 2014 14:02:59 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type; bh=YSyIh9P6W3TWNs4bbqzxGZ/GEu9u3FH0F3H+tKmmtTY=; b=0ckfaIS7g2vVc/TFfW+BcV1slUNLGxL2WkuRpCYaYtEGAScTrReEgoTY91uhFu7t84 06cKX8Zqh/DYmt4Tf01O0/GiMYy2sx+DDax/N6qu8MbhQu/wdUfzoqzfvFFP8og4SFMh JwTwfJx/7AvOlduthxf4bw/d2onmhR3mW1ThzSjiO+9yId/U2LlZ0HbWUzgAI9x+wOkY q85N5JKPM/q0cbWReKR5dCWkupCbEAFiEDQXNL5y8sTiEaxtyqCZjxXCJS7QK1RQbZFf r9GwgxLmA0l/QIS7rpH3Iw3r+2/geTzlB1yjM4TgHukW/LjUsVKxLG7DFzWYbVXLgGLZ 11xQ== MIME-Version: 1.0 X-Received: by 10.68.191.200 with SMTP id ha8mr9582298pbc.66.1393452178949; Wed, 26 Feb 2014 14:02:58 -0800 (PST) Received: by 10.66.182.195 with HTTP; Wed, 26 Feb 2014 14:02:58 -0800 (PST) In-Reply-To: References: <20140226091253.1577.55124@reviews.apache.org> <20140226180839.7270.30960@reviews.apache.org> Date: Wed, 26 Feb 2014 23:02:58 +0100 Message-ID: Subject: Re: Review Request 17888: Dispatcher corrections, refactoring and tests. Corrects problems from previous attempt From: =?ISO-8859-1?Q?Antonio_Forni=E9_Casarrubios?= To: Alena Prokharchyk Cc: "dev@cloudstack.apache.org" , daan Hoogland , Hugo Trippaers Content-Type: multipart/alternative; boundary=e89a8ff1c08c234bdc04f3565ef3 X-Virus-Checked: Checked by ClamAV on apache.org --e89a8ff1c08c234bdc04f3565ef3 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Hi Alena, Answer inline: 2014-02-26 22:24 GMT+01:00 Alena Prokharchyk = : > Antonio, please see my 2 comments inline. > > -Alena. > > On 2/26/14, 1:17 PM, "Antonio Forni=E9 Casarrubios" > wrote: > > >Hi Alena, > > > >I answer to your comments inline: > > > > > >2014-02-26 19:08 GMT+01:00 Alena Prokharchyk > >: > > > >> This is an automatically generated e-mail. To reply, visit: > >> https://reviews.apache.org/r/17888/ > >> > >> Antonio, in general looks good to me. There are some minor fixes that > >>need to be done though. > >> > >> 1) ApiServer.java > >> > >> @Inject() > >> 177 > >> protected DispatchChainFactory dispatchChainFactory =3D null; > >> > >> > >> Why do you use @Inject with ()? > >> > >> > >*I tried some parameters with the Inject annotation but finally I decide= d > >to use it as it is here. Just a habit from using other types of DI > >annotations. I just didn't didn't remove the () in this one, but as you > >know it's exactly the same so it didn't even bring my attention. I can > >change that or if you approved this patch, change it in the next patch. > >Again, it's just the same.* > > > > > > > >> 2) ApiServlet.java > >> > >> domainIdArr =3D (String[])params.get(ApiConstants.DOMAIN__ID); > >> > >> * Why do we have DOMAIN__ID and DOMAIN_ID as separate parameters? > >> > >> public static final String DOMAIN_ID =3D "domainid"; > >> public static final String DOMAIN__ID =3D "domainId"; > >> > >> The above doesn't look right to me. Why do we care about the case? the > >>API parameter is always transformed to all lowercase letters > >> > >> *Having these almost identical Strings is not my change, ApiServlet wa= s > >already using both with I and i (domainid and domainId) so I just kept i= t > >as it is (I assume removing one of them may cause a problem when it come= s > >in the req. In order to create the constants for both values I just chos= e > >these names: no names would make much more sense because having this two > >Strings were already something strange. There are several strange things > >like this, I just fixed a few and moved plenty from hardcoded values to > >constants, but no more than that, my main focus was something else and t= he > >patch is already very big.* > > > >3) CommandCreationWorker.java > >> > >> > >> throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, > >> 50 > >> "Trying to invoke creation on a Command that is no= t > >> BaseAsyncCreateCmd"); > >> > >> > >> * Don't hardcode the class name; retrieve it from the .simpleName > >>method called on the class object > >> > >> *Retrieve it? This is a piece of code that was in > >ApiDispatcher#dispatchCreateCmd just after calling processParameters, so > >it > >had to be a next worker in the chain, but I kept the code as it was. And > >this specific method was only invoked after checking the command was > >BaseAsyncCreateCmd (or any class extending, of course), so I created the > >same check in the worker. And then of course I keep also the reason to > >fail > >in the error msg. If it fails the only thing I care is that it is not a > >BaseAsyncCreateCmd as it should... so I don't understand what do you > >propose there. For anybody who chages this code in the future, this erro= r > >will tell them they should not apply this worker when the dispatch chain > >is > >not the expected type.* > > > :) What I=B9ve meant is, instead of "Trying to invoke creation on a Comma= nd > that is not BaseAsyncCreateCmd=B2, log it as "Trying to invoke creation = on > a Command that is not =B3 + BaseAsyncCreateCmd.class.getSimpleName(). So = in > case someone changes the class name in the future, you don=B9t have to di= g > in into hardcoded stuff in order to change it, as it will change > dynamically > > *** I see, sorry I dind't understand you meant that. I will sure change that, good point. > > > > >> 4) DispatchChain > >> > >> for (final DispatchWorker worker : workers) { > >> if (!worker.handle(task)) { > >> break; > >> } > >> } > >> > >> * Why do we just break when workder can't handle the task? Aren't we > >>supposed to do something? > >> * Can you please add more logging? At least log in trace that some > >>worker handled/coudln't handle the task > >> > >> > >> *There are many ways to create a Chain of Responsibility, for this cas= e > >>I > >decided to go for a one way chain (instead of a typical two way flow) in > >which workers share a task where they apply their work and pass their > >changes. They also have the power to stop the chain if the programmer > >decides so in her code. I'm not assuming the worker can't handle anythin= g > >or not not doing something, I am just making sure I provide a way to sto= p > >the chain if one of the workers decides so. If there were something to > >log, > >the worker would know what and if something requires so I guess they wil= l > >raise and exception instead of just returning false. We don't have > >anything > >to log just because the behavior was to stop the chain. Just see it as J= EE > >filters where they can decide to do whatever even there is no error.* > > > > Stopping and doing nothing after all doesn=B9t look right to me, as you > don=B9t pass the result to the caller stack to process it further dependi= ng > on the return type. I would rather change work.handle() return type to > void. As as you said, worker would throw an exception anyway if something > goes wrong. > > *** There is no "Stopping and doing nothing", you create your chains considering the order of workers and you code the workers considering what you want to do in the chain. You should be free to consider the work done. Just like you decide to execute some code or not with an if, and that doesn't mean you are doing nothing: there is nothing to do if the clause for the if is not true: this is the same. ie: Shouldn't a worker be able to decide to save the cmd for later execution asynchronously and when retrieved from a queue go through another chain? I don't know if this is a good example, but actually for something similar we are using ifs and instanceof checks to choose the flow, right? Again the JEE filters are the best example and they are also used to process/dispatch a request/cmd. The reference implementation of Chain of Responsibility is based on workers that wrap the next one recursively (like JEE filters) and thus each worker is free to invoke the next worker or not. With your proposal we loose that. Actually I prefer to go for the that reference implementation, but I decided to go for a list with boolean return types because: 1) We don't need pre and post process, it's a one way chain (at least so far) and also because 2) I want to create the workers as Spring singletons (default scope), but I also want to be able to have several chains with different workers so I didn't want each worker to have a reference to the next. Someone could could brake it by returning false when you shouldn't just like you could by throwing an exception when you shouldn't, but that's not a reason not to give this flexibility to the workers. I think it's better to assume that new workers will be coded appropriately. By the way and in case this info helps (I will include it also in the FS), some more clarification: The old way in the ApiDispatch was this way: hugeMethod(){ // Some code for doing A // Some code for doing B if (cmd instanceof CmdTypeX) { // Some code for doing C } if (N) { } else { // some code for doing D } } And the new approach is: Chain for X A > B > C Chain for N A > B > D So in the end what you could decide to do based on ifs or anything else, you can decide now based on the chain workers and on stopping the chain or not. For the next changes I plan to do more refactoring in this direction. I also did another kind of change so that instead of doing instanceof checks so often (we have them everywhere in CS code) we use polymorphism. For this I created the SemanticValidationWorker so that there the work is actually delegated to the Cmd class (polymorphism) > > > > >> > >> 4) I can see that you do a lot of initialization like that for private > >>variables: > >> > >> protected AccountManager _accountMgr =3D null; > >> > >> its not necessary step as private variables are being initialized to > >>NULL by default during declaration. Can you please clean it out? > >> > >> > >> > >*I know. In Java local variables are not initialized, but instance ones > >are > >(booleans to false, numbers to cero, objects to null...). But I find it > >more clear to put it this way, further people may ignore or forget it, > >specially non Java people helping in cloudstack. It's also something I'v= e > >seen in many Java projects. Again, it's just the same. If you think this > >is > >not clean and should be changed before the patch is accepted, I can chan= ge > >that too.* > > > >*Upon reception of your comments I will change and upload anything.* > > > >- Alena Prokharchyk > >> > >> On February 26th, 2014, 9:12 a.m. UTC, Antonio Fornie wrote: > >> Review request for cloudstack, Alena Prokharchyk, daan Hoogland, and > >> Hugo Trippaers. > >> By Antonio Fornie. > >> > >> *Updated Feb. 26, 2014, 9:12 a.m.* > >> *Repository: * cloudstack-git > >> Description > >> > >> Dispatcher corrections, refactoring and tests. Corrects problems from > >>previous attempts that were reverted by Alena. Most of the changes are > >>the same, but this one is not creating conflicts of Map types for Aync > >>Commands or for parameters as Lists or Maps. > >> > >> Testing > >> > >> Full build and test plus manually testing many features. Also includin= g > >>CreateTagsCommand that failed in previous commit. > >> > >> All unit and integration tests. > >> > >> Test CS Web UI with the browser going through several use cases. > >> > >> Also use the CS API by sending HTTP requests generated manually > >>including requests for Async Commands with Map parameters and during > >>these tests apart fromtesting correct functionality I also debugged to > >>check that Maps created correctly where they should but also that in th= e > >>cases where the async command must be persisted and later on retrieved > >>and deserialized by gson everything works ok and does what and where is > >>expected. An example based on the comment by > >>Alena: > http://localhost:8096/client/api?command=3DcreateTags&resourceids=3Dids > >>&resourcetype=3Dtype&tags[0].key=3Dregion&tags[0].value=3Dcanada > >> Also other examples > >>likehttp://localhost:8096/client/api?command=3DcreateSecondaryStagingSt= ore& > >>url=3Dhttpbla&details[0].key=3Dregion&details[0].value=3Dcanada&details= [1].key=3D > >>element&details[1].value=3Dfire > >> > >> Diffs > >> > >> - api/src/org/apache/cloudstack/api/ApiConstants.java (7b7f9ca) > >> - api/src/org/apache/cloudstack/api/BaseCmd.java (0e83cee) > >> - api/src/org/apache/cloudstack/api/BaseListCmd.java (c1a4b4c) > >> - > >>api/src/org/apache/cloudstack/api/command/admin/user/GetUserCmd.java > >> (592b828) > >> - > >>api/src/org/apache/cloudstack/api/command/admin/user/UpdateUserCmd.java > >> (de6e550) > >> - > >>api/src/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScal= eV > >>mProfileCmd.java > >> (06d86ec) > >> - > >>server/resources/META-INF/cloudstack/core/spring-server-core-misc-conte= xt > >>.xml > >> (fd2f5fb) > >> - server/src/com/cloud/api/ApiAsyncJobDispatcher.java (71ac616) > >> - server/src/com/cloud/api/ApiDispatcher.java (ed95c72) > >> - server/src/com/cloud/api/ApiServer.java (d715db6) > >> - server/src/com/cloud/api/ApiServlet.java (46f7eba) > >> - server/src/com/cloud/api/dispatch/CommandCreationWorker.java > >> (PRE-CREATION) > >> - server/src/com/cloud/api/dispatch/DispatchChain.java (PRE-CREATIO= N) > >> - server/src/com/cloud/api/dispatch/DispatchChainFactory.java > >> (PRE-CREATION) > >> - server/src/com/cloud/api/dispatch/DispatchTask.java (PRE-CREATION= ) > >> - server/src/com/cloud/api/dispatch/DispatchWorker.java > >>(PRE-CREATION) > >> - server/src/com/cloud/api/dispatch/ParamGenericValidationWorker.ja= va > >> (PRE-CREATION) > >> - server/src/com/cloud/api/dispatch/ParamProcessWorker.java > >> (PRE-CREATION) > >> - > >>server/src/com/cloud/api/dispatch/ParamSemanticValidationWorker.java > >> (PRE-CREATION) > >> - server/src/com/cloud/api/dispatch/ParamUnpackWorker.java > >> (PRE-CREATION) > >> - server/src/com/cloud/network/as/AutoScaleManagerImpl.java (ff2b2e= a) > >> - server/src/com/cloud/storage/snapshot/SnapshotSchedulerImpl.java > >> (3159059) > >> - server/test/com/cloud/api/ApiDispatcherTest.java (7314a57) > >> - server/test/com/cloud/api/dispatch/CommandCreationWorkerTest.java > >> (PRE-CREATION) > >> - server/test/com/cloud/api/dispatch/DispatchChainFactoryTest.java > >> (PRE-CREATION) > >> - > >>server/test/com/cloud/api/dispatch/ParamGenericValidationWorkerTest.jav= a > >> (PRE-CREATION) > >> - server/test/com/cloud/api/dispatch/ParamProcessWorkerTest.java > >> (PRE-CREATION) > >> - > >>server/test/com/cloud/api/dispatch/ParamSemanticValidationWorkerTest.ja= va > >> (PRE-CREATION) > >> > >> View Diff > >> > > --e89a8ff1c08c234bdc04f3565ef3--