zoukankan      html  css  js  c++  java
  • Jenkins插件开发(6.1)—— 分析JenkinsJOB的CRUD源码

    标题上说CRUD,其实不太准确。

    我的这个插件不关注Retrieve操作,所以只研究:Create Job, Update Job, Rename Job, Delete Job

    1. Create(创建JOB)

    Jenkins Job的创建有三种方式:

    • 通过Copy已有JOB创建
    • 通过CLI命令远程创建
    • 通过Jenkins创建Job页面填写相关配置信息来创建。

    这三种方式的实现在hudson.model.ItemGroupMixIn中实现,源码片段如下:

        /**
         * Copies an existing {@link TopLevelItem} to a new name.
         *
         * The caller is responsible for calling {@link ItemListener#fireOnCopied(Item, Item)}. This method
         * cannot do that because it doesn't know how to make the newly added item reachable from the parent.
         */
    // Copy已有JOB创建新JOB(页面Copy和CLI命令Copy都调用这个方法) @SuppressWarnings({"unchecked"}) public synchronized <T extends TopLevelItem> T copy(T src, String name) throws IOException { acl.checkPermission(Job.CREATE); T result = (T)createProject(src.getDescriptor(),name,false); // copy config Util.copyFile(Items.getConfigFile(src).getFile(),Items.getConfigFile(result).getFile()); // reload from the new config result = (T)Items.load(parent,result.getRootDir()); result.onCopiedFrom(src); add(result);

              //这就是我想要看到的:触发ItemListener监听器
              //只要注册一个扩展点(@extension),并继承ItemListener,就可捕获这个动作

            ItemListener.fireOnCopied(src,result);  
            Hudson.getInstance().rebuildDependencyGraph();
    
            return result;
        }
    
    // CLI命令远程创建JOB
    public synchronized TopLevelItem createProjectFromXML(String name, InputStream xml) throws IOException { acl.checkPermission(Job.CREATE); Jenkins.getInstance().getProjectNamingStrategy().checkName(name); // place it as config.xml File configXml = Items.getConfigFile(getRootDirFor(name)).getFile(); configXml.getParentFile().mkdirs(); try { IOUtils.copy(xml,configXml); // load it TopLevelItem result = (TopLevelItem)Items.load(parent,configXml.getParentFile()); add(result);

                 //这就是我想要看到的:触发ItemListener监听器
                 //只要注册一个扩展点(@extension),并继承ItemListener,就可捕获这个动作

                ItemListener.fireOnCreated(result); 
    Jenkins.getInstance().rebuildDependencyGraph();
    return result; } catch (IOException e) { // if anything fails, delete the config file to avoid further confusion Util.deleteRecursive(configXml.getParentFile()); throw e; } }
    // 填写配置信息创建JOB
    public synchronized TopLevelItem createProject( TopLevelItemDescriptor type, String name, boolean notify ) throws IOException { acl.checkPermission(Job.CREATE); Jenkins.getInstance().getProjectNamingStrategy().checkName(name); if(parent.getItem(name)!=null) throw new IllegalArgumentException("Project of the name "+name+" already exists"); TopLevelItem item; try { item = type.newInstance(parent,name); } catch (Exception e) { throw new IllegalArgumentException(e); } try { callOnCreatedFromScratch(item); } catch (AbstractMethodError e) { // ignore this error. Must be older plugin that doesn't have this method } item.save(); add(item);

              //这就是我想要看到的:触发ItemListener监听器
              //只要注册一个扩展点(@extension),并继承ItemListener,就可捕获这个动作

            if (notify)
                ItemListener.fireOnCreated(item); 
    return item; }

    2. Update(更新JOB)

    Jenkins Job的Update有两种方式:

    • 通过配置页面修改
    • 通过CLI命令远程更新

    这三种方式的实现在hudson.model.AbstractItem中实现,源码片段如下:

    /**
         * Updates Job by its XML definition.
         */
        public void updateByXml(Source source) throws IOException {
            checkPermission(CONFIGURE);
            XmlFile configXmlFile = getConfigFile();
            AtomicFileWriter out = new AtomicFileWriter(configXmlFile.getFile());
            try {
                try {
                    // this allows us to use UTF-8 for storing data,
                    // plus it checks any well-formedness issue in the submitted
                    // data
                    Transformer t = TransformerFactory.newInstance()
                            .newTransformer();
                    t.transform(source,
                            new StreamResult(out));
                    out.close();
                } catch (TransformerException e) {
                    throw new IOException2("Failed to persist configuration.xml", e);
                }
    
                // try to reflect the changes by reloading
                new XmlFile(Items.XSTREAM, out.getTemporaryFile()).unmarshal(this);
                onLoad(getParent(), getRootDir().getName());
                Jenkins.getInstance().rebuildDependencyGraph();
    
                // if everything went well, commit this new version
                out.commit();
                //这就是我想要看到的:触发SaveableListener监听器
                //只要注册一个扩展点(@extension),并继承SaveableListener,就可捕获这个动作 SaveableListener.fireOnChange(
    this, getConfigFile());
    }
    finally { out.abort(); // don't leave anything behind } }

    3. Rename(重名JOB)

    Jenkins Job的Rename只有一种方式:

    • 通过配置页面修改名称->提交->同意改名

    也是在hudson.model.AbstractItem中实现,源码片段如下:

    /**
         * Renames this item.
         * Not all the Items need to support this operation, but if you decide to do so,
         * you can use this method.
         */
        protected void renameTo(String newName) throws IOException {
            // always synchronize from bigger objects first
            final ItemGroup parent = getParent();
            synchronized (parent) {
                synchronized (this) {
                    // sanity check
                    if (newName == null)
                        throw new IllegalArgumentException("New name is not given");
    
                    // noop?
                    if (this.name.equals(newName))
                        return;
    
                    Item existing = parent.getItem(newName);
                    if (existing != null && existing!=this)
                        // the look up is case insensitive, so we need "existing!=this"
                        // to allow people to rename "Foo" to "foo", for example.
                        // see http://www.nabble.com/error-on-renaming-project-tt18061629.html
                        throw new IllegalArgumentException("Job " + newName
                                + " already exists");
    
                    String oldName = this.name;
                    File oldRoot = this.getRootDir();
    
                    doSetName(newName);
                    File newRoot = this.getRootDir();
    
                    boolean success = false;
    
                    try {// rename data files
                        boolean interrupted = false;
                        boolean renamed = false;
    
                        // try to rename the job directory.
                        // this may fail on Windows due to some other processes
                        // accessing a file.
                        // so retry few times before we fall back to copy.
                        for (int retry = 0; retry < 5; retry++) {
                            if (oldRoot.renameTo(newRoot)) {
                                renamed = true;
                                break; // succeeded
                            }
                            try {
                                Thread.sleep(500);
                            } catch (InterruptedException e) {
                                // process the interruption later
                                interrupted = true;
                            }
                        }
    
                        if (interrupted)
                            Thread.currentThread().interrupt();
    
                        if (!renamed) {
                            // failed to rename. it must be that some lengthy
                            // process is going on
                            // to prevent a rename operation. So do a copy. Ideally
                            // we'd like to
                            // later delete the old copy, but we can't reliably do
                            // so, as before the VM
                            // shuts down there might be a new job created under the
                            // old name.
                            Copy cp = new Copy();
                            cp.setProject(new org.apache.tools.ant.Project());
                            cp.setTodir(newRoot);
                            FileSet src = new FileSet();
                            src.setDir(getRootDir());
                            cp.addFileset(src);
                            cp.setOverwrite(true);
                            cp.setPreserveLastModified(true);
                            cp.setFailOnError(false); // keep going even if
                                                        // there's an error
                            cp.execute();
    
                            // try to delete as much as possible
                            try {
                                Util.deleteRecursive(oldRoot);
                            } catch (IOException e) {
                                // but ignore the error, since we expect that
                                e.printStackTrace();
                            }
                        }
    
                        success = true;
                    } finally {
                        // if failed, back out the rename.
                        if (!success)
                            doSetName(oldName);
                    }
    
                    callOnRenamed(newName, parent, oldName);
    
    //这里也会触发监听器
    //只要注册一个扩展点(@extension),并继承ItemListener,就可捕获这个动作
    for (ItemListener l : ItemListener.all()) l.onRenamed(this, oldName, newName); } } }

    4. Delete(删除JOB)

    Jenkins Job的Delete有两种方式:

    • 通过页面直接删除
    • 通过CLI命令远程删除

    这两种方式的实现在hudson.model.AbstractItem中实现,源码片段如下:

        
       /**
         * Deletes this item.
         */
        @CLIMethod(name="delete-job")
        @RequirePOST
        public void doDoDelete( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException, InterruptedException {
            delete();
            if (rsp != null) // null for CLI
                rsp.sendRedirect2(req.getContextPath()+"/"+getParent().getUrl());
        }

    public void delete( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException { try { doDoDelete(req,rsp); } catch (InterruptedException e) { // TODO: allow this in Stapler throw new ServletException(e); } } /** * Deletes this item. * * <p> * Any exception indicates the deletion has failed, but {@link AbortException} would prevent the caller * from showing the stack trace. This */ public synchronized void delete() throws IOException, InterruptedException { checkPermission(DELETE); performDelete(); try { invokeOnDeleted(); } catch (AbstractMethodError e) { // ignore } Jenkins.getInstance().rebuildDependencyGraph(); } /** * A pointless function to work around what appears to be a HotSpot problem. See JENKINS-5756 and bug 6933067 * on BugParade for more details. */ private void invokeOnDeleted() throws IOException { getParent().onDeleted(this); } /** * Does the real job of deleting the item. */ protected void performDelete() throws IOException, InterruptedException { getConfigFile().delete(); Util.deleteRecursive(getRootDir()); }

    getParent().onDeleteed(this)方法在jenkins.model.Jenkins定义,如下:

        /**
         * Called in response to {@link Job#doDoDelete(StaplerRequest, StaplerResponse)}
         */
        public void onDeleted(TopLevelItem item) throws IOException {
    //同样,这里也触发了监听器。只要注册一个扩展点(@extension),并继承ItemListener,就可捕获这个动作。
    for (ItemListener l : ItemListener.all()) l.onDeleted(item); items.remove(item.getName()); for (View v : views) v.onJobRenamed(item, item.getName(), null); save(); }

     另外,这里是使用注解的方式注册CLI的,除了@CLIMethod外,还有@CLIResolver配套使用:

        /**
         * Used for CLI binding.
         */
        @CLIResolver
        public static AbstractItem resolveForCLI(
                @Argument(required=true,metaVar="NAME",usage="Job name") String name) throws CmdLineException {
            AbstractItem item = Jenkins.getInstance().getItemByFullName(name, AbstractItem.class);
            if (item==null)
                throw new CmdLineException(null,Messages.AbstractItem_NoSuchJobExists(name,AbstractProject.findNearest(name).getFullName()));
            return item;
        }

    关于使用注解的方式注册CLI命令,请参照:https://wiki.jenkins-ci.org/display/JENKINS/Writing+CLI+commands

  • 相关阅读:
    ESB数据发布思路
    Mongo客户端MongoVUE的基本使用
    ESB数据采集思路
    css3学习笔记之按钮
    发布Qt程序时别忘了带上plugins(codecs等)
    [Qt]Cannot retrieve debugging output
    Linux下编译opencv库[转]
    OpenCV库Windows下QT编译及使用
    ipad4自动下载了ios8的安装包,好几个G啊,不想更新,怎么删了呢?
    VC: error LNK2019:unresolved external symbol *** referenced in function ***的解决方案
  • 原文地址:https://www.cnblogs.com/zhangqingsh/p/3030256.html
Copyright © 2011-2022 走看看