Skip to content
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

Servlet 6.x (EE11) demos #12678

Draft
wants to merge 12 commits into
base: jetty-12.1.x
Choose a base branch
from
Draft
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@

import java.io.IOException;
import java.time.Instant;
import java.util.Objects;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;

import org.eclipse.jetty.http.CompressedContentFormat;
import org.eclipse.jetty.http.HttpField;
Expand Down Expand Up @@ -69,6 +71,7 @@ public class CachingHttpContentFactory implements HttpContent.Factory
private final HttpContent.Factory _authority;
private final ConcurrentHashMap<String, CachingHttpContent> _cache = new ConcurrentHashMap<>();
private final AtomicLong _cachedSize = new AtomicLong();
private final AtomicBoolean _shrinking = new AtomicBoolean();
private final ByteBufferPool.Sized _bufferPool;
private int _maxCachedFileSize = DEFAULT_MAX_CACHED_FILE_SIZE;
private int _maxCachedFiles = DEFAULT_MAX_CACHED_FILES;
Expand Down Expand Up @@ -138,35 +141,45 @@ public void setMaxCachedFiles(int maxCachedFiles)

private void shrinkCache()
{
// While we need to shrink
int numCacheEntries = _cache.size();
while (numCacheEntries > 0 && (numCacheEntries > _maxCachedFiles || _cachedSize.get() > _maxCacheSize))
// Only 1 thread shrinking at once
if (_shrinking.compareAndSet(false, true))
{
// Scan the entire cache and generate an ordered list by last accessed time.
SortedSet<CachingHttpContent> sorted = new TreeSet<>((c1, c2) ->
try
{
long delta = NanoTime.elapsed(c2.getLastAccessedNanos(), c1.getLastAccessedNanos());
if (delta != 0)
return delta < 0 ? -1 : 1;

delta = c1.getContentLengthValue() - c2.getContentLengthValue();
if (delta != 0)
return delta < 0 ? -1 : 1;

return c1.getKey().compareTo(c2.getKey());
});
sorted.addAll(_cache.values());

// TODO: Can we remove the buffers from the content before evicting.
// Invalidate least recently used first
for (CachingHttpContent content : sorted)
// While we need to shrink
int numCacheEntries = _cache.size();
while (numCacheEntries > 0 && (numCacheEntries > _maxCachedFiles || _cachedSize.get() > _maxCacheSize))
{
// Scan the entire cache and generate an ordered list by last accessed time.
SortedSet<CachingHttpContent> sorted = new TreeSet<>((c1, c2) ->
{
long delta = NanoTime.elapsed(c2.getLastAccessedNanos(), c1.getLastAccessedNanos());
if (delta != 0)
return delta < 0 ? -1 : 1;

delta = c1.getContentLengthValue() - c2.getContentLengthValue();
if (delta != 0)
return delta < 0 ? -1 : 1;

return c1.getKey().compareTo(c2.getKey());
});
sorted.addAll(_cache.values());

// Invalidate least recently used first
for (CachingHttpContent content : sorted)
{
if (_cache.size() <= _maxCachedFiles && _cachedSize.get() <= _maxCacheSize)
break;
removeFromCache(content);
}

numCacheEntries = _cache.size();
}
}
finally
{
if (_cache.size() <= _maxCachedFiles && _cachedSize.get() <= _maxCacheSize)
break;
removeFromCache(content);
_shrinking.set(false);
}

numCacheEntries = _cache.size();
}
}

Expand Down Expand Up @@ -290,7 +303,7 @@ protected interface CachingHttpContent extends HttpContent

