Class ChildNameGenerator<P extends AbstractFolder<I>,I extends TopLevelItem>
- java.lang.Object
-
- com.cloudbees.hudson.plugins.folder.ChildNameGenerator<P,I>
-
- Type Parameters:
P
- the type ofAbstractFolder
.I
- the type ofTopLevelItem
within the folder.
public abstract class ChildNameGenerator<P extends AbstractFolder<I>,I extends TopLevelItem> extends Object
Provides a way for aComputedFolder
to break the association between the directory names on disk that are used to store its items and theItem.getName()
which is used to create the URL of the item.NOTE: if you need to implement this functionality, you need to ensure that users cannot rename items within the
ComputedFolder
as renaming is not supported when using aChildNameGenerator
.Challenges:
- See the notes on
itemNameFromItem(AbstractFolder, TopLevelItem)
anddirNameFromItem(AbstractFolder, TopLevelItem)
regarding the constraints on how to name things - There are some items which need the
PersistenceRoot.getRootDir()
during construction (those are bold evil item types that leak side-effects, you should fix them if you find them). While you wait for them to be fixed you will need to work-around the issue by ensuring that you callbeforeCreateItem(AbstractFolder, String, String)
passing theItem.getName()
you want the item to have and the ideal unmangled name before you callnew ChildItemType(parent,name)
and then callafterItemCreated(Trace)
when the constructor has returned. Then insure that youritemNameFromItem(AbstractFolder, TopLevelItem)
anddirNameFromItem(AbstractFolder, TopLevelItem)
fall back toidealNameFromItem(AbstractFolder, TopLevelItem)
when the magic property they are looking for is missing.
ComputedFolder
using thisChildNameGenerator
will be attaching into theItem
the actual name, typically via aJobProperty
orAction
(bewareTransientActionFactory
implementations may want to invokePersistenceRoot.getRootDir()
which will trigger a stack overflow though, so safer to stick with theJobProperty
or equivalent). TheitemNameFromItem(AbstractFolder, TopLevelItem)
method's task is to find the stored name and return the name stored within ornull
if that information is missing (in which caseitemNameFromLegacy(AbstractFolder, String)
will be called to try and infer the name from the disk name that theItem
is being loaded from. A similar relation exists for thedirNameFromItem(AbstractFolder, TopLevelItem)
anddirNameFromLegacy(AbstractFolder, String)
methods.- Since:
- 5.17
-
-
Nested Class Summary
Nested Classes Modifier and Type Class Description static class
ChildNameGenerator.Trace
Traces the creation of a newItem
in a folder.
-
Field Summary
Fields Modifier and Type Field Description static String
CHILD_NAME_FILE
The name of the file that contains the actual name of the child item.
-
Constructor Summary
Constructors Constructor Description ChildNameGenerator()
-
Method Summary
All Methods Static Methods Instance Methods Abstract Methods Concrete Methods Modifier and Type Method Description static ChildNameGenerator.Trace
beforeCreateItem(AbstractFolder<?> project, String itemName, String idealName)
Work-around helper method to "fix"Item
constructors that have on-disk side-effects and therefore needPersistenceRoot.getRootDir()
to work during the constructor.abstract String
dirNameFromItem(P parent, I item)
Infers the directory name in which theItem
instance itself should be stored.abstract String
dirNameFromLegacy(P parent, String legacyDirName)
dirNameFromItem(AbstractFolder, TopLevelItem)
could not help, we are loading the item for the first time since theChildNameGenerator
was enabled for the parent folder type, this method's mission is to pretend thelegacyDirName
is the "mostly correct" name and turn this into the filesystem safe mangled equivalent name to use going forward.protected String
idealNameFromItem(P parent, I item)
Looks up theItem
to see if we stored the ideal name before invoking the constructor that is having on-disk side-effects before the object has escapedbeforeCreateItem(AbstractFolder, String, String)
abstract String
itemNameFromItem(P parent, I item)
Infers theItem.getName()
from theItem
instance itself.abstract String
itemNameFromLegacy(P parent, String legacyDirName)
itemNameFromItem(AbstractFolder, TopLevelItem)
could not help, we are loading the item for the first time since theChildNameGenerator
was enabled for the parent folder type, this method's mission is to pretend thelegacyDirName
is the "mostly correct" name and turn this into the actual name.abstract void
recordLegacyName(P parent, I item, String legacyDirName)
Record the ideal name inferred in the item when it was missing and has been inferred from the legacy directory name.
-
-
-
Field Detail
-
CHILD_NAME_FILE
public static final String CHILD_NAME_FILE
The name of the file that contains the actual name of the child item. This file is to allow a Jenkins Administrator to determine which child is which when dealing with a folder containing child names that have been mangled.If there is nothing else to go on, this file will be used in preference to the child directory name, but as it is too easy for users to mistakenly think changing the contents of the file will rename the child (which could cause data loss for the computed folder's child) it is better for implementations to store the definitive ideal name in a
JobProperty
,Action
or equivalent that is attached directly to theItem
.- See Also:
- Constant Field Values
-
-
Method Detail
-
beforeCreateItem
@NonNull public static final ChildNameGenerator.Trace beforeCreateItem(@NonNull AbstractFolder<?> project, @NonNull String itemName, @NonNull String idealName)
Work-around helper method to "fix"Item
constructors that have on-disk side-effects and therefore needPersistenceRoot.getRootDir()
to work during the constructor.- Parameters:
project
- theAbstractFolder
.itemName
- the name that will be returned byItem.getName()
when the item is constructed. This is the second parameter ofAbstractItem(ItemGroup, String)
. This one would be the one with URL path segment escaping.idealName
- the original name before whatever URL path segment escaping you applied- Returns:
- the
ChildNameGenerator.Trace
to keep track of when we can remove the memory of the creation process. PleaseChildNameGenerator.Trace.close()
the trace after the item is created.
-
idealNameFromItem
@CheckForNull protected final String idealNameFromItem(@NonNull P parent, @NonNull I item)
Looks up theItem
to see if we stored the ideal name before invoking the constructor that is having on-disk side-effects before the object has escapedbeforeCreateItem(AbstractFolder, String, String)
- Parameters:
parent
- the parent within which the item is being created.item
- the partially created item.- Returns:
- the ideal name of the item.
-
itemNameFromItem
@CheckForNull public abstract String itemNameFromItem(@NonNull P parent, @NonNull I item)
Infers theItem.getName()
from theItem
instance itself. Challenges include:- There are some characters that it would be really bad to return in the item name, such as
"/" / "?" / "#" / "[" / "]" / "\"
as these could end up modifying the effective URL - There are names that it would be bad to return as the item name, such as
"" / "." / ".."
as these could end creating broken effective URLs
- Parameters:
parent
- the parent within which the item is being loaded.item
- the partially loaded item (take care what methods you call, the item will not have a reference to its parent).- Returns:
- the name of the item.
- There are some characters that it would be really bad to return in the item name, such as
-
dirNameFromItem
@CheckForNull public abstract String dirNameFromItem(@NonNull P parent, @NonNull I item)
Infers the directory name in which theItem
instance itself should be stored. Challenges include:- The only really filesystem safe characters are
A-Za-z0-9_.-
- Because of Windows and allowing for users to migrate their Jenkins from Unix to Windows and vice-versa,
some names are reserved names under Windows:
AUX, COM1, COM2, ..., COM9, CON, LPT1, LPT2, ..., LPT9, NUL, PRN
plus all case variations of these names plus the variants where a single.
is appended, you need to map those to something else - Don't make the filenames too long. Try to keep them under 32 characters. If you can go smaller, even better.
- Get it right the first time
- Parameters:
parent
- the parent within which the item is being loaded.item
- the partially loaded item (take care what methods you call, the item will not have a reference to its parent).- Returns:
- the filesystem safe mangled equivalent name of the item.
- The only really filesystem safe characters are
-
itemNameFromLegacy
@NonNull public abstract String itemNameFromLegacy(@NonNull P parent, @NonNull String legacyDirName)
itemNameFromItem(AbstractFolder, TopLevelItem)
could not help, we are loading the item for the first time since theChildNameGenerator
was enabled for the parent folder type, this method's mission is to pretend thelegacyDirName
is the "mostly correct" name and turn this into the actual name. Challenges include:- Previously the name may have been over-encoded with
Util.rawEncode(String)
so you may need to decode it first - There are some characters that it would be really bad to return in the item name, such as
"/" / "?" / "#" / "[" / "]" / "\"
as these could end up modifying the effective URL - There are names that it would be bad to return as the item name, such as
"" / "." / ".."
as these could end creating broken effective URLs
- Parameters:
parent
- the parent within which the item is being loaded.legacyDirName
- the directory name that we are loading an item from.- Returns:
- the name of the item.
- Previously the name may have been over-encoded with
-
dirNameFromLegacy
@NonNull public abstract String dirNameFromLegacy(@NonNull P parent, @NonNull String legacyDirName)
dirNameFromItem(AbstractFolder, TopLevelItem)
could not help, we are loading the item for the first time since theChildNameGenerator
was enabled for the parent folder type, this method's mission is to pretend thelegacyDirName
is the "mostly correct" name and turn this into the filesystem safe mangled equivalent name to use going forward. Challenges include:- The only really filesystem safe characters are
A-Za-z0-9_.-
- Because of Windows and allowing for users to migrate their Jenkins from Unix to Windows and vice-versa,
some names are reserved names under Windows:
AUX, COM1, COM2, ..., COM9, CON, LPT1, LPT2, ..., LPT9, NUL, PRN
plus all case variations of these names plus the variants where a single.
is appended, you need to map those to something else - Don't make the filenames too long. Try to keep them under 32 characters. If you can go smaller, even better.
- Get it right the first time
- Parameters:
parent
- the parent within which the item is being loaded.legacyDirName
- the directory name that we are loading an item from.- Returns:
- the filesystem safe mangled equivalent name of the item.
- The only really filesystem safe characters are
-
recordLegacyName
public abstract void recordLegacyName(P parent, I item, String legacyDirName) throws IOException
Record the ideal name inferred in the item when it was missing and has been inferred from the legacy directory name.- Parameters:
parent
- the parent.item
- the item.legacyDirName
- the name of the directory that the item was loaded from.- Throws:
IOException
- if the ideal name could not be attached to the item.
-
-