Introducing Java Servlet Container beta.

This commit is contained in:
Max Romanov
2019-02-28 18:02:42 +03:00
parent ec7319d32c
commit 5bfdebb9e4
95 changed files with 13360 additions and 1 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
package nginx.unit;
import javax.servlet.DispatcherType;
public interface DynamicDispatcherRequest
{
public void setDispatcherType(DispatcherType type);
}

View File

@@ -0,0 +1,15 @@
package nginx.unit;
public interface DynamicPathRequest
extends DynamicDispatcherRequest
{
public void setServletPath(String servlet_path, String path_info);
public void setServletPath(String filter_path, String servlet_path, String path_info);
public void setRequestURI(String uri);
public void setQueryString(String query);
public String getFilterPath();
}

View File

@@ -0,0 +1,150 @@
package nginx.unit;
import java.util.List;
import java.util.Map;
import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.UrlEncoded;
public class ForwardRequestWrapper implements DynamicPathRequest
{
private final Request request_;
private final boolean keep_attrs;
private final String orig_filter_path;
private final String orig_servlet_path;
private final String orig_path_info;
private final String orig_uri;
private final String orig_context_path;
private final String orig_query;
private final DispatcherType orig_dtype;
private MultiMap<String> orig_parameters;
public ForwardRequestWrapper(ServletRequest request)
{
if (request instanceof Request) {
request_ = (Request) request;
} else {
request_ = (Request) request.getAttribute(Request.BARE);
}
keep_attrs = request_.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI) != null;
orig_dtype = request_.getDispatcherType();
orig_filter_path = request_.getFilterPath();
orig_servlet_path = request_.getServletPath();
orig_path_info = request_.getPathInfo();
orig_uri = request_.getRequestURI();
orig_context_path = request_.getContextPath();
orig_query = request_.getQueryString();
}
@Override
public void setDispatcherType(DispatcherType type)
{
request_.setDispatcherType(type);
/*
9.4.2 Forwarded Request Parameters
...
Note that these attributes must always reflect the information in
the original request even under the situation that multiple
forwards and subsequent includes are called.
*/
if (keep_attrs) {
return;
}
/*
9.4.2 Forwarded Request Parameters
...
The values of these attributes must be equal to the return values
of the HttpServletRequest methods getRequestURI, getContextPath,
getServletPath, getPathInfo, getQueryString respectively, invoked
on the request object passed to the first servlet object in the
call chain that received the request from the client.
*/
request_.setAttribute_(RequestDispatcher.FORWARD_SERVLET_PATH, orig_servlet_path);
request_.setAttribute_(RequestDispatcher.FORWARD_PATH_INFO, orig_path_info);
request_.setAttribute_(RequestDispatcher.FORWARD_REQUEST_URI, orig_uri);
request_.setAttribute_(RequestDispatcher.FORWARD_CONTEXT_PATH, orig_context_path);
request_.setAttribute_(RequestDispatcher.FORWARD_QUERY_STRING, orig_query);
}
@Override
public void setServletPath(String servlet_path, String path_info)
{
request_.setServletPath(servlet_path, path_info);
}
@Override
public void setServletPath(String filter_path, String servlet_path, String path_info)
{
request_.setServletPath(filter_path, servlet_path, path_info);
}
@Override
public void setRequestURI(String uri)
{
request_.setRequestURI(uri);
}
@Override
public void setQueryString(String query)
{
if (query != null) {
orig_parameters = request_.getParameters();
MultiMap<String> parameters = new MultiMap<>();
UrlEncoded.decodeUtf8To(query, parameters);
for (Map.Entry<String, List<String>> e: orig_parameters.entrySet()) {
parameters.addValues(e.getKey(), e.getValue());
}
request_.setParameters(parameters);
request_.setQueryString(query);
}
}
@Override
public String getFilterPath()
{
return request_.getFilterPath();
}
public void close()
{
request_.setDispatcherType(orig_dtype);
request_.setRequestURI(orig_uri);
request_.setServletPath(orig_filter_path, orig_servlet_path, orig_path_info);
request_.setQueryString(orig_query);
if (orig_parameters != null) {
request_.setParameters(orig_parameters);
}
if (keep_attrs) {
return;
}
request_.setAttribute_(RequestDispatcher.FORWARD_SERVLET_PATH, null);
request_.setAttribute_(RequestDispatcher.FORWARD_PATH_INFO, null);
request_.setAttribute_(RequestDispatcher.FORWARD_REQUEST_URI, null);
request_.setAttribute_(RequestDispatcher.FORWARD_CONTEXT_PATH, null);
request_.setAttribute_(RequestDispatcher.FORWARD_QUERY_STRING, null);
}
}

View File

@@ -0,0 +1,42 @@
package nginx.unit;
import java.lang.String;
import java.util.Enumeration;
import java.util.NoSuchElementException;
public class HeaderNamesEnumeration implements Enumeration<String> {
private long headers_ptr;
private long size;
private long pos = 0;
public HeaderNamesEnumeration(long _headers_ptr, long _size) {
headers_ptr = _headers_ptr;
size = _size;
}
@Override
public boolean hasMoreElements()
{
if (pos >= size) {
return false;
}
pos = nextElementPos(headers_ptr, size, pos);
return pos < size;
}
static private native long nextElementPos(long headers_ptr, long size, long pos);
@Override
public String nextElement()
{
if (pos >= size) {
throw new NoSuchElementException();
}
return nextElement(headers_ptr, size, pos++);
}
static private native String nextElement(long headers_ptr, long size, long pos);
}

View File

@@ -0,0 +1,40 @@
package nginx.unit;
import java.lang.String;
import java.util.Enumeration;
public class HeadersEnumeration implements Enumeration<String> {
private long headers_ptr;
private long size;
private long initial_pos;
private long pos;
public HeadersEnumeration(long _headers_ptr, long _size, long _initial_pos) {
headers_ptr = _headers_ptr;
size = _size;
initial_pos = _initial_pos;
pos = _initial_pos;
}
@Override
public boolean hasMoreElements()
{
if (pos >= size) {
return false;
}
pos = nextElementPos(headers_ptr, size, initial_pos, pos);
return pos < size;
}
static private native long nextElementPos(long headers_ptr, long size, long initial_pos, long pos);
@Override
public String nextElement()
{
return nextElement(headers_ptr, size, initial_pos, pos++);
}
static private native String nextElement(long headers_ptr, long size, long initial_pos, long pos);
}

View File

@@ -0,0 +1,88 @@
package nginx.unit;
import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletRequest;
public class IncludeRequestWrapper implements DynamicPathRequest
{
private final Request request_;
private final Object orig_servlet_path_attr;
private final Object orig_path_info_attr;
private final Object orig_uri_attr;
private final Object orig_context_path_attr;
private final Object orig_query_string_attr;
private final DispatcherType orig_dtype;
private String filter_path_;
public IncludeRequestWrapper(ServletRequest request)
{
if (request instanceof Request) {
request_ = (Request) request;
} else {
request_ = (Request) request.getAttribute(Request.BARE);
}
orig_servlet_path_attr = request_.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH);
orig_path_info_attr = request_.getAttribute(RequestDispatcher.INCLUDE_PATH_INFO);
orig_uri_attr = request_.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI);
orig_context_path_attr = request_.getAttribute(RequestDispatcher.INCLUDE_CONTEXT_PATH);
orig_query_string_attr = request_.getAttribute(RequestDispatcher.INCLUDE_QUERY_STRING);
orig_dtype = request_.getDispatcherType();
request_.setAttribute_(RequestDispatcher.INCLUDE_CONTEXT_PATH, request_.getContextPath());
}
@Override
public void setDispatcherType(DispatcherType type)
{
request_.setDispatcherType(type);
}
@Override
public void setServletPath(String servlet_path, String path_info)
{
setServletPath(servlet_path, servlet_path, path_info);
}
@Override
public void setServletPath(String filter_path, String servlet_path, String path_info)
{
request_.setAttribute_(RequestDispatcher.INCLUDE_SERVLET_PATH, servlet_path);
request_.setAttribute_(RequestDispatcher.INCLUDE_PATH_INFO, path_info);
filter_path_ = filter_path;
}
@Override
public void setRequestURI(String uri)
{
request_.setAttribute_(RequestDispatcher.INCLUDE_REQUEST_URI, uri);
}
@Override
public void setQueryString(String query)
{
request_.setAttribute_(RequestDispatcher.INCLUDE_QUERY_STRING, query);
}
@Override
public String getFilterPath()
{
return filter_path_;
}
public void close()
{
request_.setDispatcherType(orig_dtype);
request_.setAttribute_(RequestDispatcher.INCLUDE_SERVLET_PATH, orig_servlet_path_attr);
request_.setAttribute_(RequestDispatcher.INCLUDE_PATH_INFO, orig_path_info_attr);
request_.setAttribute_(RequestDispatcher.INCLUDE_REQUEST_URI, orig_uri_attr);
request_.setAttribute_(RequestDispatcher.INCLUDE_CONTEXT_PATH, orig_context_path_attr);
request_.setAttribute_(RequestDispatcher.INCLUDE_QUERY_STRING, orig_query_string_attr);
}
}

View File

@@ -0,0 +1,117 @@
package nginx.unit;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
public class IncludeResponseWrapper extends HttpServletResponseWrapper {
private static final Charset UTF_8 = StandardCharsets.UTF_8;
public IncludeResponseWrapper(ServletResponse response)
{
super((HttpServletResponse) response);
}
@Override
public void addCookie(Cookie cookie)
{
trace("addCookie: " + cookie.getName() + "=" + cookie.getValue());
}
@Override
public void addDateHeader(String name, long date)
{
trace("addDateHeader: " + name + ": " + date);
}
@Override
public void addHeader(String name, String value)
{
trace("addHeader: " + name + ": " + value);
}
@Override
public void addIntHeader(String name, int value)
{
trace("addIntHeader: " + name + ": " + value);
}
@Override
public void sendRedirect(String location) throws IOException
{
trace("sendRedirect: " + location);
}
@Override
public void setDateHeader(String name, long date)
{
trace("setDateHeader: " + name + ": " + date);
}
@Override
public void setHeader(String name, String value)
{
trace("setHeader: " + name + ": " + value);
}
@Override
public void setIntHeader(String name, int value)
{
trace("setIntHeader: " + name + ": " + value);
}
@Override
public void setStatus(int sc)
{
trace("setStatus: " + sc);
}
@Override
@Deprecated
public void setStatus(int sc, String sm)
{
trace("setStatus: " + sc + "; " + sm);
}
@Override
public void reset()
{
trace("reset");
}
@Override
public void setCharacterEncoding(String charset)
{
trace("setCharacterEncoding " + charset);
}
@Override
public void setContentLength(int len)
{
trace("setContentLength: " + len);
}
@Override
public void setContentLengthLong(long len)
{
trace("setContentLengthLong: " + len);
}
@Override
public void setContentType(String type)
{
trace("setContentType: " + type);
}
private void trace(String msg)
{
msg = "IncludeResponse." + msg;
Response.trace(0, msg.getBytes(UTF_8));
}
}

View File

@@ -0,0 +1,7 @@
package nginx.unit;
public interface InitParams {
public boolean setInitParameter(String name, String value);
public String getInitParameter(String name);
}

View File

@@ -0,0 +1,90 @@
package nginx.unit;
import java.io.IOException;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
public class InputStream extends ServletInputStream {
private long req_info_ptr;
public InputStream(long ptr)
{
req_info_ptr = ptr;
}
@Override
public int readLine(byte[] b, int off, int len) throws IOException {
if (len <= 0) {
return 0;
}
return readLine(req_info_ptr, b, off, len);
}
private static native int readLine(long req_info_ptr, byte[] b, int off, int len);
@Override
public boolean isFinished()
{
return isFinished(req_info_ptr);
}
private static native boolean isFinished(long req_info_ptr);
@Override
public boolean isReady()
{
return true;
}
@Override
public void setReadListener(ReadListener listener)
{
}
@Override
public int read() throws IOException
{
return read(req_info_ptr);
}
private static native int read(long req_info_ptr);
@Override
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
return read(req_info_ptr, b, off, len);
}
private static native int read(long req_info_ptr, byte b[], int off, int len);
@Override
public long skip(long n) throws IOException {
return skip(req_info_ptr, n);
}
private static native long skip(long req_info_ptr, long n);
@Override
public int available() throws IOException {
return available(req_info_ptr);
}
private static native int available(long req_info_ptr);
}

View File

