ItemListener.java 9.3 KB
Newer Older
K
kohsuke 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * The MIT License
 * 
 * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
24 25
package hudson.model.listeners;

26
import com.google.common.base.Function;
27
import hudson.ExtensionPoint;
28 29
import hudson.ExtensionList;
import hudson.Extension;
30
import hudson.model.Item;
31 32
import hudson.model.ItemGroup;
import hudson.model.Items;
33
import hudson.security.ACL;
34
import java.util.List;
35 36
import java.util.logging.Level;
import java.util.logging.Logger;
37
import jenkins.security.NotReallyRoleSensitiveCallable;
38 39

/**
K
kohsuke 已提交
40 41
 * Receives notifications about CRUD operations of {@link Item}.
 *
42 43 44 45
 * @since 1.74
 * @author Kohsuke Kawaguchi
 */
public class ItemListener implements ExtensionPoint {
46 47 48

    private static final Logger LOGGER = Logger.getLogger(ItemListener.class.getName());

49
    /**
50
     * Called after a new job is created and added to {@link jenkins.model.Jenkins},
K
kohsuke 已提交
51 52 53 54
     * before the initial configuration page is provided.
     * <p>
     * This is useful for changing the default initial configuration of newly created jobs.
     * For example, you can enable/add builders, etc.
55 56 57 58
     */
    public void onCreated(Item item) {
    }

59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
    /**
     * Called after a new job is created by copying from an existing job.
     *
     * For backward compatibility, the default implementation of this method calls {@link #onCreated(Item)}.
     * If you choose to handle this method, think about whether you want to call super.onCopied or not.
     *
     *
     * @param src
     *      The source item that the new one was copied from. Never null.
     * @param  item
     *      The newly created item. Never null.
     *
     * @since 1.325
     *      Before this version, a copy triggered {@link #onCreated(Item)}.
     */
    public void onCopied(Item src, Item item) {
        onCreated(item);
    }

78
    /**
79
     * Called after all the jobs are loaded from disk into {@link jenkins.model.Jenkins}
M
mindless 已提交
80
     * object.
81 82 83 84 85 86 87 88 89 90 91
     */
    public void onLoaded() {
    }

    /**
     * Called right before a job is going to be deleted.
     *
     * At this point the data files of the job is already gone.
     */
    public void onDeleted(Item item) {
    }
92 93 94

    /**
     * Called after a job is renamed.
95
     * Most implementers should rather use {@link #onLocationChanged}.
96 97 98 99 100 101 102 103 104 105
     * @param item
     *      The job being renamed.
     * @param oldName
     *      The old name of the job.
     * @param newName
     *      The new name of the job. Same as {@link Item#getName()}.
     * @since 1.146
     */
    public void onRenamed(Item item, String oldName, String newName) {
    }
K
kohsuke 已提交
106

107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
    /**
     * Called after an item’s fully-qualified location has changed.
     * This might be because:
     * <ul>
     * <li>This item was renamed.
     * <li>Some ancestor folder was renamed.
     * <li>This item was moved between folders (or from a folder to Jenkins root or vice-versa).
     * <li>Some ancestor folder was moved.
     * </ul>
     * Where applicable, {@link #onRenamed} will already have been called on this item or an ancestor.
     * And where applicable, {@link #onLocationChanged} will already have been called on its ancestors.
     * <p>This method should be used (instead of {@link #onRenamed}) by any code
     * which seeks to keep (absolute) references to items up to date:
     * if a persisted reference matches {@code oldFullName}, replace it with {@code newFullName}.
     * @param item an item whose absolute position is now different
     * @param oldFullName the former {@link Item#getFullName}
     * @param newFullName the current {@link Item#getFullName}
124
     * @see Items#computeRelativeNamesAfterRenaming
125
     * @since 1.548
126 127 128
     */
    public void onLocationChanged(Item item, String oldFullName, String newFullName) {}

129 130 131 132 133 134 135 136
    /**
     * Called after a job has its configuration updated.
     *
     * @since 1.460
     */
    public void onUpdated(Item item) {
    }

K
Kohsuke Kawaguchi 已提交
137 138 139 140 141 142 143 144
    /**
     * @since 1.446
     *      Called at the begenning of the orderly shutdown sequence to
     *      allow plugins to clean up stuff
     */
    public void onBeforeShutdown() {
    }

K
kohsuke 已提交
145 146
    /**
     * Registers this instance to Hudson and start getting notifications.
147 148 149
     *
     * @deprecated as of 1.286
     *      put {@link Extension} on your class to have it auto-registered.
K
kohsuke 已提交
150
     */
151
    @Deprecated
K
kohsuke 已提交
152
    public void register() {
153 154 155 156 157 158 159
        all().add(this);
    }

