Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@
import com.cloudbees.hudson.plugins.folder.views.DefaultFolderViewHolder;
import hudson.BulkChange;
import hudson.Extension;
import hudson.ExtensionList;
import hudson.Util;
import static hudson.Util.fixEmpty;
import hudson.XmlFile;
import hudson.init.InitMilestone;
import hudson.init.Initializer;
import hudson.model.AbstractItem;
Expand Down Expand Up @@ -73,13 +73,11 @@
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -337,52 +335,10 @@
* @param <V> the child type.
* @return a map of the children keyed by the generated keys.
*/
// TODO replace with ItemGroupMixIn.loadChildren once baseline core has JENKINS-41222 merged

Check warning on line 338 in src/main/java/com/cloudbees/hudson/plugins/folder/AbstractFolder.java

View check run for this annotation

ci.jenkins.io / Open Tasks Scanner

TODO

NORMAL: replace with ItemGroupMixIn.loadChildren once baseline core has JENKINS-41222 merged
public static <K, V extends TopLevelItem> Map<K, V> loadChildren(AbstractFolder<V> parent, File modulesDir,
Function<? super V, ? extends K> key) {
CopyOnWriteMap.Tree<K, V> configurations = new CopyOnWriteMap.Tree<>();
if (!modulesDir.isDirectory() && !modulesDir.mkdirs()) { // make sure it exists
LOGGER.log(Level.SEVERE, "Could not create {0} for folder {1}",
new Object[]{modulesDir, parent.getFullName()});
return configurations;
}

File[] subdirs = modulesDir.listFiles(File::isDirectory);
if (subdirs == null) {
return configurations;
}
final ChildNameGenerator<AbstractFolder<V>,V> childNameGenerator = parent.childNameGenerator();
Map<String,V> byDirName = new HashMap<>();
if (parent.items != null) {
for (V item : parent.items.values()) {
byDirName.put(childNameGenerator.dirName(parent, item), item);
}
}
for (File subdir : subdirs) {
// Try to retain the identity of an existing child object if we can.
V item = byDirName.get(subdir.getName());
try {
if (item == null) {
XmlFile xmlFile = Items.getConfigFile(subdir);
if (xmlFile.exists()) {
item = (V) xmlFile.read();
} else {
throw new FileNotFoundException("Could not find configuration file " + xmlFile.getFile());
}
}
String name = childNameGenerator.itemNameFromItem(parent, item);
if (name == null) {
name = subdir.getName();
}
item.onLoad(parent, name);
configurations.put(key.apply(item), item);
} catch (Exception e) {
LOGGER.warning(() -> "could not load " + subdir + " due to " + e);
LOGGER.log(Level.FINE, null, e);
}
}

return configurations;
return ExtensionList.lookupFirst(ChildLoader.class).loadChildren(parent, modulesDir, key);
}

@Override
Expand Down Expand Up @@ -478,7 +434,7 @@
}
}