@@ -0,0 +1,169 @@
package nginx.unit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.servlet.descriptor.JspPropertyGroupDescriptor;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class JspPropertyGroup implements JspPropertyGroupDescriptor
{
private final List<String> url_patterns_ = new ArrayList<>();
private String el_ignored_ = null;
private String page_encoding_ = null;
private String scripting_invalid_ = null;
private String is_xml_ = null;
private final List<String> include_preludes_ = new ArrayList<>();
private final List<String> include_codas_ = new ArrayList<>();
private String deffered_syntax_allowed_as_literal_ = null;
private String trim_directive_whitespaces_ = null;
private String default_content_type_ = null;
private String buffer_ = null;
private String error_on_undeclared_namespace_ = null;
public JspPropertyGroup(NodeList nodes)
{
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
String tag_name = node.getNodeName();
if (tag_name.equals("url-pattern")) {
url_patterns_.add(node.getTextContent().trim());
continue;
}
if (tag_name.equals("el-ignored")) {
el_ignored_ = node.getTextContent().trim();
continue;
}
if (tag_name.equals("page-encoding")) {
page_encoding_ = node.getTextContent().trim();
continue;
}
if (tag_name.equals("scripting-invalid")) {
scripting_invalid_ = node.getTextContent().trim();
continue;
}
if (tag_name.equals("is-xml")) {
is_xml_ = node.getTextContent().trim();
continue;
}
if (tag_name.equals("include-prelude")) {
include_preludes_.add(node.getTextContent().trim());
continue;
}
if (tag_name.equals("include-coda")) {
include_codas_.add(node.getTextContent().trim());
continue;
}
if (tag_name.equals("deferred-syntax-allowed-as-literal")) {
deffered_syntax_allowed_as_literal_ = node.getTextContent().trim();
continue;
}
if (tag_name.equals("trim-directive-whitespaces")) {
trim_directive_whitespaces_ = node.getTextContent().trim();
continue;
}
if (tag_name.equals("default-content-type")) {
default_content_type_ = node.getTextContent().trim();
continue;
}
if (tag_name.equals("buffer")) {
buffer_ = node.getTextContent().trim();
continue;
}
if (tag_name.equals("error-on-undeclared-namespace")) {
error_on_undeclared_namespace_ = node.getTextContent().trim();
continue;
}
}
}
@Override
public Collection<String> getUrlPatterns()
{
return new ArrayList<>(url_patterns_);
}
@Override
public String getElIgnored()
{
return el_ignored_;
}
@Override
public String getPageEncoding()
{
return page_encoding_;
}
@Override
public String getScriptingInvalid()
{
return scripting_invalid_;
}
@Override
public String getIsXml()
{
return is_xml_;
}
@Override
public Collection<String> getIncludePreludes()
{
return new ArrayList<>(include_preludes_);
}
@Override
public Collection<String> getIncludeCodas()
{
return new ArrayList<>(include_codas_);
}
@Override
public String getDeferredSyntaxAllowedAsLiteral()
{
return deffered_syntax_allowed_as_literal_;
}
@Override
public String getTrimDirectiveWhitespaces()
{
return trim_directive_whitespaces_;
}
@Override
public String getDefaultContentType()
{
return default_content_type_;
}
@Override
public String getBuffer()
{
return buffer_;
}
@Override
public String getErrorOnUndeclaredNamespace()
{
return error_on_undeclared_namespace_;
}
}

View File