protected class CachedHttpContent extends HttpContent.Wrapper implements CachingHttpContent
{
private final RetainableByteBuffer _buffer;
private final AtomicReference<RetainableByteBuffer> _buffer = new AtomicReference<>();
private final String _cacheKey;
private final HttpField _etagField;
private volatile long _lastAccessed;
Expand Down Expand Up @@ -323,7 +336,7 @@ public CachedHttpContent(String key, HttpContent httpContent)
throw new IllegalArgumentException("Resource is too large: length " + contentLengthValue + " > " + _maxCachedFileSize);

// Read the content into memory
_buffer = IOResources.toRetainableByteBuffer(httpContent.getResource(), _bufferPool);
_buffer.set(Objects.requireNonNull(IOResources.toRetainableByteBuffer(httpContent.getResource(), _bufferPool)));

_characterEncoding = httpContent.getCharacterEncoding();
_compressedFormats = httpContent.getPreCompressedContentFormats();
Expand Down Expand Up @@ -354,23 +367,40 @@ public String getKey()
@Override
public void writeTo(Content.Sink sink, long offset, long length, Callback callback)
{
try
RetainableByteBuffer buffer = _buffer.get();
if (buffer != null)
{
_buffer.retain();
sink.write(true, BufferUtil.slice(_buffer.getByteBuffer(), (int)offset, (int)length), Callback.from(_buffer::release, callback));
}
catch (Throwable x)
{
// BufferUtil.slice() may fail if offset and/or length are out of bounds.
_buffer.release();
callback.failed(x);
try
{
buffer.retain();
try
{
sink.write(true, BufferUtil.slice(buffer.getByteBuffer(), (int)offset, (int)length), Callback.from(buffer::release, callback));
Copy link
Contributor

@lorban lorban Jan 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't you want to also null out the buffer ref in the callback?

}
catch (Throwable x)
{
// BufferUtil.slice() may fail if offset and/or length are out of bounds.
if (buffer.release())
_buffer.set(null);
callback.failed(x);
}
return;
}
catch (Throwable ignored)
{
LOG.trace("ignored", ignored);
}
}

getWrapped().writeTo(sink, offset, length, callback);
}

@Override
public void release()
{
_buffer.release();
RetainableByteBuffer buffer = _buffer.get();
if (buffer != null && buffer.release())
_buffer.set(null);
}

@Override
Expand Down Expand Up @@ -406,7 +436,8 @@ public HttpField getContentLength()
@Override
public long getContentLengthValue()
{
return _buffer.remaining();
RetainableByteBuffer buffer = _buffer.get();
return buffer == null ? getWrapped().getContentLengthValue() : buffer.remaining();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,151 +2,101 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.jetty.ee11.demos</groupId>
<artifactId>jetty-ee11-demos</artifactId>
<groupId>org.eclipse.jetty.demos</groupId>
<artifactId>jetty-servlet6-demos</artifactId>
<version>12.1.0-SNAPSHOT</version>
</parent>
<artifactId>jetty-ee11-demo-embedded</artifactId>
<name>EE11 :: Demo :: Embedded Jetty</name>
<description>Embedded Jetty Demos</description>
<artifactId>jetty-servlet6-demo-embedded</artifactId>
<packaging>war</packaging>
<name>Servlet 6 :: Demo :: Embedded</name>
<description>Embedded Jetty Servlet 6 Demos</description>
<properties>
<bundle-symbolic-name>${project.groupId}.embedded</bundle-symbolic-name>
</properties>
<dependencies>
<dependency>
<groupId>jakarta.transaction</groupId>
<artifactId>jakarta.transaction-api</artifactId>
<scope>compile</scope>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>${ee11.jakarta.servlet.api.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-conscrypt-server</artifactId>
<artifactId>jetty-alpn-java-client</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-java-server</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-deploy</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jmx</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-rewrite</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-security</artifactId>
<artifactId>jetty-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util-ajax</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee11</groupId>
<artifactId>jetty-ee11-annotations</artifactId>
<artifactId>jetty-slf4j-impl</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee11</groupId>
<artifactId>jetty-ee11-apache-jsp</artifactId>
<groupId>org.eclipse.jetty.demos</groupId>
<artifactId>jetty-servlet6-demo-mock-resources</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee11</groupId>
<artifactId>jetty-ee11-glassfish-jstl</artifactId>
<artifactId>jetty-ee11-annotations</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee11</groupId>
<artifactId>jetty-ee11-plus</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee11</groupId>
<artifactId>jetty-ee11-proxy</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee11</groupId>
<artifactId>jetty-ee11-servlets</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee11</groupId>
<artifactId>jetty-ee11-webapp</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee11.demos</groupId>
<artifactId>jetty-ee11-demo-async-rest-webapp</artifactId>
<version>${project.version}</version>
<type>war</type>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee11.demos</groupId>
<artifactId>jetty-ee11-demo-jndi-webapp</artifactId>
<version>${project.version}</version>
<type>war</type>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee11.demos</groupId>
<artifactId>jetty-ee11-demo-jsp-webapp</artifactId>
<version>${project.version}</version>
<type>war</type>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee11.demos</groupId>
<artifactId>jetty-ee11-demo-mock-resources</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee11.demos</groupId>
<artifactId>jetty-ee11-demo-simple-webapp</artifactId>
<version>${project.version}</version>
<type>war</type>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee11.demos</groupId>
<artifactId>jetty-ee11-demo-spec-webapp</artifactId>
<version>${project.version}</version>
<type>war</type>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee11.websocket</groupId>
<artifactId>jetty-ee11-websocket-jakarta-server</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee11.websocket</groupId>
<artifactId>jetty-ee11-websocket-jetty-server</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee11.websocket</groupId>
<artifactId>jetty-ee11-websocket-servlet</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>jetty-http2-server</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>jetty-websocket-jetty-client</artifactId>
<scope>test</scope>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// ========================================================================
//

package org.eclipse.jetty.ee9.demos;
package org.eclipse.jetty.servlet6.embedded;

import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// ========================================================================
//

package org.eclipse.jetty.ee9.demos;
package org.eclipse.jetty.servlet6.embedded;

import java.io.IOException;
import java.io.PrintWriter;
Expand Down
Loading
Loading