    /**
     * All the registered {@link ItemListener}s.
     */
    public static ExtensionList<ItemListener> all() {
160
        return ExtensionList.lookup(ItemListener.class);
K
kohsuke 已提交
161
    }
K
kohsuke 已提交
162

163
    // TODO JENKINS-21224 generalize this to a method perhaps in ExtensionList and use consistently from all listeners
164 165 166 167 168 169
    private static void forAll(final /* java.util.function.Consumer<ItemListener> */Function<ItemListener,Void> consumer) {
        for (ItemListener l : all()) {
            try {
                consumer.apply(l);
            } catch (RuntimeException x) {
                LOGGER.log(Level.WARNING, "failed to send event to listener of " + l.getClass(), x);
170
            }
171 172 173 174 175 176 177 178 179
        }
    }

    public static void fireOnCopied(final Item src, final Item result) {
        forAll(new Function<ItemListener,Void>() {
            @Override public Void apply(ItemListener l) {
                l.onCopied(src, result);
                return null;
            }
180
        });
K
kohsuke 已提交
181 182
    }

183
    public static void fireOnCreated(final Item item) {
184 185 186 187
        forAll(new Function<ItemListener,Void>() {
            @Override public Void apply(ItemListener l) {
                l.onCreated(item);
                return null;
188
            }
189
        });
K
kohsuke 已提交
190
    }
191

192
    public static void fireOnUpdated(final Item item) {
193 194 195 196
        forAll(new Function<ItemListener,Void>() {
            @Override public Void apply(ItemListener l) {
                l.onUpdated(item);
                return null;
197
            }
198
        });
199
    }
J
Jesse Glick 已提交
200

201
    /** @since 1.548 */
202
    public static void fireOnDeleted(final Item item) {
203 204 205 206
        forAll(new Function<ItemListener,Void>() {
            @Override public Void apply(ItemListener l) {
                l.onDeleted(item);
                return null;
207
            }
208
        });
J
Jesse Glick 已提交
209 210
    }

211 212 213 214
    /**
     * Calls {@link #onRenamed} and {@link #onLocationChanged} as appropriate.
     * @param rootItem the topmost item whose location has just changed
     * @param oldFullName the previous {@link Item#getFullName}
215
     * @since 1.548
216
     */
217
    public static void fireLocationChange(final Item rootItem, final String oldFullName) {
218 219 220 221
        String prefix = rootItem.getParent().getFullName();
        if (!prefix.isEmpty()) {
            prefix += '/';
        }
222
        final String newFullName = rootItem.getFullName();
223 224 225
        assert newFullName.startsWith(prefix);
        int prefixS = prefix.length();
        if (oldFullName.startsWith(prefix) && oldFullName.indexOf('/', prefixS) == -1) {
226 227
            final String oldName = oldFullName.substring(prefixS);
            final String newName = rootItem.getName();
228
            assert newName.equals(newFullName.substring(prefixS));
229 230 231 232 233
            forAll(new Function<ItemListener, Void>() {
                @Override public Void apply(ItemListener l) {
                    l.onRenamed(rootItem, oldName, newName);
                    return null;
                }
234
            });
235
        }
236 237 238 239 240
        forAll(new Function<ItemListener, Void>() {
            @Override public Void apply(ItemListener l) {
                l.onLocationChanged(rootItem, oldFullName, newFullName);
                return null;
            }
241
        });
242
        if (rootItem instanceof ItemGroup) {
243 244 245 246 247
            for (final Item child : ACL.impersonate(ACL.SYSTEM, new NotReallyRoleSensitiveCallable<List<Item>,RuntimeException>() {
                @Override public List<Item> call() {
                    return Items.getAllItems((ItemGroup) rootItem, Item.class);
                }
            })) {
248
                final String childNew = child.getFullName();
249 250
                assert childNew.startsWith(newFullName);
                assert childNew.charAt(newFullName.length()) == '/';
251 252 253 254 255 256
                final String childOld = oldFullName + childNew.substring(newFullName.length());
                forAll(new Function<ItemListener, Void>() {
                    @Override public Void apply(ItemListener l) {
                        l.onLocationChanged(child, childOld, childNew);
                        return null;
                    }
257
                });
258 259 260 261
            }
        }
    }

262
}