@@ -0,0 +1,68 @@
package nginx.unit;
import java.io.IOException;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
public class OutputStream extends ServletOutputStream {
private long req_info_ptr;
public OutputStream(long ptr) {
req_info_ptr = ptr;
}
@Override
public void write(int b) throws IOException
{
write(req_info_ptr, b);
}
private static native void write(long req_info_ptr, int b);
@Override
public void write(byte b[], int off, int len) throws IOException
{
if (b == null) {
throw new NullPointerException();
} else if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
write(req_info_ptr, b, off, len);
}
private static native void write(long req_info_ptr, byte b[], int off, int len);
@Override
public void flush()
{
flush(req_info_ptr);
}
private static native void flush(long req_info_ptr);
@Override
public void close()
{
close(req_info_ptr);
}
private static native void close(long req_info_ptr);
@Override
public boolean isReady()
{
return true;
}
@Override
public void setWriteListener(WriteListener listener)
{
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,40 @@
package nginx.unit;
import java.util.List;
import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
public class RequestAttrProxy implements ServletRequestAttributeListener
{
private final List<ServletRequestAttributeListener> listeners_;
public RequestAttrProxy(List<ServletRequestAttributeListener> listeners)
{
listeners_ = listeners;
}
@Override
public void attributeAdded(ServletRequestAttributeEvent srae)
{
for (ServletRequestAttributeListener l : listeners_) {
l.attributeAdded(srae);
}
}
@Override
public void attributeReplaced(ServletRequestAttributeEvent srae)
{
for (ServletRequestAttributeListener l : listeners_) {
l.attributeReplaced(srae);
}
}
@Override
public void attributeRemoved(ServletRequestAttributeEvent srae)
{
for (ServletRequestAttributeListener l : listeners_) {
l.attributeRemoved(srae);
}
}
}

View File

@@ -0,0 +1,817 @@
package nginx.unit;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.IllegalArgumentException;
import java.lang.String;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.Locale;
import java.util.TimeZone;
import java.util.Vector;
import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.util.StringUtil;
public class Response implements HttpServletResponse {
private long req_info_ptr;
private static final String defaultCharacterEncoding = "iso-8859-1";
private String characterEncoding = defaultCharacterEncoding;
private String contentType = null;
private String contentTypeHeader = null;
private static final Charset ISO_8859_1 = StandardCharsets.ISO_8859_1;
private static final Charset UTF_8 = StandardCharsets.UTF_8;
private static final String CONTENT_TYPE = "Content-Type";
private static final byte[] SET_COOKIE_BYTES = "Set-Cookie".getBytes(ISO_8859_1);
private static final byte[] EXPIRES_BYTES = "Expires".getBytes(ISO_8859_1);
/**
* The only date format permitted when generating HTTP headers.
*/
public static final String RFC1123_DATE =
"EEE, dd MMM yyyy HH:mm:ss zzz";
private static final SimpleDateFormat format =
new SimpleDateFormat(RFC1123_DATE, Locale.US);
private static final String ZERO_DATE_STRING = dateToString(0);
private static final byte[] ZERO_DATE_BYTES = ZERO_DATE_STRING.getBytes(ISO_8859_1);
/**
* If this string is found within the comment of a cookie added with {@link #addCookie(Cookie)}, then the cookie
* will be set as HTTP ONLY.
*/
public final static String HTTP_ONLY_COMMENT = "__HTTP_ONLY__";
private OutputStream outputStream = null;
private PrintWriter writer = null;
public Response(long ptr) {
req_info_ptr = ptr;
}
/**
* Format a set cookie value by RFC6265
*
* @param name the name
* @param value the value
* @param domain the domain
* @param path the path
* @param maxAge the maximum age
* @param isSecure true if secure cookie
* @param isHttpOnly true if for http only
*/
public void addSetRFC6265Cookie(
final String name,
final String value,
final String domain,
final String path,
final long maxAge,
final boolean isSecure,
final boolean isHttpOnly)
{
// Check arguments
if (name == null || name.length() == 0) {
throw new IllegalArgumentException("Bad cookie name");
}
// Name is checked for legality by servlet spec, but can also be passed directly so check again for quoting
// Per RFC6265, Cookie.name follows RFC2616 Section 2.2 token rules
//Syntax.requireValidRFC2616Token(name, "RFC6265 Cookie name");
// Ensure that Per RFC6265, Cookie.value follows syntax rules
//Syntax.requireValidRFC6265CookieValue(value);
// Format value and params
StringBuilder buf = new StringBuilder();
buf.append(name).append('=').append(value == null ? "" : value);
// Append path
if (path != null && path.length() > 0) {
buf.append(";Path=").append(path);
}
// Append domain
if (domain != null && domain.length() > 0) {
buf.append(";Domain=").append(domain);
}
// Handle max-age and/or expires
if (maxAge >= 0) {
// Always use expires
// This is required as some browser (M$ this means you!) don't handle max-age even with v1 cookies
buf.append(";Expires=");
if (maxAge == 0)
buf.append(ZERO_DATE_STRING);
else
buf.append(dateToString(System.currentTimeMillis() + 1000L * maxAge));
buf.append(";Max-Age=");
buf.append(maxAge);
}
// add the other fields
if (isSecure)
buf.append(";Secure");
if (isHttpOnly)
buf.append(";HttpOnly");
// add the set cookie
addHeader(req_info_ptr, SET_COOKIE_BYTES,
buf.toString().getBytes(ISO_8859_1));
// Expire responses with set-cookie headers so they do not get cached.
setHeader(req_info_ptr, EXPIRES_BYTES, ZERO_DATE_BYTES);
}
@Override
public void addCookie(Cookie cookie)
{
trace("addCookie: " + cookie.getName() + "=" + cookie.getValue());
if (StringUtil.isBlank(cookie.getName())) {
throw new IllegalArgumentException("Cookie.name cannot be blank/null");
}
if (isCommitted()) {
return;
}
addCookie_(cookie);
}
private void addCookie_(Cookie cookie)
{
String comment = cookie.getComment();
boolean httpOnly = false;
if (comment != null && comment.contains(HTTP_ONLY_COMMENT)) {
httpOnly = true;
}
addSetRFC6265Cookie(cookie.getName(),
cookie.getValue(),
cookie.getDomain(),
cookie.getPath(),
cookie.getMaxAge(),
cookie.getSecure(),
httpOnly || cookie.isHttpOnly());
}
public void addSessionIdCookie(Cookie cookie)
{
trace("addSessionIdCookie: " + cookie.getName() + "=" + cookie.getValue());
if (isCommitted()) {
/*
9.3 The Include Method
... any call to HttpServletRequest.getSession() or
HttpServletRequest.getSession(boolean) that would require
adding a Cookie response header must throw an
IllegalStateException if the response has been committed.
*/
throw new IllegalStateException("Response already sent");
}
addCookie_(cookie);
}
@Override
public void addDateHeader(String name, long date)
{
trace("addDateHeader: " + name + ": " + date);
if (isCommitted()) {
return;
}
String value = dateToString(date);
addHeader(req_info_ptr, name.getBytes(ISO_8859_1),
value.getBytes(ISO_8859_1));
}
private static String dateToString(long date)
{
Date dateValue = new Date(date);
format.setTimeZone(TimeZone.getTimeZone("GMT"));
return format.format(dateValue);
}
@Override
public void addHeader(String name, String value)
{
trace("addHeader: " + name + ": " + value);
if (value == null) {
return;
}
if (isCommitted()) {
return;
}
if (CONTENT_TYPE.equalsIgnoreCase(name)) {
setContentType(value);
return;
}
addHeader(req_info_ptr, name.getBytes(ISO_8859_1),
value.getBytes(ISO_8859_1));
}
private static native void addHeader(long req_info_ptr, byte[] name, byte[] value);
@Override
public void addIntHeader(String name, int value)
{
trace("addIntHeader: " + name + ": " + value);
if (isCommitted()) {
return;
}
addIntHeader(req_info_ptr, name.getBytes(ISO_8859_1), value);
}
private static native void addIntHeader(long req_info_ptr, byte[] name, int value);
@Override
public boolean containsHeader(String name)
{
trace("containsHeader: " + name);
return containsHeader(req_info_ptr, name.getBytes(ISO_8859_1));
}
private static native boolean containsHeader(long req_info_ptr, byte[] name);
@Override
@Deprecated
public String encodeRedirectUrl(String url)
{
return encodeRedirectURL(url);
}
@Override
public String encodeRedirectURL(String url)
{
log("encodeRedirectURL: " + url);
return url;
}
@Override
@Deprecated
public String encodeUrl(String url)
{
return encodeURL(url);
}
@Override
public String encodeURL(String url)
{
log("encodeURL: " + url);
return url;
}
@Override
public String getHeader(String name)
{
trace("getHeader: " + name);
return getHeader(req_info_ptr, name.getBytes(ISO_8859_1));
}
private static native String getHeader(long req_info_ptr, byte[] name);
@Override
public Collection<String> getHeaderNames()
{
trace("getHeaderNames");
Enumeration<String> e = getHeaderNames(req_info_ptr);
if (e == null) {
return Collections.emptyList();
}
return Collections.list(e);
}
private static native Enumeration<String> getHeaderNames(long req_info_ptr);
@Override
public Collection<String> getHeaders(String name)
{
trace("getHeaders: " + name);
Enumeration<String> e = getHeaders(req_info_ptr, name.getBytes(ISO_8859_1));
if (e == null) {
return Collections.emptyList();
}
return Collections.list(e);
}
private static native Enumeration<String> getHeaders(long req_info_ptr, byte[] name);
@Override
public int getStatus()
{
trace("getStatus");
return getStatus(req_info_ptr);
}
private static native int getStatus(long req_info_ptr);
@Override
public void sendError(int sc) throws IOException
{
sendError(sc, null);
}
@Override
public void sendError(int sc, String msg) throws IOException
{
trace("sendError: " + sc + ", " + msg);
if (isCommitted()) {
throw new IllegalStateException("Response already sent");
}
setStatus(sc);
Request request = getRequest(req_info_ptr);
// If we are allowed to have a body, then produce the error page.
if (sc != SC_NO_CONTENT && sc != SC_NOT_MODIFIED &&
sc != SC_PARTIAL_CONTENT && sc >= SC_OK)
{
request.setAttribute_(RequestDispatcher.ERROR_STATUS_CODE, sc);
request.setAttribute_(RequestDispatcher.ERROR_MESSAGE, msg);
request.setAttribute_(RequestDispatcher.ERROR_REQUEST_URI,
request.getRequestURI());
/*
request.setAttribute_(RequestDispatcher.ERROR_SERVLET_NAME,
request.getServletName());
*/
}
/*
Avoid commit and give chance for error handlers.
if (!request.isAsyncStarted()) {
commit();
}
*/
}
private static native Request getRequest(long req_info_ptr);
private void commit()
{
if (writer != null) {
writer.close();
} else if (outputStream != null) {
outputStream.close();
} else {
commit(req_info_ptr);
}
}
private static native void commit(long req_info_ptr);
@Override
public void sendRedirect(String location) throws IOException
{
trace("sendRedirect: " + location);
if (isCommitted()) {
return;
}
try {
URI uri = new URI(location);
if (!uri.isAbsolute()) {
URI req_uri = new URI(getRequest(req_info_ptr).getRequestURL().toString());
uri = req_uri.resolve(uri);
location = uri.toString();
}
} catch (URISyntaxException e) {
log("sendRedirect: failed to send redirect: " + e);
return;
}
sendRedirect(req_info_ptr, location.getBytes(ISO_8859_1));
}
private static native void sendRedirect(long req_info_ptr, byte[] location);
@Override
public void setDateHeader(String name, long date)
{
trace("setDateHeader: " + name + ": " + date);
if (isCommitted()) {
return;
}
String value = dateToString(date);
setHeader(req_info_ptr, name.getBytes(ISO_8859_1),
value.getBytes(ISO_8859_1));
}
@Override
public void setHeader(String name, String value)
{
trace("setHeader: " + name + ": " + value);
if (isCommitted()) {
return;
}
if (CONTENT_TYPE.equalsIgnoreCase(name)) {
setContentType(value);
return;
}
/*
* When value is null container behaviour is undefined.
* - Tomcat ignores setHeader call;
* - Jetty & Resin acts as removeHeader;
*/
if (value == null) {
removeHeader(req_info_ptr, name.getBytes(ISO_8859_1));
return;
}
setHeader(req_info_ptr, name.getBytes(ISO_8859_1),
value.getBytes(ISO_8859_1));
}
private static native void setHeader(long req_info_ptr, byte[] name, byte[] value);
private static native void removeHeader(long req_info_ptr, byte[] name);
@Override
public void setIntHeader(String name, int value)
{
trace("setIntHeader: " + name + ": " + value);
if (isCommitted()) {
return;
}
setIntHeader(req_info_ptr, name.getBytes(ISO_8859_1), value);
}
private static native void setIntHeader(long req_info_ptr, byte[] name, int value);
@Override
public void setStatus(int sc)
{
trace("setStatus: " + sc);
if (isCommitted()) {
return;
}
setStatus(req_info_ptr, sc);
}
private static native void setStatus(long req_info_ptr, int sc);
@Override
@Deprecated
public void setStatus(int sc, String sm)
{
trace("setStatus: " + sc + "; " + sm);
if (isCommitted()) {
return;
}
setStatus(req_info_ptr, sc);
}
@Override
public void flushBuffer() throws IOException
{
trace("flushBuffer");
if (writer != null) {
writer.flush();
}
if (outputStream != null) {
outputStream.flush();
}
}
@Override
public int getBufferSize()
{
trace("getBufferSize");
return getBufferSize(req_info_ptr);
}
public static native int getBufferSize(long req_info_ptr);
@Override
public String getCharacterEncoding()
{
trace("getCharacterEncoding");
return characterEncoding;
}
@Override
public String getContentType()
{
/* In JIRA decorator get content type called after commit. */
String res = contentTypeHeader;
trace("getContentType: " + res);
return res;
}
private static native String getContentType(long req_info_ptr);
@Override
public Locale getLocale()
{
log("getLocale");
return null;
}
@Override
public ServletOutputStream getOutputStream() throws IOException
{
trace("getOutputStream");
if (writer != null) {
throw new IllegalStateException("Writer already created");
}
if (outputStream == null) {
outputStream = new OutputStream(req_info_ptr);
}
return outputStream;
}
@Override
public PrintWriter getWriter() throws IOException
{
trace("getWriter ( characterEncoding = '" + characterEncoding + "' )");
if (outputStream != null) {
throw new IllegalStateException("OutputStream already created");
}
if (writer == null) {
ServletOutputStream stream = new OutputStream(req_info_ptr);
writer = new PrintWriter(
new OutputStreamWriter(stream, Charset.forName(characterEncoding)),
false);
}
return writer;
}
@Override
public boolean isCommitted()
{
trace("isCommitted");
return isCommitted(req_info_ptr);
}
public static native boolean isCommitted(long req_info_ptr);
@Override
public void reset()
{
trace("reset");
if (isCommitted()) {
return;
}
reset(req_info_ptr);
writer = null;
outputStream = null;
}
public static native void reset(long req_info_ptr);
@Override
public void resetBuffer()
{
trace("resetBuffer");
resetBuffer(req_info_ptr);
writer = null;
outputStream = null;
}
public static native void resetBuffer(long req_info_ptr);
@Override
public void setBufferSize(int size)
{
trace("setBufferSize: " + size);
setBufferSize(req_info_ptr, size);
}
public static native void setBufferSize(long req_info_ptr, int size);
@Override
public void setCharacterEncoding(String charset)
{
trace("setCharacterEncoding " + charset);
if (isCommitted()) {
return;
}
if (charset == null) {
if (writer != null
&& !characterEncoding.equalsIgnoreCase(defaultCharacterEncoding))
{
/* TODO throw */
return;
}
characterEncoding = defaultCharacterEncoding;
} else {
if (writer != null
&& !characterEncoding.equalsIgnoreCase(charset))
{
/* TODO throw */
return;
}
characterEncoding = charset;
}
if (contentType != null) {
String type = contentType + ";charset=" + characterEncoding;
contentTypeHeader = type;
setContentType(req_info_ptr, type.getBytes(ISO_8859_1));
}
}
@Override
public void setContentLength(int len)
{
trace("setContentLength: " + len);
if (isCommitted()) {
return;
}
setContentLength(req_info_ptr, len);
}
@Override
public void setContentLengthLong(long len)
{
trace("setContentLengthLong: " + len);
if (isCommitted()) {
return;
}
setContentLength(req_info_ptr, len);
}
private static native void setContentLength(long req_info_ptr, long len);
@Override
public void setContentType(String type)
{
trace("setContentType: " + type);
if (isCommitted()) {
return;
}
if (type == null) {
removeContentType(req_info_ptr);
contentType = null;
contentTypeHeader = null;
return;
}
String charset = MimeTypes.getCharsetFromContentType(type);
String ctype = MimeTypes.getContentTypeWithoutCharset(type);
if (writer != null
&& charset != null
&& !characterEncoding.equalsIgnoreCase(charset))
{
/* To late to change character encoding */
charset = characterEncoding;
type = ctype + ";charset=" + characterEncoding;
}
if (charset == null) {
type = type + ";charset=" + characterEncoding;
} else {
characterEncoding = charset;
}
contentType = ctype;
contentTypeHeader = type;
setContentType(req_info_ptr, type.getBytes(ISO_8859_1));
}
private static native void setContentType(long req_info_ptr, byte[] type);
private static native void removeContentType(long req_info_ptr);
@Override
public void setLocale(Locale loc)
{
log("setLocale: " + loc);
}
private void log(String msg)
{
msg = "Response." + msg;
log(req_info_ptr, msg.getBytes(UTF_8));
}
public static native void log(long req_info_ptr, byte[] msg);
private void trace(String msg)
{
msg = "Response." + msg;
trace(req_info_ptr, msg.getBytes(UTF_8));
}
public static native void trace(long req_info_ptr, byte[] msg);
}

View File

@@ -0,0 +1,251 @@
package nginx.unit;
import java.io.Serializable;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
/**
* @author Andrey Kazankov
*/
public class Session implements HttpSession, Serializable
{
private final Map<String, Object> attributes = new HashMap<>();
private final long creation_time = new Date().getTime();
private long last_access_time = creation_time;
private long access_time = creation_time;
private int max_inactive_interval;
private String id;
private final Context context;
private boolean is_new = true;
private final HttpSessionAttributeListener attr_listener;
public Session(Context context, String id,
HttpSessionAttributeListener al, int max_inactive_interval)
{
this.id = id;
this.context = context;
attr_listener = al;
this.max_inactive_interval = max_inactive_interval;
}
public void setId(String id)
{
this.id = id;
}
@Override
public long getCreationTime()
{
return creation_time;
}
@Override
public String getId()
{
return id;
}
@Override
public long getLastAccessedTime()
{
return last_access_time;
}
@Override
public ServletContext getServletContext()
{
return context;
}
@Override
public void setMaxInactiveInterval(int i)
{
max_inactive_interval = i;
}
@Override
public int getMaxInactiveInterval()
{
return max_inactive_interval;
}
@Deprecated
@Override
public javax.servlet.http.HttpSessionContext getSessionContext()
{
return null;
}
@Override
public Object getAttribute(String s)
{
synchronized (attributes) {
return attributes.get(s);
}
}
@Deprecated
@Override
public Object getValue(String s)
{
return getAttribute(s);
}
@Override
public Enumeration<String> getAttributeNames()
{
synchronized (attributes) {
return Collections.enumeration(attributes.keySet());
}
}
@Deprecated
@Override
public String[] getValueNames()
{
synchronized (attributes) {
return attributes.keySet().toArray(new String[attributes.keySet().size()]);
}
}
@Override
public void setAttribute(String s, Object o)
{
Object old;
if (o != null && o instanceof HttpSessionBindingListener) {
HttpSessionBindingListener l = (HttpSessionBindingListener) o;
HttpSessionBindingEvent e = new HttpSessionBindingEvent(this, s);
l.valueBound(e);
}
synchronized (attributes) {
if (o != null) {
old = attributes.put(s, o);
} else {
old = attributes.remove(s);
}
}
if (old != null && old instanceof HttpSessionBindingListener) {
HttpSessionBindingListener l = (HttpSessionBindingListener) old;
HttpSessionBindingEvent e = new HttpSessionBindingEvent(this, s);
l.valueUnbound(e);
}
if (attr_listener == null) {
return;
}
if (o == null) {
if (old != null) {
HttpSessionBindingEvent e = new HttpSessionBindingEvent(this, s, old);
attr_listener.attributeRemoved(e);
}
return;
}
if (old != null) {
HttpSessionBindingEvent e = new HttpSessionBindingEvent(this, s, old);
attr_listener.attributeReplaced(e);
} else {
HttpSessionBindingEvent e = new HttpSessionBindingEvent(this, s, o);
attr_listener.attributeAdded(e);
}
}
@Deprecated
@Override
public void putValue(String s, Object o)
{
setAttribute(s,o);
}
@Override
public void removeAttribute(String s)
{
Object o;
synchronized (attributes) {
o = attributes.remove(s);
}
if (o != null && o instanceof HttpSessionBindingListener) {
HttpSessionBindingListener l = (HttpSessionBindingListener) o;
HttpSessionBindingEvent e = new HttpSessionBindingEvent(this, s);
l.valueUnbound(e);
}
if (attr_listener == null || o == null) {
return;
}
HttpSessionBindingEvent e = new HttpSessionBindingEvent(this, s, o);
attr_listener.attributeRemoved(e);
}
@Deprecated
@Override
public void removeValue(String s)
{
removeAttribute(s);
}
@Override
public void invalidate()
{
context.invalidateSession(this);
unboundAttributes();
}
private void unboundAttributes()
{
for (Map.Entry<String, Object> a : attributes.entrySet()) {
Object o = a.getValue();
if (o != null && o instanceof HttpSessionBindingListener) {
HttpSessionBindingListener l = (HttpSessionBindingListener) o;
HttpSessionBindingEvent e = new HttpSessionBindingEvent(this, a.getKey());
l.valueUnbound(e);
}
}
attributes.clear();
}
@Override
public boolean isNew()
{
return is_new;
}
public void accessed() {
synchronized (this) {
is_new = false;
last_access_time = access_time;
access_time = new Date().getTime();
}
}
public boolean checkTimeOut()
{
return (max_inactive_interval > 0) &&
(access_time - last_access_time > max_inactive_interval * 1000);
}
}

View File

@@ -0,0 +1,40 @@
package nginx.unit;
import java.util.List;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
public class SessionAttrProxy implements HttpSessionAttributeListener
{
private final List<HttpSessionAttributeListener> listeners_;
public SessionAttrProxy(List<HttpSessionAttributeListener> listeners)
{
listeners_ = listeners;
}
@Override
public void attributeAdded(HttpSessionBindingEvent event)
{
for (HttpSessionAttributeListener l : listeners_) {
l.attributeAdded(event);
}
}
@Override
public void attributeRemoved(HttpSessionBindingEvent event)
{
for (HttpSessionAttributeListener l : listeners_) {
l.attributeRemoved(event);
}
}
@Override
public void attributeReplaced(HttpSessionBindingEvent event)
{
for (HttpSessionAttributeListener l : listeners_) {
l.attributeReplaced(event);
}
}
}

View File

@@ -0,0 +1,44 @@
package nginx.unit;
import javax.servlet.descriptor.TaglibDescriptor;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class Taglib implements TaglibDescriptor
{
private String uri_ = null;
private String location_ = null;
public Taglib(NodeList nodes)
{
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
String tag_name = node.getNodeName();
if (tag_name.equals("taglib-uri")) {
uri_ = node.getTextContent().trim();
continue;
}
if (tag_name.equals("taglib-location")) {
location_ = node.getTextContent().trim();
continue;
}
}
}
@Override
public String getTaglibURI()
{
return uri_;
}
@Override
public String getTaglibLocation()
{
return location_;
}
}

View File

@@ -0,0 +1,110 @@
package nginx.unit;
import javax.servlet.SessionCookieConfig;
/*
<session-config>
<session-timeout>60</session-timeout>
<cookie-config></cookie-config>
<tracking-mode></tracking-mode>
</session-config>
*/
public class UnitSessionCookieConfig implements SessionCookieConfig {
private static final String default_name = "JSESSIONID";
private String name = default_name;
private String domain;
private String path;
private String comment;
private boolean httpOnly = true;
private boolean secure = false;
private int maxAge = -1;
@Override
public void setName(String name)
{
this.name = name;
}
@Override
public String getName()
{
return name;
}
@Override
public void setDomain(String domain)
{
this.domain = domain;
}
@Override
public String getDomain()
{
return domain;
}
@Override
public void setPath(String path)
{
this.path = path;
}
@Override
public String getPath()
{
return path;
}
@Override
public void setComment(String comment)
{
this.comment = comment;
}
@Override
public String getComment()
{
return comment;
}
@Override
public void setHttpOnly(boolean httpOnly)
{
this.httpOnly = httpOnly;
}
@Override
public boolean isHttpOnly()
{
return httpOnly;
}
@Override
public void setSecure(boolean secure)
{
this.secure = secure;
}
@Override
public boolean isSecure()
{
return secure;
}
@Override
public void setMaxAge(int maxAge)
{
this.maxAge = maxAge;
}
@Override
public int getMaxAge()
{
return maxAge;
}
}

175
src/java/nxt_jni.c Normal file
View File

@@ -0,0 +1,175 @@
/*
* Copyright (C) NGINX, Inc.
*/
#include <nxt_auto_config.h>
#include <jni.h>
#include <nxt_unit.h>
#include <nxt_unit_field.h>
#include "nxt_jni.h"
static jclass nxt_java_NoSuchElementException_class;
static jclass nxt_java_IOException_class;
static jclass nxt_java_IllegalStateException_class;
static jclass nxt_java_File_class;
static jmethodID nxt_java_File_ctor;
static inline char nxt_java_lowcase(char c);
int
nxt_java_jni_init(JNIEnv *env)
{
jclass cls;
cls = (*env)->FindClass(env, "java/util/NoSuchElementException");
if (cls == NULL) {
return NXT_UNIT_ERROR;
}
nxt_java_NoSuchElementException_class = (*env)->NewGlobalRef(env, cls);
(*env)->DeleteLocalRef(env, cls);
cls = (*env)->FindClass(env, "java/io/IOException");
if (cls == NULL) {
(*env)->DeleteGlobalRef(env, nxt_java_NoSuchElementException_class);
return NXT_UNIT_ERROR;
}
nxt_java_IOException_class = (*env)->NewGlobalRef(env, cls);
(*env)->DeleteLocalRef(env, cls);
cls = (*env)->FindClass(env, "java/lang/IllegalStateException");
if (cls == NULL) {
(*env)->DeleteGlobalRef(env, nxt_java_NoSuchElementException_class);
(*env)->DeleteGlobalRef(env, nxt_java_IOException_class);
return NXT_UNIT_ERROR;
}
nxt_java_IllegalStateException_class = (*env)->NewGlobalRef(env, cls);
(*env)->DeleteLocalRef(env, cls);
cls = (*env)->FindClass(env, "java/io/File");
if (cls == NULL) {
(*env)->DeleteGlobalRef(env, nxt_java_NoSuchElementException_class);
(*env)->DeleteGlobalRef(env, nxt_java_IOException_class);
(*env)->DeleteGlobalRef(env, nxt_java_IllegalStateException_class);
return NXT_UNIT_ERROR;
}
nxt_java_File_class = (*env)->NewGlobalRef(env, cls);
(*env)->DeleteLocalRef(env, cls);
nxt_java_File_ctor = (*env)->GetMethodID(env, nxt_java_File_class, "<init>",
"(Ljava/lang/String;)V");
if (nxt_java_File_ctor == NULL) {
(*env)->DeleteGlobalRef(env, nxt_java_NoSuchElementException_class);
(*env)->DeleteGlobalRef(env, nxt_java_IOException_class);
(*env)->DeleteGlobalRef(env, nxt_java_IllegalStateException_class);
(*env)->DeleteGlobalRef(env, nxt_java_File_class);
return NXT_UNIT_ERROR;
}
return NXT_UNIT_OK;
}
void
nxt_java_throw_NoSuchElementException(JNIEnv *env, const char *msg)
{
(*env)->ThrowNew(env, nxt_java_NoSuchElementException_class, msg);
}
void
nxt_java_throw_IOException(JNIEnv *env, const char *msg)
{
(*env)->ThrowNew(env, nxt_java_IOException_class, msg);
}
void
nxt_java_throw_IllegalStateException(JNIEnv *env, const char *msg)
{
(*env)->ThrowNew(env, nxt_java_IllegalStateException_class, msg);
}
nxt_unit_field_t *
nxt_java_findHeader(nxt_unit_field_t *f, nxt_unit_field_t *end,
const char *name, uint8_t name_len)
{
const char *field_name;
for (/* void */ ; f < end; f++) {
if (f->skip != 0 || f->name_length != name_len) {
continue;
}
field_name = nxt_unit_sptr_get(&f->name);
if (nxt_java_strcaseeq(name, field_name, name_len)) {
return f;
}
}
return NULL;
}
int
nxt_java_strcaseeq(const char *str1, const char *str2, int len)
{
char c1, c2;
const char *end1;
end1 = str1 + len;
while (str1 < end1) {
c1 = nxt_java_lowcase(*str1++);
c2 = nxt_java_lowcase(*str2++);
if (c1 != c2) {
return 0;
}
}
return 1;
}
static inline char
nxt_java_lowcase(char c)
{
return (c >= 'A' && c <= 'Z') ? c | 0x20 : c;
}
jstring
nxt_java_newString(JNIEnv *env, char *str, uint32_t len)
{
char tmp;
jstring res;
tmp = str[len];
if (tmp != '\0') {
str[len] = '\0';
}
res = (*env)->NewStringUTF(env, str);
if (tmp != '\0') {
str[len] = tmp;
}
return res;
}

55
src/java/nxt_jni.h Normal file
View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) NGINX, Inc.
*/
#ifndef _NXT_JAVA_JNI_H_INCLUDED_
#define _NXT_JAVA_JNI_H_INCLUDED_
#include <jni.h>
#include <nxt_unit_typedefs.h>
int nxt_java_jni_init(JNIEnv *env);
void nxt_java_throw_NoSuchElementException(JNIEnv *env, const char *msg);
void nxt_java_throw_IOException(JNIEnv *env, const char *msg);
void nxt_java_throw_IllegalStateException(JNIEnv *env, const char *msg);
nxt_unit_field_t *nxt_java_findHeader(nxt_unit_field_t *f, nxt_unit_field_t *e,
const char *name, uint8_t name_len);
int nxt_java_strcaseeq(const char *str1, const char *str2, int len);
jstring nxt_java_newString(JNIEnv *env, char *str, uint32_t len);
typedef struct {
uint32_t header_size;
uint32_t buf_size;
jobject jreq;
jobject jresp;
nxt_unit_buf_t *first;
nxt_unit_buf_t *buf;
} nxt_java_request_data_t;
static inline jlong
nxt_ptr2jlong(void *ptr)
{
return (jlong) (intptr_t) ptr;
}
static inline void *
nxt_jlong2ptr(jlong l)
{
return (void *) (intptr_t) l;
}
#endif /* _NXT_JAVA_JNI_H_INCLUDED_ */

164
src/java/nxt_jni_Context.c Normal file
View File

@@ -0,0 +1,164 @@
/*
* Copyright (C) NGINX, Inc.
*/
#include <nxt_auto_config.h>
#include <nxt_unit.h>
#include <jni.h>
#include "nxt_jni.h"
#include "nxt_jni_Context.h"
#include "nxt_jni_URLClassLoader.h"
static jclass nxt_java_Context_class;
static jmethodID nxt_java_Context_start;
static jmethodID nxt_java_Context_service;
static jmethodID nxt_java_Context_stop;
static void JNICALL nxt_java_Context_log(JNIEnv *env, jclass cls,
jlong ctx_ptr, jstring msg, jint msg_len);
static void JNICALL nxt_java_Context_trace(JNIEnv *env, jclass cls,
jlong ctx_ptr, jstring msg, jint msg_len);
int
nxt_java_initContext(JNIEnv *env, jobject cl)
{
int res;
jclass cls;
cls = nxt_java_loadClass(env, cl, "nginx.unit.Context");
if (cls == NULL) {
nxt_unit_warn(NULL, "nginx.unit.Context not found");
return NXT_UNIT_ERROR;
}
nxt_java_Context_class = (*env)->NewGlobalRef(env, cls);
(*env)->DeleteLocalRef(env, cls);
cls = nxt_java_Context_class;
nxt_java_Context_start = (*env)->GetStaticMethodID(env, cls, "start",
"(Ljava/lang/String;[Ljava/net/URL;)Lnginx/unit/Context;");
if (nxt_java_Context_start == NULL) {
nxt_unit_warn(NULL, "nginx.unit.Context.start() not found");
goto failed;
}
nxt_java_Context_service = (*env)->GetMethodID(env, cls, "service",
"(Lnginx/unit/Request;Lnginx/unit/Response;)V");
if (nxt_java_Context_service == NULL) {
nxt_unit_warn(NULL, "nginx.unit.Context.service() not found");
goto failed;
}
nxt_java_Context_stop = (*env)->GetMethodID(env, cls, "stop", "()V");
if (nxt_java_Context_service == NULL) {
nxt_unit_warn(NULL, "nginx.unit.Context.stop() not found");
goto failed;
}
JNINativeMethod context_methods[] = {
{ (char *) "log",
(char *) "(JLjava/lang/String;I)V",
nxt_java_Context_log },
{ (char *) "trace",
(char *) "(JLjava/lang/String;I)V",
nxt_java_Context_trace },
};
res = (*env)->RegisterNatives(env, nxt_java_Context_class,
context_methods,
sizeof(context_methods)
/ sizeof(context_methods[0]));
nxt_unit_debug(NULL, "registered Context methods: %d", res);
if (res != 0) {
nxt_unit_warn(NULL, "registering natives for Context failed");
goto failed;
}
return NXT_UNIT_OK;
failed:
(*env)->DeleteGlobalRef(env, cls);
return NXT_UNIT_ERROR;
}
jobject
nxt_java_startContext(JNIEnv *env, const char *webapp, jobject classpaths)
{
jstring webapp_str;
webapp_str = (*env)->NewStringUTF(env, webapp);
if (webapp_str == NULL) {
return NULL;
}
return (*env)->CallStaticObjectMethod(env, nxt_java_Context_class,
nxt_java_Context_start, webapp_str,
classpaths);
}
void
nxt_java_service(JNIEnv *env, jobject ctx, jobject jreq, jobject jresp)
{
(*env)->CallVoidMethod(env, ctx, nxt_java_Context_service, jreq, jresp);
}
void
nxt_java_stopContext(JNIEnv *env, jobject ctx)
{
(*env)->CallVoidMethod(env, ctx, nxt_java_Context_stop);
}
static void JNICALL
nxt_java_Context_log(JNIEnv *env, jclass cls, jlong ctx_ptr, jstring msg,
jint msg_len)
{
const char *msg_str;
nxt_unit_ctx_t *ctx;
ctx = nxt_jlong2ptr(ctx_ptr);
msg_str = (*env)->GetStringUTFChars(env, msg, NULL);
if (msg_str == NULL) {
return;
}
nxt_unit_log(ctx, NXT_UNIT_LOG_INFO, "%.*s", msg_len, msg_str);
(*env)->ReleaseStringUTFChars(env, msg, msg_str);
}
static void JNICALL
nxt_java_Context_trace(JNIEnv *env, jclass cls, jlong ctx_ptr, jstring msg,
jint msg_len)
{
#if (NXT_DEBUG)
const char *msg_str;
nxt_unit_ctx_t *ctx;
ctx = nxt_jlong2ptr(ctx_ptr);
msg_str = (*env)->GetStringUTFChars(env, msg, NULL);
if (msg_str == NULL) {
return;
}
nxt_unit_debug(ctx, "%.*s", msg_len, msg_str);
(*env)->ReleaseStringUTFChars(env, msg, msg_str);
#endif
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (C) NGINX, Inc.
*/
#ifndef _NXT_JAVA_CONTEXT_H_INCLUDED_
#define _NXT_JAVA_CONTEXT_H_INCLUDED_
#include <jni.h>
int nxt_java_initContext(JNIEnv *env, jobject cl);
jobject nxt_java_startContext(JNIEnv *env, const char *webapp,
jobject classpaths);
void nxt_java_service(JNIEnv *env, jobject ctx, jobject jreq, jobject jresp);
void nxt_java_stopContext(JNIEnv *env, jobject ctx);
#endif /* _NXT_JAVA_CONTEXT_H_INCLUDED_ */

View File

@@ -0,0 +1,153 @@
/*
* Copyright (C) NGINX, Inc.
*/
#include <nxt_auto_config.h>
#include <nxt_unit.h>
#include <nxt_unit_request.h>
#include <jni.h>
#include <stdio.h>
#include "nxt_jni.h"
#include "nxt_jni_URLClassLoader.h"
#include "nxt_jni_HeaderNamesEnumeration.h"
static jlong JNICALL nxt_java_HeaderNamesEnumeration_nextElementPos(JNIEnv *env,
jclass cls, jlong headers_ptr, jlong size, jlong pos);
static jstring JNICALL nxt_java_HeaderNamesEnumeration_nextElement(JNIEnv *env,
jclass cls, jlong headers_ptr, jlong size, jlong pos);
static jclass nxt_java_HeaderNamesEnumeration_class;
static jmethodID nxt_java_HeaderNamesEnumeration_ctor;
int
nxt_java_initHeaderNamesEnumeration(JNIEnv *env, jobject cl)
{
int res;
jclass cls;
cls = nxt_java_loadClass(env, cl, "nginx.unit.HeaderNamesEnumeration");
if (cls == NULL) {
return NXT_UNIT_ERROR;
}
nxt_java_HeaderNamesEnumeration_class = (*env)->NewGlobalRef(env, cls);
(*env)->DeleteLocalRef(env, cls);
cls = nxt_java_HeaderNamesEnumeration_class;
nxt_java_HeaderNamesEnumeration_ctor = (*env)->GetMethodID(env, cls,
"<init>", "(JJ)V");
if (nxt_java_HeaderNamesEnumeration_ctor == NULL) {
(*env)->DeleteGlobalRef(env, cls);
return NXT_UNIT_ERROR;
}
JNINativeMethod hnenum_methods[] = {
{ (char *) "nextElementPos",
(char *) "(JJJ)J",
nxt_java_HeaderNamesEnumeration_nextElementPos },
{ (char *) "nextElement",
(char *) "(JJJ)Ljava/lang/String;",
nxt_java_HeaderNamesEnumeration_nextElement },
};
res = (*env)->RegisterNatives(env, nxt_java_HeaderNamesEnumeration_class,
hnenum_methods,
sizeof(hnenum_methods)
/ sizeof(hnenum_methods[0]));
nxt_unit_debug(NULL, "registered HeaderNamesEnumeration methods: %d", res);
if (res != 0) {
(*env)->DeleteGlobalRef(env, cls);
return NXT_UNIT_ERROR;
}
return NXT_UNIT_OK;
}
jobject
nxt_java_newHeaderNamesEnumeration(JNIEnv *env, nxt_unit_field_t *f,
uint32_t fields_count)
{
return (*env)->NewObject(env,
nxt_java_HeaderNamesEnumeration_class,
nxt_java_HeaderNamesEnumeration_ctor, nxt_ptr2jlong(f),
(jlong) fields_count);
}
static jlong JNICALL
nxt_java_HeaderNamesEnumeration_nextElementPos(JNIEnv *env, jclass cls,
jlong headers_ptr, jlong size, jlong pos)
{
nxt_unit_field_t *f;
f = nxt_jlong2ptr(headers_ptr);
if (pos >= size) {
return size;
}
if (pos > 0) {
while (pos < size
&& f[pos].hash == f[pos - 1].hash
&& f[pos].name_length == f[pos - 1].name_length)
{
pos++;
}
}
return pos;
}
static jstring JNICALL
nxt_java_HeaderNamesEnumeration_nextElement(JNIEnv *env, jclass cls,
jlong headers_ptr, jlong size, jlong pos)
{
char *name, tmp;
jstring res;
nxt_unit_field_t *f;
f = nxt_jlong2ptr(headers_ptr);
if (pos > 0) {
while (pos < size
&& f[pos].hash == f[pos - 1].hash
&& f[pos].name_length == f[pos - 1].name_length)
{
pos++;
}
}
if (pos >= size) {
nxt_java_throw_NoSuchElementException(env, "pos >= size");
return NULL;
}
f += pos;
name = nxt_unit_sptr_get(&f->name);
tmp = name[f->name_length];
if (tmp != '\0') {
name[f->name_length] = '\0';
}
res = (*env)->NewStringUTF(env, name);
if (tmp != '\0') {
name[f->name_length] = tmp;
}
return res;
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) NGINX, Inc.
*/
#ifndef _NXT_JAVA_HEADERNAMESENUMERATION_H_INCLUDED_
#define _NXT_JAVA_HEADERNAMESENUMERATION_H_INCLUDED_
#include <jni.h>
#include <nxt_unit_typedefs.h>
int nxt_java_initHeaderNamesEnumeration(JNIEnv *env, jobject cl);
jobject nxt_java_newHeaderNamesEnumeration(JNIEnv *env, nxt_unit_field_t *f,
uint32_t fields_count);
#endif /* _NXT_JAVA_HEADERNAMESENUMERATION_H_INCLUDED_ */

View File

@@ -0,0 +1,148 @@
/*
* Copyright (C) NGINX, Inc.
*/
#include <nxt_auto_config.h>
#include <nxt_unit.h>
#include <nxt_unit_request.h>
#include <jni.h>
#include <stdio.h>
#include "nxt_jni.h"
#include "nxt_jni_URLClassLoader.h"
#include "nxt_jni_HeadersEnumeration.h"
static jclass nxt_java_HeadersEnumeration_class;
static jmethodID nxt_java_HeadersEnumeration_ctor;
static jlong JNICALL nxt_java_HeadersEnumeration_nextElementPos(JNIEnv *env,
jclass cls, jlong headers_ptr, jlong size, jlong ipos, jlong pos);
static jstring JNICALL nxt_java_HeadersEnumeration_nextElement(JNIEnv *env,
jclass cls, jlong headers_ptr, jlong size, jlong ipos, jlong pos);
int
nxt_java_initHeadersEnumeration(JNIEnv *env, jobject cl)
{
int res;
jclass cls;
cls = nxt_java_loadClass(env, cl, "nginx.unit.HeadersEnumeration");
if (cls == NULL) {
return NXT_UNIT_ERROR;
}
nxt_java_HeadersEnumeration_class = (*env)->NewGlobalRef(env, cls);
(*env)->DeleteLocalRef(env, cls);
cls = nxt_java_HeadersEnumeration_class;
nxt_java_HeadersEnumeration_ctor = (*env)->GetMethodID(env, cls,
"<init>", "(JJJ)V");
if (nxt_java_HeadersEnumeration_ctor == NULL) {
(*env)->DeleteGlobalRef(env, cls);
return NXT_UNIT_ERROR;
}
JNINativeMethod methods[] = {
{ (char *) "nextElementPos",
(char *) "(JJJJ)J",
nxt_java_HeadersEnumeration_nextElementPos },
{ (char *) "nextElement",
(char *) "(JJJJ)Ljava/lang/String;",
nxt_java_HeadersEnumeration_nextElement },
};
res = (*env)->RegisterNatives(env, nxt_java_HeadersEnumeration_class,
methods,
sizeof(methods) / sizeof(methods[0]));
nxt_unit_debug(NULL, "registered HeadersEnumeration methods: %d", res);
if (res != 0) {
(*env)->DeleteGlobalRef(env, cls);
return NXT_UNIT_ERROR;
}
return NXT_UNIT_OK;
}
jobject
nxt_java_newHeadersEnumeration(JNIEnv *env, nxt_unit_field_t *f,
uint32_t fields_count, uint32_t pos)
{
return (*env)->NewObject(env,
nxt_java_HeadersEnumeration_class,
nxt_java_HeadersEnumeration_ctor, nxt_ptr2jlong(f),
(jlong) fields_count, (jlong) pos);
}
static jlong JNICALL
nxt_java_HeadersEnumeration_nextElementPos(JNIEnv *env, jclass cls,
jlong headers_ptr, jlong size, jlong ipos, jlong pos)
{
nxt_unit_field_t *f, *init_field;
f = nxt_jlong2ptr(headers_ptr);
init_field = f + ipos;
if (pos >= size) {
return size;
}
f += pos;
if (f->hash != init_field->hash
|| f->name_length != init_field->name_length)
{
return size;
}
if (!nxt_java_strcaseeq(nxt_unit_sptr_get(&f->name),
nxt_unit_sptr_get(&init_field->name),
init_field->name_length))
{
return size;
}
return pos;
}
static jstring JNICALL
nxt_java_HeadersEnumeration_nextElement(JNIEnv *env, jclass cls,
jlong headers_ptr, jlong size, jlong ipos, jlong pos)
{
nxt_unit_field_t *f, *init_field;
f = nxt_jlong2ptr(headers_ptr);
init_field = f + ipos;
if (pos >= size) {
nxt_java_throw_IOException(env, "pos >= size");
return NULL;
}
f += pos;
if (f->hash != init_field->hash
|| f->name_length != init_field->name_length)
{
nxt_java_throw_IOException(env, "f->hash != hash");
return NULL;
}
return nxt_java_newString(env, nxt_unit_sptr_get(&f->value),
f->value_length);
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) NGINX, Inc.
*/
#ifndef _NXT_JAVA_HEADERSENUMERATION_H_INCLUDED_
#define _NXT_JAVA_HEADERSENUMERATION_H_INCLUDED_
#include <jni.h>
#include <nxt_unit_typedefs.h>
int nxt_java_initHeadersEnumeration(JNIEnv *env, jobject cl);
jobject nxt_java_newHeadersEnumeration(JNIEnv *env, nxt_unit_field_t *f,
uint32_t fields_count, uint32_t pos);
#endif /* _NXT_JAVA_HEADERSENUMERATION_H_INCLUDED_ */

View File

@@ -0,0 +1,230 @@
/*
* Copyright (C) NGINX, Inc.
*/
#include <nxt_auto_config.h>
#include <jni.h>
#include <nxt_unit.h>
#include <string.h>
#include "nxt_jni.h"
#include "nxt_jni_InputStream.h"
#include "nxt_jni_URLClassLoader.h"
static jint JNICALL nxt_java_InputStream_readLine(JNIEnv *env, jclass cls,
jlong req_info_ptr, jarray b, jint off, jint len);
static jboolean JNICALL nxt_java_InputStream_isFinished(JNIEnv *env, jclass cls,
jlong req_info_ptr);
static jint JNICALL nxt_java_InputStream_readByte(JNIEnv *env, jclass cls,
jlong req_info_ptr);
static jint JNICALL nxt_java_InputStream_read(JNIEnv *env, jclass cls,
jlong req_info_ptr, jarray b, jint off, jint len);
static jlong JNICALL nxt_java_InputStream_skip(JNIEnv *env, jclass cls,
jlong req_info_ptr, jlong n);
static jint JNICALL nxt_java_InputStream_available(JNIEnv *env, jclass cls,
jlong req_info_ptr);
static jclass nxt_java_InputStream_class;
int
nxt_java_initInputStream(JNIEnv *env, jobject cl)
{
int res;
jclass cls;
cls = nxt_java_loadClass(env, cl, "nginx.unit.InputStream");
if (cls == NULL) {
return NXT_UNIT_ERROR;
}
nxt_java_InputStream_class = (*env)->NewGlobalRef(env, cls);
(*env)->DeleteLocalRef(env, cls);
JNINativeMethod is_methods[] = {
{ (char *) "readLine",
(char *) "(J[BII)I",
nxt_java_InputStream_readLine },
{ (char *) "isFinished",
(char *) "(J)Z",
nxt_java_InputStream_isFinished },
{ (char *) "read",
(char *) "(J)I",
nxt_java_InputStream_readByte },
{ (char *) "read",
(char *) "(J[BII)I",
nxt_java_InputStream_read },
{ (char *) "skip",
(char *) "(JJ)J",
nxt_java_InputStream_skip },
{ (char *) "available",
(char *) "(J)I",
nxt_java_InputStream_available },
};
res = (*env)->RegisterNatives(env, nxt_java_InputStream_class,
is_methods,
sizeof(is_methods) / sizeof(is_methods[0]));
nxt_unit_debug(NULL, "registered InputStream methods: %d", res);
if (res != 0) {
(*env)->DeleteGlobalRef(env, cls);
return NXT_UNIT_ERROR;
}
return NXT_UNIT_OK;
}
static jint JNICALL
nxt_java_InputStream_readLine(JNIEnv *env, jclass cls,
jlong req_info_ptr, jarray out, jint off, jint len)
{
char *p;
jint size, b_size;
uint8_t *data;
ssize_t res;
nxt_unit_buf_t *b;
nxt_unit_request_info_t *req;
req = nxt_jlong2ptr(req_info_ptr);
size = 0;
for (b = req->content_buf; b; b = nxt_unit_buf_next(b)) {
b_size = b->end - b->free;
p = memchr(b->free, '\n', b_size);
if (p != NULL) {
p++;
size += p - b->free;
break;
}
size += b_size;
if (size >= len) {
break;
}
}
len = len < size ? len : size;
data = (*env)->GetPrimitiveArrayCritical(env, out, NULL);
res = nxt_unit_request_read(req, data + off, len);
nxt_unit_req_debug(req, "readLine '%.*s'", res, (char *) data + off);
(*env)->ReleasePrimitiveArrayCritical(env, out, data, 0);
return res > 0 ? res : -1;
}
static jboolean JNICALL
nxt_java_InputStream_isFinished(JNIEnv *env, jclass cls, jlong req_info_ptr)
{
nxt_unit_request_info_t *req;
req = nxt_jlong2ptr(req_info_ptr);
return req->content_length == 0;
}
static jint JNICALL
nxt_java_InputStream_readByte(JNIEnv *env, jclass cls, jlong req_info_ptr)
{
uint8_t b;
ssize_t size;
nxt_unit_request_info_t *req;
req = nxt_jlong2ptr(req_info_ptr);
size = nxt_unit_request_read(req, &b, 1);
return size == 1 ? b : -1;
}
static jint JNICALL
nxt_java_InputStream_read(JNIEnv *env, jclass cls, jlong req_info_ptr,
jarray b, jint off, jint len)
{
uint8_t *data;
ssize_t res;
nxt_unit_request_info_t *req;
req = nxt_jlong2ptr(req_info_ptr);
data = (*env)->GetPrimitiveArrayCritical(env, b, NULL);
res = nxt_unit_request_read(req, data + off, len);
nxt_unit_req_debug(req, "read '%.*s'", res, (char *) data + off);
(*env)->ReleasePrimitiveArrayCritical(env, b, data, 0);
return res > 0 ? res : -1;
}
static jlong JNICALL
nxt_java_InputStream_skip(JNIEnv *env, jclass cls, jlong req_info_ptr, jlong n)
{
size_t rest, b_size;
nxt_unit_buf_t *buf;
nxt_unit_request_info_t *req;
req = nxt_jlong2ptr(req_info_ptr);
rest = n;
buf = req->content_buf;
while (buf != NULL) {
b_size = buf->end - buf->free;
b_size = rest < b_size ? rest : b_size;
buf->free += b_size;
rest -= b_size;
if (rest == 0) {
if (buf->end == buf->free) {
buf = nxt_unit_buf_next(buf);
}
break;
}
buf = nxt_unit_buf_next(buf);
}
n = n < (jlong) req->content_length ? n : (jlong) req->content_length;
req->content_length -= n;
return n;
}
static jint JNICALL
nxt_java_InputStream_available(JNIEnv *env, jclass cls, jlong req_info_ptr)
{
nxt_unit_request_info_t *req;
req = nxt_jlong2ptr(req_info_ptr);
return req->content_length;
}

View File

@@ -0,0 +1,15 @@
/*
* Copyright (C) NGINX, Inc.
*/
#ifndef _NXT_JAVA_INPUTSTREAM_H_INCLUDED_
#define _NXT_JAVA_INPUTSTREAM_H_INCLUDED_
#include <jni.h>
int nxt_java_initInputStream(JNIEnv *env, jobject cl);
#endif /* _NXT_JAVA_INPUTSTREAM_H_INCLUDED_ */

View File

@@ -0,0 +1,236 @@
/*
* Copyright (C) NGINX, Inc.
*/
#include <nxt_auto_config.h>
#include <jni.h>
#include <nxt_unit.h>
#include "nxt_jni.h"
#include "nxt_jni_OutputStream.h"
#include "nxt_jni_URLClassLoader.h"
static void JNICALL nxt_java_OutputStream_writeByte(JNIEnv *env, jclass cls,
jlong req_info_ptr, jint b);
static nxt_unit_buf_t *nxt_java_OutputStream_req_buf(JNIEnv *env,
nxt_unit_request_info_t *req);
static void JNICALL nxt_java_OutputStream_write(JNIEnv *env, jclass cls,
jlong req_info_ptr, jarray b, jint off, jint len);
static void JNICALL nxt_java_OutputStream_flush(JNIEnv *env, jclass cls,
jlong req_info_ptr);
static void JNICALL nxt_java_OutputStream_close(JNIEnv *env, jclass cls,
jlong req_info_ptr);
static jclass nxt_java_OutputStream_class;
int
nxt_java_initOutputStream(JNIEnv *env, jobject cl)
{
int res;
jclass cls;
cls = nxt_java_loadClass(env, cl, "nginx.unit.OutputStream");
if (cls == NULL) {
return NXT_UNIT_ERROR;
}
nxt_java_OutputStream_class = (*env)->NewGlobalRef(env, cls);
(*env)->DeleteLocalRef(env, cls);
cls = nxt_java_OutputStream_class;
JNINativeMethod os_methods[] = {
{ (char *) "write",
(char *) "(JI)V",
nxt_java_OutputStream_writeByte },
{ (char *) "write",
(char *) "(J[BII)V",
nxt_java_OutputStream_write },
{ (char *) "flush",
(char *) "(J)V",
nxt_java_OutputStream_flush },
{ (char *) "close",
(char *) "(J)V",
nxt_java_OutputStream_close },
};
res = (*env)->RegisterNatives(env, nxt_java_OutputStream_class,
os_methods,
sizeof(os_methods) / sizeof(os_methods[0]));
nxt_unit_debug(NULL, "registered OutputStream methods: %d", res);
if (res != 0) {
(*env)->DeleteGlobalRef(env, cls);
return NXT_UNIT_ERROR;
}
return NXT_UNIT_OK;
}
static void JNICALL
nxt_java_OutputStream_writeByte(JNIEnv *env, jclass cls, jlong req_info_ptr,
jint b)
{
nxt_unit_buf_t *buf;
nxt_unit_request_info_t *req;
nxt_java_request_data_t *data;
req = nxt_jlong2ptr(req_info_ptr);
data = req->data;
buf = nxt_java_OutputStream_req_buf(env, req);
if (buf == NULL) {
return;
}
*buf->free++ = b;
if ((uint32_t) (buf->free - buf->start) >= data->buf_size) {
nxt_java_OutputStream_flush_buf(env, req);
}
}
int
nxt_java_OutputStream_flush_buf(JNIEnv *env, nxt_unit_request_info_t *req)
{
int rc;
nxt_java_request_data_t *data;
data = req->data;
if (!nxt_unit_response_is_init(req)) {
rc = nxt_unit_response_init(req, 200, 0, 0);
if (rc != NXT_UNIT_OK) {
nxt_java_throw_IOException(env, "Failed to allocate response");
return rc;
}
}
if (!nxt_unit_response_is_sent(req)) {
rc = nxt_unit_response_send(req);
if (rc != NXT_UNIT_OK) {
nxt_java_throw_IOException(env, "Failed to send response headers");
return rc;
}
}
if (data->buf != NULL) {
rc = nxt_unit_buf_send(data->buf);
if (rc != NXT_UNIT_OK) {
nxt_java_throw_IOException(env, "Failed to send buffer");
} else {
data->buf = NULL;
}
} else {
rc = NXT_UNIT_OK;
}
return rc;
}
static nxt_unit_buf_t *
nxt_java_OutputStream_req_buf(JNIEnv *env, nxt_unit_request_info_t *req)
{
uint32_t size;
nxt_unit_buf_t *buf;
nxt_java_request_data_t *data;
data = req->data;
buf = data->buf;
if (buf == NULL || buf->free >= buf->end) {
size = data->buf_size == 0 ? nxt_unit_buf_min() : data->buf_size;
buf = nxt_unit_response_buf_alloc(req, size);
if (buf == NULL) {
nxt_java_throw_IOException(env, "Failed to allocate buffer");
return NULL;
}
data->buf = buf;
}
return buf;
}
static void JNICALL
nxt_java_OutputStream_write(JNIEnv *env, jclass cls, jlong req_info_ptr,
jarray b, jint off, jint len)
{
int rc;
jint copy;
uint8_t *ptr;
nxt_unit_buf_t *buf;
nxt_unit_request_info_t *req;
nxt_java_request_data_t *data;
req = nxt_jlong2ptr(req_info_ptr);
data = req->data;
ptr = (*env)->GetPrimitiveArrayCritical(env, b, NULL);
while (len > 0) {
buf = nxt_java_OutputStream_req_buf(env, req);
if (buf == NULL) {
return;
}
copy = buf->end - buf->free;
copy = copy < len ? copy : len;
memcpy(buf->free, ptr + off, copy);
buf->free += copy;
len -= copy;
off += copy;
if ((uint32_t) (buf->free - buf->start) >= data->buf_size) {
rc = nxt_java_OutputStream_flush_buf(env, req);
if (rc != NXT_UNIT_OK) {
break;
}
}
}
(*env)->ReleasePrimitiveArrayCritical(env, b, ptr, 0);
}
static void JNICALL
nxt_java_OutputStream_flush(JNIEnv *env, jclass cls, jlong req_info_ptr)
{
nxt_unit_request_info_t *req;
nxt_java_request_data_t *data;
req = nxt_jlong2ptr(req_info_ptr);
data = req->data;
if (data->buf != NULL && data->buf->free > data->buf->start) {
nxt_java_OutputStream_flush_buf(env, req);
}
}
static void JNICALL
nxt_java_OutputStream_close(JNIEnv *env, jclass cls, jlong req_info_ptr)
{
nxt_java_OutputStream_flush_buf(env, nxt_jlong2ptr(req_info_ptr));
}

View File

@@ -0,0 +1,17 @@
/*
* Copyright (C) NGINX, Inc.
*/
#ifndef _NXT_JAVA_OUTPUTSTREAM_H_INCLUDED_
#define _NXT_JAVA_OUTPUTSTREAM_H_INCLUDED_
#include <jni.h>
int nxt_java_initOutputStream(JNIEnv *env, jobject cl);
int nxt_java_OutputStream_flush_buf(JNIEnv *env, nxt_unit_request_info_t *req);
#endif /* _NXT_JAVA_OUTPUTSTREAM_H_INCLUDED_ */

658
src/java/nxt_jni_Request.c Normal file
View File

@@ -0,0 +1,658 @@
/*
* Copyright (C) NGINX, Inc.
*/
#include <nxt_auto_config.h>
#include <nxt_unit.h>
#include <nxt_unit_request.h>
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include "nxt_jni.h"
#include "nxt_jni_Request.h"
#include "nxt_jni_URLClassLoader.h"
#include "nxt_jni_HeadersEnumeration.h"
#include "nxt_jni_HeaderNamesEnumeration.h"
static jstring JNICALL nxt_java_Request_getHeader(JNIEnv *env, jclass cls,
jlong req_ptr, jstring name, jint name_len);
static jobject JNICALL nxt_java_Request_getHeaderNames(JNIEnv *env, jclass cls,
jlong req_ptr);
static jobject JNICALL nxt_java_Request_getHeaders(JNIEnv *env, jclass cls,
jlong req_ptr, jstring name, jint name_len);
static jint JNICALL nxt_java_Request_getIntHeader(JNIEnv *env, jclass cls,
jlong req_ptr, jstring name, jint name_len);
static jstring JNICALL nxt_java_Request_getMethod(JNIEnv *env, jclass cls,
jlong req_ptr);
static jstring JNICALL nxt_java_Request_getQueryString(JNIEnv *env, jclass cls,
jlong req_ptr);
static jstring JNICALL nxt_java_Request_getRequestURI(JNIEnv *env, jclass cls,
jlong req_ptr);
static jlong JNICALL nxt_java_Request_getContentLength(JNIEnv *env, jclass cls,
jlong req_ptr);
static jstring JNICALL nxt_java_Request_getContentType(JNIEnv *env, jclass cls,
jlong req_ptr);
static jstring JNICALL nxt_java_Request_getLocalAddr(JNIEnv *env, jclass cls,
jlong req_ptr);
static jstring JNICALL nxt_java_Request_getLocalName(JNIEnv *env, jclass cls,
jlong req_ptr);
static jint JNICALL nxt_java_Request_getLocalPort(JNIEnv *env, jclass cls,
jlong req_ptr);
static jstring JNICALL nxt_java_Request_getProtocol(JNIEnv *env, jclass cls,
jlong req_ptr);
static jstring JNICALL nxt_java_Request_getRemoteAddr(JNIEnv *env, jclass cls,
jlong req_ptr);
static jstring JNICALL nxt_java_Request_getRemoteHost(JNIEnv *env, jclass cls,
jlong req_ptr);
static jint JNICALL nxt_java_Request_getRemotePort(JNIEnv *env, jclass cls,
jlong req_ptr);
static jstring JNICALL nxt_java_Request_getScheme(JNIEnv *env, jclass cls,
jlong req_ptr);
static jstring JNICALL nxt_java_Request_getServerName(JNIEnv *env, jclass cls,
jlong req_ptr);
static jint JNICALL nxt_java_Request_getServerPort(JNIEnv *env, jclass cls,
jlong req_ptr);
static void JNICALL nxt_java_Request_log(JNIEnv *env, jclass cls,
jlong req_info_ptr, jstring msg, jint msg_len);
static void JNICALL nxt_java_Request_trace(JNIEnv *env, jclass cls,
jlong req_info_ptr, jstring msg, jint msg_len);
static jobject JNICALL nxt_java_Request_getResponse(JNIEnv *env, jclass cls,
jlong req_info_ptr);
static jclass nxt_java_Request_class;
static jmethodID nxt_java_Request_ctor;
int
nxt_java_initRequest(JNIEnv *env, jobject cl)
{
int res;
jclass cls;
cls = nxt_java_loadClass(env, cl, "nginx.unit.Request");
if (cls == NULL) {
return NXT_UNIT_ERROR;
}
nxt_java_Request_class = (*env)->NewGlobalRef(env, cls);
(*env)->DeleteLocalRef(env, cls);
cls = nxt_java_Request_class;
nxt_java_Request_ctor = (*env)->GetMethodID(env, cls, "<init>", "(Lnginx/unit/Context;JJ)V");
if (nxt_java_Request_ctor == NULL) {
(*env)->DeleteGlobalRef(env, cls);
return NXT_UNIT_ERROR;
}
JNINativeMethod request_methods[] = {
{ (char *) "getHeader",
(char *) "(JLjava/lang/String;I)Ljava/lang/String;",
nxt_java_Request_getHeader },
{ (char *) "getHeaderNames",
(char *) "(J)Ljava/util/Enumeration;",
nxt_java_Request_getHeaderNames },
{ (char *) "getHeaders",
(char *) "(JLjava/lang/String;I)Ljava/util/Enumeration;",
nxt_java_Request_getHeaders },
{ (char *) "getIntHeader",
(char *) "(JLjava/lang/String;I)I",
nxt_java_Request_getIntHeader },
{ (char *) "getMethod",
(char *) "(J)Ljava/lang/String;",
nxt_java_Request_getMethod },
{ (char *) "getQueryString",
(char *) "(J)Ljava/lang/String;",
nxt_java_Request_getQueryString },
{ (char *) "getRequestURI",
(char *) "(J)Ljava/lang/String;",
nxt_java_Request_getRequestURI },
{ (char *) "getContentLength",
(char *) "(J)J",
nxt_java_Request_getContentLength },
{ (char *) "getContentType",
(char *) "(J)Ljava/lang/String;",
nxt_java_Request_getContentType },
{ (char *) "getLocalAddr",
(char *) "(J)Ljava/lang/String;",
nxt_java_Request_getLocalAddr },
{ (char *) "getLocalName",
(char *) "(J)Ljava/lang/String;",
nxt_java_Request_getLocalName },
{ (char *) "getLocalPort",
(char *) "(J)I",
nxt_java_Request_getLocalPort },
{ (char *) "getProtocol",
(char *) "(J)Ljava/lang/String;",
nxt_java_Request_getProtocol },
{ (char *) "getRemoteAddr",
(char *) "(J)Ljava/lang/String;",
nxt_java_Request_getRemoteAddr },
{ (char *) "getRemoteHost",
(char *) "(J)Ljava/lang/String;",
nxt_java_Request_getRemoteHost },
{ (char *) "getRemotePort",
(char *) "(J)I",
nxt_java_Request_getRemotePort },
{ (char *) "getScheme",
(char *) "(J)Ljava/lang/String;",
nxt_java_Request_getScheme },
{ (char *) "getServerName",
(char *) "(J)Ljava/lang/String;",
nxt_java_Request_getServerName },
{ (char *) "getServerPort",
(char *) "(J)I",
nxt_java_Request_getServerPort },
{ (char *) "log",
(char *) "(JLjava/lang/String;I)V",
nxt_java_Request_log },
{ (char *) "trace",
(char *) "(JLjava/lang/String;I)V",
nxt_java_Request_trace },
{ (char *) "getResponse",
(char *) "(J)Lnginx/unit/Response;",
nxt_java_Request_getResponse },
};
res = (*env)->RegisterNatives(env, nxt_java_Request_class,
request_methods,
sizeof(request_methods) / sizeof(request_methods[0]));
nxt_unit_debug(NULL, "registered Request methods: %d", res);
if (res != 0) {
nxt_unit_warn(NULL, "registering natives for Request failed");
goto failed;
}
res = nxt_java_initHeadersEnumeration(env, cl);
if (res != NXT_UNIT_OK) {
goto failed;
}
res = nxt_java_initHeaderNamesEnumeration(env, cl);
if (res != NXT_UNIT_OK) {
goto failed;
}
return NXT_UNIT_OK;
failed:
(*env)->DeleteGlobalRef(env, cls);
return NXT_UNIT_ERROR;
}
jobject
nxt_java_newRequest(JNIEnv *env, jobject ctx, nxt_unit_request_info_t *req)
{
return (*env)->NewObject(env, nxt_java_Request_class,
nxt_java_Request_ctor, ctx, nxt_ptr2jlong(req),
nxt_ptr2jlong(req->request));
}
static jstring JNICALL
nxt_java_Request_getHeader(JNIEnv *env, jclass cls, jlong req_ptr,
jstring name, jint name_len)
{
const char *name_str;
nxt_unit_field_t *f;
nxt_unit_request_t *r;
name_str = (*env)->GetStringUTFChars(env, name, NULL);
if (name_str == NULL) {
return NULL;
}
r = nxt_jlong2ptr(req_ptr);
f = nxt_java_findHeader(r->fields, r->fields + r->fields_count,
name_str, name_len);
(*env)->ReleaseStringUTFChars(env, name, name_str);
if (f == NULL) {
return NULL;
}
return (*env)->NewStringUTF(env, nxt_unit_sptr_get(&f->value));
}
static jobject JNICALL
nxt_java_Request_getHeaderNames(JNIEnv *env, jclass cls, jlong req_ptr)
{
nxt_unit_request_t *r;
r = nxt_jlong2ptr(req_ptr);
return nxt_java_newHeaderNamesEnumeration(env, r->fields, r->fields_count);
}
static jobject JNICALL
nxt_java_Request_getHeaders(JNIEnv *env, jclass cls, jlong req_ptr,
jstring name, jint name_len)
{
const char *name_str;
nxt_unit_field_t *f;
nxt_unit_request_t *r;
name_str = (*env)->GetStringUTFChars(env, name, NULL);
if (name_str == NULL) {
return NULL;
}
r = nxt_jlong2ptr(req_ptr);
f = nxt_java_findHeader(r->fields, r->fields + r->fields_count,
name_str, name_len);
(*env)->ReleaseStringUTFChars(env, name, name_str);
if (f == NULL) {
f = r->fields + r->fields_count;
}
return nxt_java_newHeadersEnumeration(env, r->fields, r->fields_count,
f - r->fields);
}
static jint JNICALL
nxt_java_Request_getIntHeader(JNIEnv *env, jclass cls, jlong req_ptr,
jstring name, jint name_len)
{
jint res;
char *value, *end;
const char *name_str;
nxt_unit_field_t *f;
nxt_unit_request_t *r;
res = -1;
name_str = (*env)->GetStringUTFChars(env, name, NULL);
if (name_str == NULL) {
return res;
}
r = nxt_jlong2ptr(req_ptr);
f = nxt_java_findHeader(r->fields, r->fields + r->fields_count,
name_str, name_len);
(*env)->ReleaseStringUTFChars(env, name, name_str);
if (f == NULL) {
return res;
}
value = nxt_unit_sptr_get(&f->value);
end = value + f->value_length;
res = strtol(value, &end, 10);
if (end < value + f->value_length) {
// TODO throw NumberFormatException.forInputString(value)
}
return res;
}
static jstring JNICALL
nxt_java_Request_getMethod(JNIEnv *env, jclass cls, jlong req_ptr)
{
nxt_unit_request_t *r;
r = nxt_jlong2ptr(req_ptr);
return (*env)->NewStringUTF(env, nxt_unit_sptr_get(&r->method));
}
static jstring JNICALL
nxt_java_Request_getQueryString(JNIEnv *env, jclass cls, jlong req_ptr)
{
char *query;
nxt_unit_request_t *r;
r = nxt_jlong2ptr(req_ptr);
if (r->query.offset != 0) {
query = nxt_unit_sptr_get(&r->query);
return (*env)->NewStringUTF(env, query);
}
return NULL;
}
static jstring JNICALL
nxt_java_Request_getRequestURI(JNIEnv *env, jclass cls, jlong req_ptr)
{
char *target, *query;
nxt_unit_request_t *r;
r = nxt_jlong2ptr(req_ptr);
target = nxt_unit_sptr_get(&r->target);
if (r->query.offset != 0) {
query = nxt_unit_sptr_get(&r->query);
return nxt_java_newString(env, target, query - target - 1);
}
return (*env)->NewStringUTF(env, target);
}
static jlong JNICALL
nxt_java_Request_getContentLength(JNIEnv *env, jclass cls, jlong req_ptr)
{
nxt_unit_request_t *r;
r = nxt_jlong2ptr(req_ptr);
return r->content_length;
}
static jstring JNICALL
nxt_java_Request_getContentType(JNIEnv *env, jclass cls, jlong req_ptr)
{
nxt_unit_field_t *f;
nxt_unit_request_t *r;
r = nxt_jlong2ptr(req_ptr);
if (r->content_type_field != NXT_UNIT_NONE_FIELD) {
f = r->fields + r->content_type_field;
return (*env)->NewStringUTF(env, nxt_unit_sptr_get(&f->value));
}
return NULL;
}
static jstring JNICALL
nxt_java_Request_getLocalAddr(JNIEnv *env, jclass cls, jlong req_ptr)
{
nxt_unit_request_t *r;
r = nxt_jlong2ptr(req_ptr);
return nxt_java_newString(env, nxt_unit_sptr_get(&r->local),
r->local_length);
}
static jstring JNICALL
nxt_java_Request_getLocalName(JNIEnv *env, jclass cls, jlong req_ptr)
{
char *local, *colon;
nxt_unit_request_t *r;
r = nxt_jlong2ptr(req_ptr);
local = nxt_unit_sptr_get(&r->local);
colon = memchr(local, ':', r->local_length);
if (colon == NULL) {
colon = local + r->local_length;
}
return nxt_java_newString(env, local, colon - local);
}
static jint JNICALL
nxt_java_Request_getLocalPort(JNIEnv *env, jclass cls, jlong req_ptr)
{
jint res;
char *local, *colon, tmp;
nxt_unit_request_t *r;
r = nxt_jlong2ptr(req_ptr);
local = nxt_unit_sptr_get(&r->local);
colon = memchr(local, ':', r->local_length);
if (colon == NULL) {
return 80;
}
tmp = local[r->local_length];
local[r->local_length] = '\0';
res = strtol(colon + 1, NULL, 10);
local[r->local_length] = tmp;
return res;
}
static jstring JNICALL
nxt_java_Request_getProtocol(JNIEnv *env, jclass cls, jlong req_ptr)
{
nxt_unit_request_t *r;
r = nxt_jlong2ptr(req_ptr);
return (*env)->NewStringUTF(env, nxt_unit_sptr_get(&r->version));
}
static jstring JNICALL
nxt_java_Request_getRemoteAddr(JNIEnv *env, jclass cls, jlong req_ptr)
{
nxt_unit_request_t *r;
r = nxt_jlong2ptr(req_ptr);
return nxt_java_newString(env, nxt_unit_sptr_get(&r->remote),
r->remote_length);
}
static jstring JNICALL
nxt_java_Request_getRemoteHost(JNIEnv *env, jclass cls, jlong req_ptr)
{
char *remote, *colon;
nxt_unit_request_t *r;
r = nxt_jlong2ptr(req_ptr);
remote = nxt_unit_sptr_get(&r->remote);
colon = memchr(remote, ':', r->remote_length);
if (colon == NULL) {
colon = remote + r->remote_length;
}
return nxt_java_newString(env, remote, colon - remote);
}
static jint JNICALL
nxt_java_Request_getRemotePort(JNIEnv *env, jclass cls, jlong req_ptr)
{
jint res;
char *remote, *colon, tmp;
nxt_unit_request_t *r;
r = nxt_jlong2ptr(req_ptr);
remote = nxt_unit_sptr_get(&r->remote);
colon = memchr(remote, ':', r->remote_length);
if (colon == NULL) {
return 80;
}
tmp = remote[r->remote_length];
remote[r->remote_length] = '\0';
res = strtol(colon + 1, NULL, 10);
remote[r->remote_length] = tmp;
return res;
}
static jstring JNICALL
nxt_java_Request_getScheme(JNIEnv *env, jclass cls, jlong req_ptr)
{
return (*env)->NewStringUTF(env, "http");
}
static jstring JNICALL
nxt_java_Request_getServerName(JNIEnv *env, jclass cls, jlong req_ptr)
{
char *host, *colon;
nxt_unit_field_t *f;
nxt_unit_request_t *r;
r = nxt_jlong2ptr(req_ptr);
f = nxt_java_findHeader(r->fields, r->fields + r->fields_count,
"Host", 4);
if (f != NULL) {
host = nxt_unit_sptr_get(&f->value);
colon = memchr(host, ':', f->value_length);
if (colon == NULL) {
colon = host + f->value_length;
}
return nxt_java_newString(env, host, colon - host);
}
return nxt_java_Request_getLocalName(env, cls, req_ptr);
}
static jint JNICALL
nxt_java_Request_getServerPort(JNIEnv *env, jclass cls, jlong req_ptr)
{
jint res;
char *host, *colon, tmp;
nxt_unit_field_t *f;
nxt_unit_request_t *r;
r = nxt_jlong2ptr(req_ptr);
f = nxt_java_findHeader(r->fields, r->fields + r->fields_count,
"Host", 4);
if (f != NULL) {
host = nxt_unit_sptr_get(&f->value);
colon = memchr(host, ':', f->value_length);
if (colon == NULL) {
return 80;
}
tmp = host[f->value_length];
host[f->value_length] = '\0';
res = strtol(colon + 1, NULL, 10);
host[f->value_length] = tmp;
return res;
}
return nxt_java_Request_getLocalPort(env, cls, req_ptr);
}
static void JNICALL
nxt_java_Request_log(JNIEnv *env, jclass cls, jlong req_info_ptr, jstring msg,
jint msg_len)
{
const char *msg_str;
nxt_unit_request_info_t *req;
req = nxt_jlong2ptr(req_info_ptr);
msg_str = (*env)->GetStringUTFChars(env, msg, NULL);
if (msg_str == NULL) {
return;
}
nxt_unit_req_log(req, NXT_UNIT_LOG_INFO, "%.*s", msg_len, msg_str);
(*env)->ReleaseStringUTFChars(env, msg, msg_str);
}
static void JNICALL
nxt_java_Request_trace(JNIEnv *env, jclass cls, jlong req_info_ptr, jstring msg,
jint msg_len)
{
#if (NXT_DEBUG)
const char *msg_str;
nxt_unit_request_info_t *req;
req = nxt_jlong2ptr(req_info_ptr);
msg_str = (*env)->GetStringUTFChars(env, msg, NULL);
if (msg_str == NULL) {
return;
}
nxt_unit_req_debug(req, "%.*s", msg_len, msg_str);
(*env)->ReleaseStringUTFChars(env, msg, msg_str);
#endif
}
static jobject JNICALL
nxt_java_Request_getResponse(JNIEnv *env, jclass cls, jlong req_info_ptr)
{
nxt_unit_request_info_t *req;
nxt_java_request_data_t *data;
req = nxt_jlong2ptr(req_info_ptr);
data = req->data;
return data->jresp;
}

View File

@@ -0,0 +1,18 @@
/*
* Copyright (C) NGINX, Inc.
*/
#ifndef _NXT_JAVA_REQUEST_H_INCLUDED_
#define _NXT_JAVA_REQUEST_H_INCLUDED_
#include <jni.h>
#include <nxt_unit_typedefs.h>
int nxt_java_initRequest(JNIEnv *env, jobject cl);
jobject nxt_java_newRequest(JNIEnv *env, jobject ctx, nxt_unit_request_info_t *req);
#endif /* _NXT_JAVA_REQUEST_H_INCLUDED_ */

1105
src/java/nxt_jni_Response.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,18 @@
/*
* Copyright (C) NGINX, Inc.
*/
#ifndef _NXT_JAVA_RESPONSE_H_INCLUDED_
#define _NXT_JAVA_RESPONSE_H_INCLUDED_
#include <jni.h>
#include <nxt_unit_typedefs.h>
int nxt_java_initResponse(JNIEnv *env, jobject cl);
jobject nxt_java_newResponse(JNIEnv *env, nxt_unit_request_info_t *req);
#endif /* _NXT_JAVA_RESPONSE_H_INCLUDED_ */

94
src/java/nxt_jni_Thread.c Normal file
View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) NGINX, Inc.
*/
#include <nxt_unit.h>
#include <jni.h>
#include "nxt_jni_Thread.h"
static jclass nxt_java_Thread_class;
static jmethodID nxt_java_Thread_currentThread;
static jmethodID nxt_java_Thread_getContextClassLoader;
static jmethodID nxt_java_Thread_setContextClassLoader;
int
nxt_java_initThread(JNIEnv *env)
{
jclass cls;
cls = (*env)->FindClass(env, "java/lang/Thread");
if (cls == NULL) {
nxt_unit_warn(NULL, "java.lang.Thread not found");
return NXT_UNIT_ERROR;
}
nxt_java_Thread_class = (*env)->NewGlobalRef(env, cls);
(*env)->DeleteLocalRef(env, cls);
cls = nxt_java_Thread_class;
nxt_java_Thread_currentThread = (*env)->GetStaticMethodID(env, cls,
"currentThread", "()Ljava/lang/Thread;");
if (nxt_java_Thread_currentThread == NULL) {
nxt_unit_warn(NULL, "java.lang.Thread.currentThread() not found");
goto failed;
}
nxt_java_Thread_getContextClassLoader = (*env)->GetMethodID(env, cls,
"getContextClassLoader", "()Ljava/lang/ClassLoader;");
if (nxt_java_Thread_getContextClassLoader == NULL) {
nxt_unit_warn(NULL, "java.lang.Thread.getContextClassLoader() "
"not found");
goto failed;
}
nxt_java_Thread_setContextClassLoader = (*env)->GetMethodID(env, cls,
"setContextClassLoader", "(Ljava/lang/ClassLoader;)V");
if (nxt_java_Thread_setContextClassLoader == NULL) {
nxt_unit_warn(NULL, "java.lang.Thread.setContextClassLoader() "
"not found");
goto failed;
}
return NXT_UNIT_OK;
failed:
(*env)->DeleteGlobalRef(env, cls);
return NXT_UNIT_ERROR;
}
void
nxt_java_setContextClassLoader(JNIEnv *env, jobject cl)
{
jobject thread;
thread = (*env)->CallStaticObjectMethod(env, nxt_java_Thread_class,
nxt_java_Thread_currentThread);
if (thread == NULL) {
return;
}
(*env)->CallVoidMethod(env, thread, nxt_java_Thread_setContextClassLoader,
cl);
}
jobject
nxt_java_getContextClassLoader(JNIEnv *env)
{
jobject thread;
thread = (*env)->CallStaticObjectMethod(env, nxt_java_Thread_class,
nxt_java_Thread_currentThread);
if (thread == NULL) {
return NULL;
}
return (*env)->CallObjectMethod(env, thread,
nxt_java_Thread_getContextClassLoader);
}