private ChildNameGenerator<AbstractFolder<I>,I> childNameGenerator() {
ChildNameGenerator<AbstractFolder<I>,I> childNameGenerator() {
return getDescriptor().childNameGenerator();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package com.cloudbees.hudson.plugins.folder;

import edu.umd.cs.findbugs.annotations.CheckForNull;
import hudson.ExtensionPoint;
import hudson.XmlFile;
import hudson.model.Item;
import hudson.model.Items;
import hudson.model.TopLevelItem;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;

public abstract class ChildLoader implements ExtensionPoint {

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

/**
* Loads all the child {@link Item}s.
*
* @param parent the parent of the children.
* @param modulesDir Directory that contains sub-directories for each child item.
* @param key the key generating function.
* @param <K> the key type
* @param <V> the child type.
* @return a map of the children keyed by the generated keys.
*/
protected abstract <K, V extends TopLevelItem> Map<K, V> loadChildren(
AbstractFolder<V> parent, File modulesDir, Function<? super V, ? extends K> key);

/**
* Ensure that the specified directory exists. If the directory does not exist, attempt to create it.
*
* @param modulesDir the directory that should exist or needs to be created
* @param parent parent folder associated with the directory
* @return {@code true} if the directory exists or was successfully created; {@code false} otherwise
*/
protected <V extends TopLevelItem> boolean ensureDirExists(File modulesDir, AbstractFolder<V> parent) {
if (!modulesDir.isDirectory() && !modulesDir.mkdirs()) { // make sure it exists

Check warning on line 42 in src/main/java/com/cloudbees/hudson/plugins/folder/ChildLoader.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 42 is only partially covered, one branch is missing
LOGGER.log(Level.SEVERE, "Could not create {0} for folder {1}",
new Object[]{modulesDir, parent.getFullName()});
return false;

Check warning on line 45 in src/main/java/com/cloudbees/hudson/plugins/folder/ChildLoader.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 43-45 are not covered by tests
}
return true;
}

/**
* Retrieve a map of items contained within a specified parent folder, keyed by their directory names.
*
* @param parent the parent folder containing the items
* @return a map where the keys are the directory names of the items and the values are the items themselves
*/
protected <V extends TopLevelItem> Map<String, V> getItemsByDirName(AbstractFolder<V> parent) {
Map<String, V> byDirName = new HashMap<>();
if (parent.items != null) {
final ChildNameGenerator<AbstractFolder<V>, V> childNameGenerator = parent.childNameGenerator();
for (V item : parent.items.values()) {
byDirName.put(childNameGenerator.dirName(parent, item), item);
}
}
return byDirName;
}

/**
* Load a {@link TopLevelItem} from a given directory. The method attempts to read the item configuration, assign an
* appropriate name to the item, and initialize it within the provided parent folder.
*
* @param parent the parent folder that will contain the loaded item
* @param subdir the directory from which the item configuration can potentially be loaded
* @param item an optional pre-existing item that can be reused; if null, the item will be loaded from the directory
* @return the loaded item if successful, or {@code null} if an error occurs during loading
*/
@CheckForNull
public <V extends TopLevelItem> V loadItem(AbstractFolder<V> parent, File subdir, @CheckForNull V item) {
try {
if (item == null) {
XmlFile xmlFile = Items.getConfigFile(subdir);
if (xmlFile.exists()) {

Check warning on line 81 in src/main/java/com/cloudbees/hudson/plugins/folder/ChildLoader.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 81 is only partially covered, one branch is missing
item = (V) xmlFile.read();
} else {
throw new FileNotFoundException("Could not find configuration file " + xmlFile.getFile());

Check warning on line 84 in src/main/java/com/cloudbees/hudson/plugins/folder/ChildLoader.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 84 is not covered by tests
}
}
String name = parent.childNameGenerator().itemNameFromItem(parent, item);
if (name == null) {
name = subdir.getName();
}
item.onLoad(parent, name);
return item;
} catch (Exception e) {
LOGGER.warning(() -> "could not load " + subdir + " due to " + e);
LOGGER.log(Level.FINE, null, e);
return null;

Check warning on line 96 in src/main/java/com/cloudbees/hudson/plugins/folder/ChildLoader.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 93-96 are not covered by tests
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.cloudbees.hudson.plugins.folder;

import hudson.Extension;
import hudson.model.TopLevelItem;
import hudson.util.CopyOnWriteMap;
import java.io.File;
import java.util.Map;
import java.util.function.Function;

@Extension
public final class DefaultChildLoader extends ChildLoader {

@Override
public <K, V extends TopLevelItem> Map<K, V> loadChildren(
AbstractFolder<V> parent, File modulesDir, Function<? super V, ? extends K> key) {
CopyOnWriteMap.Tree<K, V> configurations = new CopyOnWriteMap.Tree<>();
if (!ensureDirExists(modulesDir, parent)) {

Check warning on line 17 in src/main/java/com/cloudbees/hudson/plugins/folder/DefaultChildLoader.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 17 is only partially covered, one branch is missing
return configurations;

Check warning on line 18 in src/main/java/com/cloudbees/hudson/plugins/folder/DefaultChildLoader.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 18 is not covered by tests
}

File[] subdirs = modulesDir.listFiles(File::isDirectory);
if (subdirs == null) {

Check warning on line 22 in src/main/java/com/cloudbees/hudson/plugins/folder/DefaultChildLoader.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 22 is only partially covered, one branch is missing
return configurations;

Check warning on line 23 in src/main/java/com/cloudbees/hudson/plugins/folder/DefaultChildLoader.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 23 is not covered by tests
}

Map<String, V> byDirName = getItemsByDirName(parent);
for (File subdir : subdirs) {
// Try to retain the identity of an existing child object if we can.
V existingItem = byDirName.get(subdir.getName());
V item = loadItem(parent, subdir, existingItem);
if (item != null) {

Check warning on line 31 in src/main/java/com/cloudbees/hudson/plugins/folder/DefaultChildLoader.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 31 is only partially covered, one branch is missing
configurations.put(key.apply(item), item);
}
}

return configurations;
}
}