10 Replies Latest reply on Sep 9, 2010 4:52 PM by jason.greene

    AS 7 Deployment APIs

    brian.stansberry

      Thread to discuss the client deployment APIs for AS  7.

       

      This thread is closely related to and will probably interweave with ALR's thread at https://community.jboss.org/thread/154922?tstart=0 but that thread has a scope beyond AS 7, so I want a separate one for the purely AS7-focused aspects.

       

      I've posted some thoughts on deployment API in code form in a topic branch on my github repo. See http://github.com/bstansberry/jboss-as/tree/hot-deploy and in particular http://github.com/bstansberry/jboss-as/tree/hot-deploy/domain/src/main/java/org/jboss/as/deployment/plan/

       

      That work is just a draft and a basis for discussion. So thoughts and comments are very much wanted.

       

      The impl subpackages there aren't critical, particularly domain/impl which is for sure wrong. I'm mostly interested in the other packages, which express the API. Note also that much if not all of this should probably move out of the domain module.

       

      There are 2 subpackages, domain and standalone. This is because the API for the full DomainController use case is significantly more complex. There is a fair bit of duplication between the two; perhaps some refactoring could be done once we decide what we want.

       

      The core of this is a client:

       

      1) Gets a reference to DomainDeploymentManager or StandaloneDeploymentManager. How is TBD.

       

      2) Client creates a DeploymentPlan. This describes a coherent unit of work that should be done to add deployment content, deploy content, undeploy content, remove content.

       

      Creation of the DeploymentPlan is done by getting a Builder object from the deployment manager and invoking operations on it.

       

      StandaloneDeploymentManager deploymentManager = magicallyGetDeploymentManager();

      File war = getWarFileFromSomewhere();

      URL ear = getEarURLFromSomewhere();


      DeploymentPlan plan = deploymentManager.newDeploymentPlan()

                                             .withGlobalRollback()

                                             .add(war).andDeploy()

                                             .add(ear).andDeploy();

       

      The main part of the API is in the Builder interfaces that start from the return value from StandaloneDeploymentManager.newDeploymentPlan(). There are five basic operations:

       

      • add -- makes deployment content available to the standalone server or to the domain controller
      • deploy -- instruction that previously added content should be deployed on a standalone server or to one or more server groups
      • undeploy -- instruction that previously deployed content should be undeployed from a standalone server or from one or more server groups
      • replace -- atomic deply+undeploy. So if rollback is enabled and deploying the new content fails, the old content would be deployed
      • remove -- remove no longer deployed content from the repository where the standalone server or domain controller keeps content.

       

      I tried to create a fluent API so following one directive to the builder, logically coherent other directives become available, e.g.

       

      add(war).andDeploy()

       

      3) Client asks deployment manager to execute the plan; gets back a DeploymentPlanResult which can be inspected to see what happened. The DeploymentPlanResult returns a Future for each action that went into the deployment plan, so the client can review the result asynchronously.

       

      DeploymentPlanResult result = deploymentManager.execute(plan);

       

       

      I'll post more later today on some of the nuances, but that's a quick intro. I did a lot of javadoc, so the best way to understand is to poke around.

       

      A few other basic points:

       

      Deployment content once added to the system has a identifying key, in this draft represented by the DeploymentUnitKey class. That's basically an encapsulation of the name of the deployment and it's SHA1 hash. Making clients use the DeploymentUnitKey class feels a bit clunky, so overloaded methods that just use a String form (e.g. "foo.war/a1b2...") are available. Perhaps we should only use the String form in the API?

       

      The API also includes a "repository" notion, e.g. add(String name, String repository, URL content). This isn't core to the API -- it's to support a notion of separate locations where a standalone server could store deployment content, one or more of which might be a hot deployment repository with an associated scanner. We need a separate discussion on whether this is the right way to do hot deployment. If not, the "repository" notion in this API could go away.

        • 1. Re: AS 7 Deployment APIs
          jesper.pedersen

          Just a quick comment - I would rather see "String repository" as "Repository repository", and then resolve the instance of the interface before the call.

           

          I'm assuming that we will have different implementations; FileSystemRepository, MavenRepository, IvyRepository, ...

          • 2. Re: AS 7 Deployment APIs
            brian.stansberry

            Sounds like you're thinking of a slightly different repository concept. A MavenRepository for example sounds like an external place not under our control where the original content is pulled from. Not a location under the control of the server or DC where it stores content.

             

            Which tells me my repository idea is really flawed, since I saw it as either a purely internal location (e.g. server/data/deployments where content provided through the API is copied for server use) or an unholy mix of internal/external (e.g. a deploy/ dir with a scanner thread detecting changes and deploying them.)

            • 3. Re: AS 7 Deployment APIs
              jesper.pedersen

              Matter of words -- basically 'backing' store of the deployments -- likely there will always be a local copy of the deployment which is under the control of the server / DC.

              • 4. Re: AS 7 Deployment APIs
                brian.stansberry

                How would you see an API with a MavenRepository working?

                 

                I'm talking about in terms of the "add" operation, which makes the content available to the server/DC.

                 

                With a MavenRepo, I figure you'd need to pass some sort of GAV as another parameter.

                • 5. Re: AS 7 Deployment APIs
                  brian.stansberry

                  Some discussion on deployment names.

                   

                  Right now we have a "name" and a sha1 hash. But it seems we really need 3 things:

                   

                  1) A common name. This would be the file name (if the content was presented as a file). This becomes the default value for an EE application name (and the EE module name for non-EAR EE archives).

                   

                  2) An internal unique name. This is used internally, and allows the DC for example, to distinguish between two versions of the same archive. We can use the sha1 hash for this, although I think we shouldn't call it "sha1", since that ties us to a particular implementation choice.

                   

                  3) An external (i.e. end-user) unique name. This is the name the end user uses to refer to the deployment in the API. This by default would be the same as 1). However, if the end user wanted two different foo.ear's active within the same domain, they could assign a different external unique name to one or both of them.  The foo.ear common name is still needed for both so they have the expected EE application name.

                  • 6. Re: AS 7 Deployment APIs
                    jesper.pedersen
                    StandaloneDeploymentManager deploymentManager = magicallyGetDeploymentManager();
                    
                    Repository ironJacamarRepo = new MavenRepository();
                    ironJacamarRepo.addServer("https://repository.jboss.org/nexus/content/groups/public");
                    ironJacamarRepo.setGroupId("org.jboss.ironjacamar");
                    
                    // Choose Maven artifact; others include FileArtifact, URLArtifact, IvyArtifact
                    Artifact deployment = new MavenArtifact("jdbc-local", "1.0.0.Beta2", ironJacamarRepo);
                    
                    DeploymentPlan plan = deploymentManager.newDeploymentPlan()
                                                           .withGlobalRollback()
                                                           .add(deployment).andDeploy();

                     

                    Something like that is quite useful.

                     

                    Maybe we could even expose the functionality in the domain.xml file:

                     

                    <deployment name="jdbc-local" uid="xxx" type="maven">
                      <server>
                    https://repository.jboss.org/nexus/content/groups/public</server>
                      <group-id>org.jboss.ironjacamar</group-id>
                      <version>1.0.0.Beta2</version>
                    </deployment>
                    
                    
                    
                    • 7. Re: AS 7 Deployment APIs
                      aloubyansky

                      Looks good!

                      DeploymentPlan plan = deploymentManager.newDeploymentPlan()

                                                             .withGlobalRollback()

                                                             .add(war).andDeploy()

                                                             .add(ear).andDeploy();

                       

                      The main part of the API is in the Builder interfaces that start from the return value from StandaloneDeploymentManager.newDeploymentPlan(). There are five basic operations:

                       

                      • add -- makes deployment content available to the standalone server or to the domain controller
                      • deploy -- instruction that previously added content should be deployed on a standalone server or to one or more server groups
                      • undeploy -- instruction that previously deployed content should be undeployed from a standalone server or from one or more server groups
                      • replace -- atomic deply+undeploy. So if rollback is enabled and deploying the new content fails, the old content would be deployed
                      • remove -- remove no longer deployed content from the repository where the standalone server or domain controller keeps content.

                       

                      I tried to create a fluent API so following one directive to the builder, logically coherent other directives become available, e.g.

                       

                      add(war).andDeploy()

                      Wouldn't having deploy([name,] war) be simpler? Same for replace.

                      • 8. Re: AS 7 Deployment APIs
                        brian.stansberry

                        So, people can understand it, I'll document my thinking a bit on why it was add(deployment).andDeploy(). (Cutting to the chase it can probably change.)

                         

                        1. See my post above on 3 different parts of identifying a deployment -- common name, unique internal name, unique external name. That 3rd part unique external name is new from yesterday and is helpful. Without it the user needed a separate add operation so we could hash the deployment and come up with a unique name that's used in the other API methods. If instead we make the user responsible for providing a unique external name, that may simplify things.
                        2. In the domain case, we have a domain level <deployments/> element that lists all available deployments and then at the server-group level a mapping of deployments to server groups. And, within the server-group or on standalone server a deployment can have a start="false" flag. Those two facts imply a separate add operation that only makes content available but doesn't actually trigger deploy.
                        3. There are a lot of overloaded variants of "add". And I expect there will be more; see Jesper's suggestions on this thread, similar things that will probably come from Shrinkwrap. Adding all those overloaded variants to "deploy" and "replace" leads to an very busy API.

                         

                        But, now I'm less concerned about 3). If the "unique external name" is the id for a deployment, all the methods that take DeploymentUnitKey can go and we're left with the String variants. And if the variants that take "String repository" as a param can go, that again wipes out a lot of overloading. So convenience methods for deploy and replace make sense.

                        • 9. Re: AS 7 Deployment APIs
                          brian.stansberry

                          I want to simplify the rollback handling a bit.

                           

                          Right now there are a number levels at which a user can specify that they want rollback in case of failure:

                           

                          1) At the level of an individual action within a larger set of actions (Example: my plan deploys 3 apps, and if #2 fails I want it rolled back, but don't roll back #1 or #3)

                          2) Within a set of actions that apply to a server. If any action in the set fails, all roll back. For a standalone server, this is specified with the withGlobalRollback() call shown above.

                           

                          And, for the domain controller use case only (irrelevant to standalone server case):

                           

                          3) For a set of servers in a server group, whether failure on one or more trigger rollback across the group.

                          4) For a set of servers groups to which the same set of changes is being applied, whether failure of one server group triggers rollback of the others. (Example: multiple servers running same app, divided into multiple server groups to facilitate a rolling upgrade)

                          5) For a large complex plan where different sets of changes are applied to different server groups, whether failure of one set triggers rollbacks of other sets. (Example: in a multi-tier topology, roll out some deployments to server-groups in tier A, and then roll out different deployments to servers in tier B.)

                           

                          I want to get rid of 1). It complicates the implementation and I don't think it adds a lot of benefit. If people want different behavior for different deployments, they can be executed separately without much trouble.

                          • 10. Re: AS 7 Deployment APIs
                            jason.greene

                             

                            I want to get rid of 1). It complicates the implementation and I don't think it adds a lot of benefit. If people want different behavior for different deployments, they can be executed separately without much trouble.

                            Agreed