20
src/java/nxt_jni_Thread.h Normal file
View File

@@ -0,0 +1,20 @@
/*
* Copyright (C) NGINX, Inc.
*/
#ifndef _NXT_JAVA_THREAD_H_INCLUDED_
#define _NXT_JAVA_THREAD_H_INCLUDED_
#include <jni.h>
int nxt_java_initThread(JNIEnv *env);
void nxt_java_setContextClassLoader(JNIEnv *env, jobject cl);
jobject nxt_java_getContextClassLoader(JNIEnv *env);
#endif /* _NXT_JAVA_THREAD_H_INCLUDED_ */

View File

@@ -0,0 +1,187 @@
/*
* Copyright (C) NGINX, Inc.
*/
#include <nxt_unit.h>
#include <jni.h>
#include "nxt_jni_URLClassLoader.h"
static jclass nxt_java_URLClassLoader_class;
static jmethodID nxt_java_URLClassLoader_ctor;
static jmethodID nxt_java_URLClassLoader_parent_ctor;
static jmethodID nxt_java_URLClassLoader_loadClass;
static jmethodID nxt_java_URLClassLoader_addURL;
static jclass nxt_java_URL_class;
static jmethodID nxt_java_URL_ctor;
int
nxt_java_initURLClassLoader(JNIEnv *env)
{
jclass cls;
cls = (*env)->FindClass(env, "java/net/URLClassLoader");
if (cls == NULL) {
nxt_unit_warn(NULL, "java.net.URLClassLoader not found");
return NXT_UNIT_ERROR;
}
nxt_java_URLClassLoader_class = (*env)->NewGlobalRef(env, cls);
(*env)->DeleteLocalRef(env, cls);
cls = nxt_java_URLClassLoader_class;
nxt_java_URLClassLoader_ctor = (*env)->GetMethodID(env, cls,
"<init>", "([Ljava/net/URL;)V");
if (nxt_java_URLClassLoader_ctor == NULL) {
nxt_unit_warn(NULL, "java.net.URLClassLoader constructor not found");
goto failed;
}
nxt_java_URLClassLoader_parent_ctor = (*env)->GetMethodID(env, cls,
"<init>", "([Ljava/net/URL;Ljava/lang/ClassLoader;)V");
if (nxt_java_URLClassLoader_ctor == NULL) {
nxt_unit_warn(NULL, "java.net.URLClassLoader constructor not found");
goto failed;
}
nxt_java_URLClassLoader_loadClass = (*env)->GetMethodID(env, cls,
"loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
if (nxt_java_URLClassLoader_loadClass == NULL) {
nxt_unit_warn(NULL, "java.net.URLClassLoader.loadClass not found");
goto failed;
}
nxt_java_URLClassLoader_addURL = (*env)->GetMethodID(env, cls,
"addURL", "(Ljava/net/URL;)V");
if (nxt_java_URLClassLoader_addURL == NULL) {
nxt_unit_warn(NULL, "java.net.URLClassLoader.addURL not found");
goto failed;
}
cls = (*env)->FindClass(env, "java/net/URL");
if (cls == NULL) {
nxt_unit_warn(NULL, "java.net.URL not found");
return NXT_UNIT_ERROR;
}
nxt_java_URL_class = (*env)->NewGlobalRef(env, cls);
(*env)->DeleteLocalRef(env, cls);
cls = nxt_java_URL_class;
nxt_java_URL_ctor = (*env)->GetMethodID(env, cls,
"<init>", "(Ljava/lang/String;)V");
if (nxt_java_URL_ctor == NULL) {
nxt_unit_warn(NULL, "java.net.URL constructor not found");
goto failed;
}
return NXT_UNIT_OK;
failed:
(*env)->DeleteGlobalRef(env, cls);
return NXT_UNIT_ERROR;
}
jobject
nxt_java_newURLClassLoader(JNIEnv *env, int url_count, char **urls)
{
jobjectArray jurls;
jurls = nxt_java_newURLs(env, url_count, urls);
if (jurls == NULL) {
return NULL;
}
return (*env)->NewObject(env, nxt_java_URLClassLoader_class,
nxt_java_URLClassLoader_ctor, jurls);
}
jobject
nxt_java_newURLClassLoader_parent(JNIEnv *env, int url_count, char **urls,
jobject parent)
{
jobjectArray jurls;
jurls = nxt_java_newURLs(env, url_count, urls);
if (jurls == NULL) {
return NULL;
}
return (*env)->NewObject(env, nxt_java_URLClassLoader_class,
nxt_java_URLClassLoader_parent_ctor, jurls,
parent);
}
jobjectArray
nxt_java_newURLs(JNIEnv *env, int url_count, char **urls)
{
int i;
jstring surl;
jobject jurl;
jobjectArray jurls;
jurls = (*env)->NewObjectArray(env, url_count, nxt_java_URL_class, NULL);
if (jurls == NULL) {
return NULL;
}
for (i = 0; i < url_count; i++) {
surl = (*env)->NewStringUTF(env, urls[i]);
if (surl == NULL) {
return NULL;
}
jurl = (*env)->NewObject(env, nxt_java_URL_class, nxt_java_URL_ctor,
surl);
if (jurl == NULL) {
return NULL;
}
(*env)->SetObjectArrayElement(env, jurls, i, jurl);
}
return jurls;
}
jclass
nxt_java_loadClass(JNIEnv *env, jobject cl, const char *name)
{
jstring jname;
jname = (*env)->NewStringUTF(env, name);
if (jname == NULL) {
return NULL;
}
return (*env)->CallObjectMethod(env, cl, nxt_java_URLClassLoader_loadClass,
jname);
}
void
nxt_java_addURL(JNIEnv *env, jobject cl, const char *url)
{
jstring surl;
jobject jurl;
surl = (*env)->NewStringUTF(env, url);
if (surl == NULL) {
return;
}
jurl = (*env)->NewObject(env, nxt_java_URL_class, nxt_java_URL_ctor, surl);
if (jurl == NULL) {
return;
}
(*env)->CallVoidMethod(env, cl, nxt_java_URLClassLoader_addURL, jurl);
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (C) NGINX, Inc.
*/
#ifndef _NXT_JAVA_URLCLASSLOADER_H_INCLUDED_
#define _NXT_JAVA_URLCLASSLOADER_H_INCLUDED_
#include <jni.h>
int nxt_java_initURLClassLoader(JNIEnv *env);
jobject nxt_java_newURLClassLoader(JNIEnv *env, int url_count, char **urls);
jobject nxt_java_newURLClassLoader_parent(JNIEnv *env, int url_count,
char **urls, jobject parent);
jobjectArray nxt_java_newURLs(JNIEnv *env, int url_count, char **urls);
jclass nxt_java_loadClass(JNIEnv *env, jobject cl, const char *name);
void nxt_java_addURL(JNIEnv *env, jobject cl, const char *url);
#endif /* _NXT_JAVA_URLCLASSLOADER_H_INCLUDED_ */