-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Jetty 12 : Resource API Review #8474
Comments
You've left out the use-case from the jetty maven plugin: the |
With regards to the question of should resolved return s resource that does not exist, i think it should. There is the rare case of needing to support PUT. Also i think we create resources in things like quick start Checking for exists if pretty much the same hassle as null checking anyway |
This is how it currently is in Jetty 12.0.x. You can combine (using I'll add this as a use case above. |
Checking Jetty 9/10/11 I see a PutFilter, not there in Jetty 12.0.x It doesn't use Resource anyway, so not a problem. QuickStart has a few places where it creates a Resource, those all need to be addressed, as it doesn't take into account composite behaviors, and can easily break right now (eg: if there's more than 1 |
…issame Issue #8474 - Jetty 12 - Eliminate `Resource.isSame`
…ismemorymappable Issue #8474 - Jetty 12 - Eliminate `Resource.isMemoryMappable`
Issue #8474 - Jetty 12 - Introduce `Iterable<Resource>` to base `Resource`
…ified-to-instant Issue #8474 - Jetty 12 - Change signature of `Resource.lastModified()`
* Introduce Resource.getFileName and use it * Correct signature of Resource.list and use it Signed-off-by: Joakim Erdfelt <[email protected]>
…FileName()` addition (#8582) * Issue #8474 - Resource.list and Resource.getFileName work * Correcting signature of Resource.list and use it * Introduce Resource.getFileName and use it * Adding URIUtil.addPath() test to show file+str behavior Signed-off-by: Joakim Erdfelt <[email protected]>
The only remaining quirk is the The idea of making For a few reasons.
I could considered an So leaving the 3 flawed implementations of |
Closing. |
org.eclipse.jetty.util.resource.Resource Proposal
High level
The
Resource
abstract class should be made into an Interface, with minimal default implementations (if any).Existing static methods behaviors stay.
The
ResourceCollection
should be renamed.It's ultimately how we composite a set of Resources together.
Its current implementation is a List, so calling it
ResourceList
is tempting.However, the current implementation flattens and uniques the incoming Resources, so it cannot have a nested composite inside of it.
This was done in early iterations when ResourceCollection was managing the resources (mounts).
It was felt that having multiple composites, possibly from different management scopes (now called
ResourceFactory
) could be a recipe for shooting your whole leg off.This concern is not valid anymore IMO.
We could allow a composite to have other composites.
So lets call it
CompositeResource
.My argument here is that
List
implies the same entry type, and is flat, butComposite
makes it clear that it structured.The
Resource
interface should also expose aIterable<Resource>
(more on that below).Use Cases
Use Case: Create Resource from multiple Paths and pass it around
Simple creation, but used as a composite
Allowed URIs can reference locations that do not exist.
A composite with zero entries should throw an exception.
Use Case: Create Resource from single path and pass it around
Simple creation, used as a simple resource
Allowed URI or Path can reference locations that do not exist.
Use Case: combine multiple resources into a single Resource
Combine Resources together to form a composite Resource.
Constructing a composite resource from other resources, even other composite resources.
Duplicate entries are eliminated.
The order of the entries in the
combine(Resource .... resources)
is relevant to the resulting composite.Use Case: Resolve sub-resource
We have a resource, and we want a sub resource.
This API is
.resolve(String)
, and is more flexible in inputs thanPath.resolve(String)
orURI.resolve(String)
.But should it ever return a Resource if that resource doesn't exist? Potential API change, return null if it doesn't exist.
The use case of using
Resource.resolve(a)
against something that was returned from a.resolve(b)
earlier is actually quite rare in our code, the most common use case is to take the initial configured Resource and only resolve deeply.A
.resolve(String)
can result in a composite of entries, so we need to handle that possibility in code.Use Case: Alias Checking
If the input
Resource.resolve(String)
can be a reference to a file in a non-real way.There is a need to know if this reference results in a non-standard reference to said Resource.
Differences in the URI provided vs URI of path, or discovered Path vs Real Path, indicate if this reference is an alias to another Path location. (eg: symlink, windows alt references, case-insensitive filesystems, etc)
Use Case: We want a shallow directory listing
Typical fetch of shallow list from resource.
Input Resource might be a composite.
No expected ordering.
We should be able to take the List, so we can filter, sort, map, etc it further.
Use Case: Deep walk / Find resources
Sometimes we need to walk deeply all of the children of the resources, even if it's a composite.
API Proposals
Resource implements Iterable ✔️
NEW API.
The core
Resource
implementation should also be able to exposed as anIterable<Resource>
Nested composites makes this complicated, but not impossible.Resource Resource.resolve(String)
Keep this.
Behavior Change: When CompositeResource has nested CompositeResource, call nested.resolve(String), this will resultin a CompositeResource return value that has no nested CompositeResource.
exist()
API, which would be removed)Resource Resource.resolveNonExistent(String)
Path Resource.getPath() ✔️
Keep this.
boolean Resource.isContainedIn(Resource r)
Keep this.
Path.startsWith(Path)
should be sufficient :X:children.isContainedIn()
, first to return true.boolean Resource.isSame(Resource r) ✔️
Eliminate, not used anywhere outside of testing
boolean Resource.exists() :X:
Neutral opinion.
Consider elimination, as this would have no meaning if thechange above in
resolve(String)
not returning a resource that doesn't exist.Although, if the
newResource(...)
could create a Resource that doesn't exist (yet).Files.exists
:X:boolean Resource.isDirectory() :X:
Keep this.
Files.isDirectory
, false if IOException :X:Instant Resource.lastModified() :X:
SIGNATURE CHANGE: Different return type.
Was long (as millis since epoch).
Now an Instant, as this is more generally useful. :X:
Files.getLastModified
, EPOCH if IOException :X:and even nested composites,:X:this should return either EPOCH
or walk the compositeand find the newest lastmodified of thecomposite (not a fan of this approach).
long Resource.length() :X:
Keep this.
Files.size
, -1L if IOException :X:URI Resource.getURI() :X:
Path.toUri()
:X:String Resource.getName() :X:
Keep it.
Path.toAbsolutePath().toString()
:X:InputStream Resource.newInputStream()
Evaluate use.
There has been a desire to remove this and have this done in HttpContent instead.
Files.newInputStream
ReadableByteChannel Resource.newReadableByteChannel()
Evaluate use.
There has been a desire to remove this and have this done in HttpContent instead.
Files.newByteChannel
SeekableByteChannel Resource.newByteChannel()
NEW API, only if we keep the other
newInputStream
/newReadableByteChannel
APIs aboveNow SeekableByteChannel.
Files.newByteChannel
boolean Resource.isMemoryMappable()
Eliminate it.
This appears to not be used.
List<String> Resource.list()
Eliminate this signature.
List<Resource> Resource.list()
NEW SIGNATURE
Returns a shallow list of resources that the Resource contains.
This API will not remove duplicate (by filename) entries like in Jetty 9/10/11.
That is a task for the user of the API, not built into the Resource layer.
For example, classpath building requires the duplicates to be present, HTML directory listing maybe doesn't.
String Resource.getFileName()
NEW API
Returns the filename portion of the path, or null if the Resource is not based on a Path (eg: MemoryResource)
boolean Resource.isAlias()
Eliminate, in favor of new API.
Path Resource.getAlias()
NEW API: Performs same algorithm as what is in the current
PathResource.checkAliasPath()
right now.A return of null means there is no alias to worry about.
URI Resource.getAlias()
Eliminate, not used in code anywhere.
void Resource.copyTo(Path)
Unfortunately, as the user of the Resource you need to be sure that your Resource is even capable of being copied, or even needs to be copied.
The behaviors in Jetty 9 thru 11 in this regard is also quietly broken in many places (as copyTo() doesn't do anything in a few cases I looked into in Jetty 11)
In Jetty 12 if you are handed a resource (which is always a Path btw)
For Jar/Zip/War files, you have 2 ways to represent them in a Resource.
As the Jar/Zip/War itself, which is just a File, and is no more special then any other File.
As the root directory of the Jar/Zip/War archive (represented via the zipfs layer), this is a Directory, and is no different than any other Directory.
The logic that we need to settle on as a result of this issue ...
If the
Resource _____
thencopyTo(Path)
means what?Is a File
Is a Directory
does not exist
Is a ResourceCollection
String Resource.getWeakETag()
Eliminate.
This should be removed and centralized in the cache handling for ResourceService
String Resource.getWeakETag(String suffix)
This should be removed and centralized in the cache handling for ResourceService
byte[] Resource.longToBytes(long)
INTERNAL API, private, used only by getWeakETag() impls
This can be removed once the two getWeakETag() methods are centralized in the cache handling for ResourceService
Collection Resource.getAllResources()
Eliminate, not used outside of test cases.
List<Resource> walk(Predicate ... predicates);
NEW API.
Deep walk of the resource.
Files.walk()
with predicates and returnFiles.walk()
with predicates and returnResource findFirst(String);
NEW API.
Only needed if we support Composite.
Different than
.resolve(String)
in that it guarantees a single Resource, never a composite.The text was updated successfully, but these errors were encountered: