diff --git a/solon-jakarta-parent/pom.xml b/solon-jakarta-parent/pom.xml index 2e43000e851b39686eaa25f90d13a9d26d032026..29ad4786a7089516917ea33a9f2763685616481a 100644 --- a/solon-jakarta-parent/pom.xml +++ b/solon-jakarta-parent/pom.xml @@ -31,7 +31,7 @@ 12.1.1 - 2.3.19.Final + 2.3.20.Final 2.2.8.Final 16.1.0.Final diff --git a/solon-jakarta-projects/solon-server/solon-server-jetty-jakarta/src/main/java/org/noear/solon/server/jetty/jsp/JspTldLocator.java b/solon-jakarta-projects/solon-server/solon-server-jetty-jakarta/src/main/java/org/noear/solon/server/jetty/jsp/JspTldLocator.java index e50f3fd6267cb6ed628a50ca643dd015371c2dbf..4345667818772d637846df07d2747771f45b5066 100644 --- a/solon-jakarta-projects/solon-server/solon-server-jetty-jakarta/src/main/java/org/noear/solon/server/jetty/jsp/JspTldLocator.java +++ b/solon-jakarta-projects/solon-server/solon-server-jetty-jakarta/src/main/java/org/noear/solon/server/jetty/jsp/JspTldLocator.java @@ -40,7 +40,7 @@ import java.util.Set; * @since 2.6 */ public class JspTldLocator { - static final Logger log = LoggerFactory.getLogger(JspTldLocator.class); + private static final Logger log = LoggerFactory.getLogger(JspTldLocator.class); public static Map createTldInfos(String... dltDirs) throws IOException { HashMap tagLibInfos = new HashMap<>(); diff --git a/solon-jakarta-projects/solon-server/solon-server-tomcat-jakarta/src/test/java/demo3013/WebApp.java b/solon-jakarta-projects/solon-server/solon-server-tomcat-jakarta/src/test/java/demo3013/WebApp.java index 6bfbe04bd36a540d73496a34044874f142a598ff..24091339637b279c534c422ce882a97cacfaf027 100644 --- a/solon-jakarta-projects/solon-server/solon-server-tomcat-jakarta/src/test/java/demo3013/WebApp.java +++ b/solon-jakarta-projects/solon-server/solon-server-tomcat-jakarta/src/test/java/demo3013/WebApp.java @@ -14,6 +14,7 @@ import org.noear.solon.annotation.SolonMain; @SolonMain public class WebApp { public static void main(String[] args) { +// AntCompiler Solon.start(WebApp.class, args); } } diff --git a/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/pom.xml b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/pom.xml index 2b397868262883bc461799742aa7481952623575..fd5d0b2bca55023030e414a76dc6e4c3707bf690 100644 --- a/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/pom.xml +++ b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/pom.xml @@ -51,7 +51,92 @@ + + + + + io.undertow.jastow + jastow + ${undertow.jastow.jakarta.version} + + + log4j + log4j + + + io.undertow + undertow-core + + + io.undertow + undertow-servlet + + + provided + + + + org.jboss.metadata + jboss-metadata-web + ${undertow.jboss-metadata.jakarta.version} + provided + + + org.eclipse.jdt + ecj + 3.43.0 + provided + + + + org.glassfish.expressly + expressly + 6.0.0 + provided + + + jakarta.servlet.jsp.jstl + jakarta.servlet.jsp.jstl-api + 3.0.2 + provided + + + com.sun.el + el-ri + 3.0.4 + provided + + + jakarta.servlet.jsp + jakarta.servlet.jsp-api + 3.1.1 + provided + + + org.projectlombok + lombok + provided + org.noear solon-test @@ -63,6 +148,11 @@ solon-logging-simple test + + org.noear + solon-view-jsp-jakarta + test + \ No newline at end of file diff --git a/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/main/java/org/noear/solon/server/undertow/UndertowServerAddJsp.java b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/main/java/org/noear/solon/server/undertow/UndertowServerAddJsp.java new file mode 100644 index 0000000000000000000000000000000000000000..936eb5557c85c1a6590b9e5e0897ff06277a2485 --- /dev/null +++ b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/main/java/org/noear/solon/server/undertow/UndertowServerAddJsp.java @@ -0,0 +1,72 @@ +/* + * Copyright 2017-2025 noear.org and authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.noear.solon.server.undertow; + +import io.undertow.jsp.HackInstanceManager; +import io.undertow.jsp.JspServletBuilder; +import io.undertow.server.HttpHandler; +import io.undertow.servlet.api.*; +import org.apache.jasper.deploy.JspPropertyGroup; +import org.apache.jasper.deploy.TagLibraryInfo; +import org.noear.solon.server.prop.impl.HttpServerProps; +import org.noear.solon.server.undertow.http.UtHttpContextServletHandler; +import org.noear.solon.server.undertow.jsp.JspResourceManager; +import org.noear.solon.server.undertow.jsp.JspServletEx; +import org.noear.solon.server.undertow.jsp.JspTldLocator; +import org.noear.solon.core.AppClassLoader; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author by: Yukai + * @since: 2019/3/28 15:50 + */ +public class UndertowServerAddJsp extends UndertowServer { + + public UndertowServerAddJsp(HttpServerProps props) { + super(props); + } + + @Override + protected HttpHandler buildHandler() throws Exception { + DeploymentInfo builder = initDeploymentInfo(); + + //添加jsp处理 + String fileRoot = getResourceRoot(); + ServletInfo jspServlet = JspServletEx.createServlet("JSPServlet", "*.jsp"); + jspServlet.addInitParam("fork", "false"); + jspServlet.addInitParam("xpoweredBy", "false"); + jspServlet.addInitParam("development", "true"); + + builder.setResourceManager(new JspResourceManager(AppClassLoader.global(), fileRoot)) + .addServlet(new ServletInfo("ACTServlet", UtHttpContextServletHandler.class).addMapping("/").setAsyncSupported(true)) + .addServlet(jspServlet); + + + //添加taglib支持 + Map tagLibraryMap = JspTldLocator.createTldInfos("WEB-INF", "templates"); + JspServletBuilder.setupDeployment(builder, new HashMap(), tagLibraryMap, new HackInstanceManager()); + + + //开始部署 + final ServletContainer container = ServletContainer.Factory.newInstance(); + DeploymentManager manager = container.addDeployment(builder); + manager.deploy(); + + return manager.start(); + } +} diff --git a/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/main/java/org/noear/solon/server/undertow/integration/UndertowPlugin.java b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/main/java/org/noear/solon/server/undertow/integration/UndertowPlugin.java index b5aa0259a48a89bb68b764305a0f54ea5d553af2..4dda41ef7403c15e7b7339dc8292f7533b0ad227 100644 --- a/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/main/java/org/noear/solon/server/undertow/integration/UndertowPlugin.java +++ b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/main/java/org/noear/solon/server/undertow/integration/UndertowPlugin.java @@ -22,6 +22,7 @@ import org.noear.solon.server.ServerProps; import org.noear.solon.server.prop.impl.HttpServerProps; import org.noear.solon.server.prop.impl.WebSocketServerProps; import org.noear.solon.server.undertow.UndertowServer; +import org.noear.solon.server.undertow.UndertowServerAddJsp; import org.noear.solon.core.*; import org.noear.solon.core.bean.LifecycleBean; import org.noear.solon.core.event.EventBus; @@ -30,6 +31,7 @@ import org.noear.solon.core.util.ClassUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import io.undertow.jsp.JspServletBuilder; import jakarta.servlet.annotation.WebFilter; import jakarta.servlet.annotation.WebListener; import jakarta.servlet.annotation.WebServlet; @@ -85,7 +87,11 @@ public final class UndertowPlugin implements Plugin { final int _port = props.getPort(); final String _name = props.getName(); - _server = new UndertowServer(props); + if (ClassUtil.hasClass(() -> JspServletBuilder.class)) { + _server = new UndertowServerAddJsp(props); + } else { + _server = new UndertowServer(props); + } _server.enableWebSocket(context.app().enableWebSocket()); EventBus.publish(_server); diff --git a/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/main/java/org/noear/solon/server/undertow/jsp/JspResourceManager.java b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/main/java/org/noear/solon/server/undertow/jsp/JspResourceManager.java new file mode 100644 index 0000000000000000000000000000000000000000..46c8604954f301271601639a72695d6e0f75bfd8 --- /dev/null +++ b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/main/java/org/noear/solon/server/undertow/jsp/JspResourceManager.java @@ -0,0 +1,93 @@ +/* + * Copyright 2017-2025 noear.org and authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.noear.solon.server.undertow.jsp; + +import io.undertow.UndertowMessages; +import io.undertow.server.handlers.resource.Resource; +import io.undertow.server.handlers.resource.ResourceChangeListener; +import io.undertow.server.handlers.resource.ResourceManager; +import io.undertow.server.handlers.resource.URLResource; +import org.noear.solon.core.handle.Context; + +import java.io.IOException; +import java.net.URI; +import java.net.URL; + +/** + * JSP资源管理器 + */ +public class JspResourceManager implements ResourceManager { + private final ClassLoader classLoader; + private final String prefix; + + public JspResourceManager(ClassLoader classLoader, String prefix) { + this.classLoader = classLoader; + if (prefix.isEmpty()) { + this.prefix = ""; + } else if (prefix.endsWith("/")) { + this.prefix = prefix; + } else { + this.prefix = prefix + "/"; + } + + } + + @Override + public Resource getResource(String path) throws IOException { + if (path == null || path.endsWith(".jsp") == false) { + return null; + } + + if(Context.current() == null){ + //说明先走的是jsp请求 //禁止 + return null; + } + + String modPath = path; + if (path.startsWith("/")) { + modPath = path.substring(1); + } + + String realPath = this.prefix + modPath; + URL resource = null; + if (realPath.startsWith("file:")) { + resource = URI.create(realPath).toURL(); + } else { + resource = this.classLoader.getResource(realPath); + } + + return resource == null ? null : new URLResource(resource, path); + } + + @Override + public boolean isResourceChangeListenerSupported() { + return false; + } + + @Override + public void registerResourceChangeListener(ResourceChangeListener listener) { + throw UndertowMessages.MESSAGES.resourceChangeListenerNotSupported(); + } + + @Override + public void removeResourceChangeListener(ResourceChangeListener listener) { + throw UndertowMessages.MESSAGES.resourceChangeListenerNotSupported(); + } + + @Override + public void close() throws IOException { + } +} \ No newline at end of file diff --git a/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/main/java/org/noear/solon/server/undertow/jsp/JspServletEx.java b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/main/java/org/noear/solon/server/undertow/jsp/JspServletEx.java new file mode 100644 index 0000000000000000000000000000000000000000..5a09e351c81c6eae2afc07f5f1dc1d92407c4da2 --- /dev/null +++ b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/main/java/org/noear/solon/server/undertow/jsp/JspServletEx.java @@ -0,0 +1,46 @@ +/* + * Copyright 2017-2025 noear.org and authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.noear.solon.server.undertow.jsp; + +import java.io.IOException; + +import org.apache.jasper.servlet.JspServlet; +import org.noear.solon.core.handle.Context; + +import io.undertow.servlet.api.ServletInfo; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; + +public class JspServletEx extends JspServlet { + + public static ServletInfo createServlet(String name, String path) { + ServletInfo servlet = new ServletInfo(name, JspServletEx.class); + servlet.addMapping(path); + servlet.setRequireWelcomeFileMapping(true); + return servlet; + } + + + @Override + public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { + if(Context.current() == null){ + return; + } + + super.service(req, res); + } +} diff --git a/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/main/java/org/noear/solon/server/undertow/jsp/JspTldLocator.java b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/main/java/org/noear/solon/server/undertow/jsp/JspTldLocator.java new file mode 100644 index 0000000000000000000000000000000000000000..f2e069f04cd8972e73c326fdad21a2c737960397 --- /dev/null +++ b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/main/java/org/noear/solon/server/undertow/jsp/JspTldLocator.java @@ -0,0 +1,295 @@ +/* + * Copyright 2017-2025 noear.org and authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.noear.solon.server.undertow.jsp; + + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URL; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamReader; + +import org.apache.jasper.deploy.FunctionInfo; +import org.apache.jasper.deploy.TagAttributeInfo; +import org.apache.jasper.deploy.TagFileInfo; +import org.apache.jasper.deploy.TagInfo; +import org.apache.jasper.deploy.TagLibraryInfo; +import org.apache.jasper.deploy.TagLibraryValidatorInfo; +import org.apache.jasper.deploy.TagVariableInfo; +import org.jboss.annotation.javaee.Icon; +import org.jboss.metadata.javaee.spec.DescriptionGroupMetaData; +import org.jboss.metadata.javaee.spec.ParamValueMetaData; +import org.jboss.metadata.parser.jsp.TldMetaDataParser; +import org.jboss.metadata.parser.util.NoopXMLResolver; +import org.jboss.metadata.web.spec.AttributeMetaData; +import org.jboss.metadata.web.spec.BodyContentType; +import org.jboss.metadata.web.spec.FunctionMetaData; +import org.jboss.metadata.web.spec.TagFileMetaData; +import org.jboss.metadata.web.spec.TagMetaData; +import org.jboss.metadata.web.spec.TldMetaData; +import org.jboss.metadata.web.spec.VariableMetaData; +import org.noear.solon.Solon; +import org.noear.solon.Utils; +import org.noear.solon.core.AppClassLoader; +import org.noear.solon.core.util.ResourceUtil; +import org.noear.solon.core.util.ScanUtil; +import org.noear.solon.core.util.SupplierEx; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + + +/** + * Jsp Tld 定位器 + */ +public class JspTldLocator { + static final Logger log = LoggerFactory.getLogger(JspTldLocator.class); + + public static Map createTldInfos(String... dltDirs) throws IOException { + + List urls = getURLs(); + + HashMap tagLibInfos = new HashMap<>(); + + //加载外部jar包的.tld(也可能包括自己的了) + for (URL url : urls) { + if (url.toString().endsWith(".jar")) { + try { + String file_uri = URLDecoder.decode(url.getFile(), Solon.encoding()); + + JarFile jarFile = new JarFile(file_uri); + + final Enumeration entries = jarFile.entries(); + + while (entries.hasMoreElements()) { + final JarEntry entry = entries.nextElement(); + + if (entry.getName().endsWith(".tld")) { + loadTagLibraryInfo(tagLibInfos, () -> { + JarEntry fileEntry = jarFile.getJarEntry(entry.getName()); + return jarFile.getInputStream(fileEntry); + }); + } + } + } catch (Throwable e) { + log.warn(e.getMessage(), e); + } + } + } + + + //自己的.tld + try { + for (String dltDir : dltDirs) { + ScanUtil.scan(AppClassLoader.global(), dltDir, n -> n.endsWith(".tld")).forEach((uri) -> { + loadTagLibraryInfo(tagLibInfos, () -> ResourceUtil.getResource(uri).openStream()); + }); + } + } catch (Throwable e) { + log.warn(e.getMessage(), e); + } + + return tagLibInfos; + } + + static List getURLs(){ + List urls = new ArrayList<>(); + + String classPath = System.getProperty("java.class.path"); + + if (classPath != null) { + String separator = System.getProperty("path.separator"); + if(Utils.isEmpty(separator)){ + separator=":"; + } + + String[] list = classPath.split(separator); + for (String uri : list) { + // + //加载系统java包,用于后续加载使用 + // + if (uri.endsWith(".jar") || uri.indexOf(".jar ") > 0) { + try { + if (uri.startsWith("/")) { + uri = "file:" + uri; + } + + URL url = URI.create(uri).toURL(); + urls.add(url); + } catch (Throwable ex) { + //ex.printStackTrace(); + } + } + } + } + + return urls; + } + + static void loadTagLibraryInfo(HashMap tagLibInfos, SupplierEx supplier) { + InputStream is = null; + + try { + is = supplier.get(); + + final XMLInputFactory inputFactory = XMLInputFactory.newInstance(); + inputFactory.setXMLResolver(NoopXMLResolver.create()); + // 添加XML解析器属性,使其更宽松地处理未知元素 +// inputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false); +// inputFactory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, false); +// inputFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, true); + + XMLStreamReader xmlReader = inputFactory.createXMLStreamReader(is); + TldMetaData tldMetadata = TldMetaDataParser.parse(xmlReader); + TagLibraryInfo taglibInfo = getTagLibraryInfo(tldMetadata); + if (!tagLibInfos.containsKey(taglibInfo.getUri())) { + tagLibInfos.put(taglibInfo.getUri(), taglibInfo); + } + + } catch (Throwable e) { + log.warn(e.getMessage(), e); + } finally { + try { + if (is != null) { + is.close(); + } + } catch (IOException ignore) { + } + } + } + + static TagLibraryInfo getTagLibraryInfo(TldMetaData tldMetaData) { + TagLibraryInfo tagLibraryInfo = new TagLibraryInfo(); + tagLibraryInfo.setTlibversion(tldMetaData.getTlibVersion()); + if (tldMetaData.getJspVersion() == null) { + tagLibraryInfo.setJspversion(tldMetaData.getVersion()); + } else { + tagLibraryInfo.setJspversion(tldMetaData.getJspVersion()); + } + tagLibraryInfo.setShortname(tldMetaData.getShortName()); + tagLibraryInfo.setUri(tldMetaData.getUri()); + if (tldMetaData.getDescriptionGroup() != null) { + tagLibraryInfo.setInfo(tldMetaData.getDescriptionGroup().getDescription()); + } + // Validator + if (tldMetaData.getValidator() != null) { + TagLibraryValidatorInfo tagLibraryValidatorInfo = new TagLibraryValidatorInfo(); + tagLibraryValidatorInfo.setValidatorClass(tldMetaData.getValidator().getValidatorClass()); + if (tldMetaData.getValidator().getInitParams() != null) { + for (ParamValueMetaData paramValueMetaData : tldMetaData.getValidator().getInitParams()) { + tagLibraryValidatorInfo.addInitParam(paramValueMetaData.getParamName(), paramValueMetaData.getParamValue()); + } + } + tagLibraryInfo.setValidator(tagLibraryValidatorInfo); + } + // Tag + if (tldMetaData.getTags() != null) { + for (TagMetaData tagMetaData : tldMetaData.getTags()) { + TagInfo tagInfo = new TagInfo(); + tagInfo.setTagName(tagMetaData.getName()); + tagInfo.setTagClassName(tagMetaData.getTagClass()); + tagInfo.setTagExtraInfo(tagMetaData.getTeiClass()); + BodyContentType bc = tagMetaData.getBodyContent(); + if (bc != null) {//&& bc != BodyContentType.empty + tagInfo.setBodyContent(bc.toString()); + } + tagInfo.setDynamicAttributes(tagMetaData.getDynamicAttributes()); + // Description group + if (tagMetaData.getDescriptionGroup() != null) { + DescriptionGroupMetaData descriptionGroup = tagMetaData.getDescriptionGroup(); + if (descriptionGroup.getIcons() != null && descriptionGroup.getIcons().value() != null + && (descriptionGroup.getIcons().value().length > 0)) { + Icon icon = descriptionGroup.getIcons().value()[0]; + tagInfo.setLargeIcon(icon.largeIcon()); + tagInfo.setSmallIcon(icon.smallIcon()); + } + tagInfo.setInfoString(descriptionGroup.getDescription()); + tagInfo.setDisplayName(descriptionGroup.getDisplayName()); + } + // Variable + if (tagMetaData.getVariables() != null) { + for (VariableMetaData variableMetaData : tagMetaData.getVariables()) { + TagVariableInfo tagVariableInfo = new TagVariableInfo(); + tagVariableInfo.setNameGiven(variableMetaData.getNameGiven()); + tagVariableInfo.setNameFromAttribute(variableMetaData.getNameFromAttribute()); + tagVariableInfo.setClassName(variableMetaData.getVariableClass()); + tagVariableInfo.setDeclare(variableMetaData.getDeclare()); + if (variableMetaData.getScope() != null) { + tagVariableInfo.setScope(variableMetaData.getScope().toString()); + } + tagInfo.addTagVariableInfo(tagVariableInfo); + } + } + // Attribute + if (tagMetaData.getAttributes() != null) { + for (AttributeMetaData attributeMetaData : tagMetaData.getAttributes()) { + TagAttributeInfo ari = new TagAttributeInfo(); + ari.setName(attributeMetaData.getName()); + ari.setType(attributeMetaData.getType()); + ari.setReqTime(attributeMetaData.getRtexprvalue()); + ari.setRequired(attributeMetaData.getRequired()); + ari.setFragment(attributeMetaData.getFragment()); + if (attributeMetaData.getDeferredValue() != null) { + ari.setDeferredValue("true"); + ari.setExpectedTypeName(attributeMetaData.getDeferredValue().getType()); + } else { + ari.setDeferredValue("false"); + } + if (attributeMetaData.getDeferredMethod() != null) { + ari.setDeferredMethod("true"); + ari.setMethodSignature(attributeMetaData.getDeferredMethod().getMethodSignature()); + } else { + ari.setDeferredMethod("false"); + } + tagInfo.addTagAttributeInfo(ari); + } + } + tagLibraryInfo.addTagInfo(tagInfo); + } + } + // Tag files + if (tldMetaData.getTagFiles() != null) { + for (TagFileMetaData tagFileMetaData : tldMetaData.getTagFiles()) { + TagFileInfo tfi = new TagFileInfo(); + tfi.setName(tagFileMetaData.getName()); + tfi.setPath(tagFileMetaData.getPath()); + tagLibraryInfo.addTagFileInfo(tfi); + } + } + // Function + if (tldMetaData.getFunctions() != null) { + for (FunctionMetaData functionMetaData : tldMetaData.getFunctions()) { + FunctionInfo fi = new FunctionInfo(); + fi.setName(functionMetaData.getName()); + fi.setFunctionClass(functionMetaData.getFunctionClass()); + fi.setFunctionSignature(functionMetaData.getFunctionSignature()); + tagLibraryInfo.addFunctionInfo(fi); + } + } + + return tagLibraryInfo; + } +} \ No newline at end of file diff --git a/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/demo3013/Config.java b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/demo3013/Config.java new file mode 100644 index 0000000000000000000000000000000000000000..37e9c92a039ac95ca8a473cb5fea986ed3429deb --- /dev/null +++ b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/demo3013/Config.java @@ -0,0 +1,4 @@ +package demo3013; + +public class Config { +} diff --git a/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/demo3013/WebApp.java b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/demo3013/WebApp.java new file mode 100644 index 0000000000000000000000000000000000000000..1041154829a56bf26f9636d8c85c97185babbbd9 --- /dev/null +++ b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/demo3013/WebApp.java @@ -0,0 +1,20 @@ +package demo3013; + +import org.noear.solon.Solon; +import org.noear.solon.annotation.SolonMain; + +/** + * + * //资源路径说明(不用配置) + * resources/app.properties(或 app.yml) 为应用配置文件 + * resources/WEB-INF/static/ 为静态文件根目标 + * resources/WEB-INF/templates/ 为视图文件根目标(支持多视图共存) + * + * */ +@SolonMain +public class WebApp { + public static void main(String[] args) { +// System.setProperty("jdk.module.illegalAccess", "permit"); + Solon.start(WebApp.class, args); + } +} diff --git a/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/demo3013/controller/HelloworldController.java b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/demo3013/controller/HelloworldController.java new file mode 100644 index 0000000000000000000000000000000000000000..c0a3387cc8fe29438bde19bb0073c884ea7262fe --- /dev/null +++ b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/demo3013/controller/HelloworldController.java @@ -0,0 +1,57 @@ +package demo3013.controller; + +import org.noear.solon.annotation.Controller; +import org.noear.solon.annotation.Inject; +import org.noear.solon.annotation.Mapping; +import org.noear.solon.core.handle.Context; +import org.noear.solon.core.handle.ModelAndView; +import demo3013.model.UserModel; + +@Controller +public class HelloworldController { + + @Inject("${custom.user}") + protected String user; + + @Mapping("/helloworld") + public Object helloworld(Context ctx){ + UserModel m = new UserModel(); + m.setId(10); + m.setName("刘之西东"); + m.setSex(1); + + ModelAndView vm = new ModelAndView("helloworld.jsp"); + + vm.put("title","demo"); + vm.put("message","hello world!"); + + vm.put("m",m); + + vm.put("user", user); + + vm.put("ctx",ctx); + + return vm; + } + + @Mapping("/hellotld") + public Object hellotld(Context ctx){ + UserModel m = new UserModel(); + m.setId(11); + m.setName("刘之西东2"); + m.setSex(2); + + ModelAndView vm = new ModelAndView("helloworld2.jsp"); + + vm.put("title","demo2"); + vm.put("message","hello world2!"); + + vm.put("m",m); + + vm.put("user", user); + + vm.put("ctx",ctx); + + return vm; + } +} diff --git a/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/demo3013/controller/HomeController.java b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/demo3013/controller/HomeController.java new file mode 100644 index 0000000000000000000000000000000000000000..e24b1776ef4cdd2e7a9bc7f801feca1299da69dd --- /dev/null +++ b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/demo3013/controller/HomeController.java @@ -0,0 +1,12 @@ +package demo3013.controller; + +import org.noear.solon.annotation.Controller; +import org.noear.solon.annotation.Mapping; + +@Controller +public class HomeController { + @Mapping(value = "/", produces = "text/html;charset=utf-8") + public String home(){ + return "/helloworld"; + } +} diff --git a/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/demo3013/dso/service/DemoService.java b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/demo3013/dso/service/DemoService.java new file mode 100644 index 0000000000000000000000000000000000000000..133c80dbb5cf5048b6f44eaa936a2e7fdb8f46bf --- /dev/null +++ b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/demo3013/dso/service/DemoService.java @@ -0,0 +1,7 @@ +package demo3013.dso.service; + +/** + * @author noear 2022/1/6 created + */ +public class DemoService { +} diff --git a/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/demo3013/model/UserModel.java b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/demo3013/model/UserModel.java new file mode 100644 index 0000000000000000000000000000000000000000..c14a819f62289205869da96519196721e8893f05 --- /dev/null +++ b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/demo3013/model/UserModel.java @@ -0,0 +1,11 @@ +package demo3013.model; + +import lombok.Data; + +@Data +public class UserModel { + private long id; + private String name; + private int sex; + private String label; +} diff --git a/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/demo3013/widget/FooterTag.java b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/demo3013/widget/FooterTag.java new file mode 100644 index 0000000000000000000000000000000000000000..4edda7804e814ad803f0e016066dbcf6a50a7570 --- /dev/null +++ b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/demo3013/widget/FooterTag.java @@ -0,0 +1,27 @@ +package demo3013.widget; + +import jakarta.servlet.jsp.JspException; +import jakarta.servlet.jsp.tagext.TagSupport; + +public class FooterTag extends TagSupport { + @Override + public int doStartTag() throws JspException { + try { + StringBuffer sb = new StringBuffer(); + sb.append("
"); + sb.append("我是自定义标签,FooterTag"); + sb.append("
"); + pageContext.getOut().write(sb.toString()); + } + catch (Exception e){ + e.printStackTrace(); + } + + return super.doStartTag(); + } + + @Override + public int doEndTag() throws JspException { + return super.doEndTag(); + } +} \ No newline at end of file diff --git a/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/features/tomcat/App.java b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/features/tomcat/App.java new file mode 100644 index 0000000000000000000000000000000000000000..4ac41ee85c4207a812e955c27387928ae96c3869 --- /dev/null +++ b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/features/tomcat/App.java @@ -0,0 +1,49 @@ +package features.tomcat; + +import org.noear.solon.Solon; +import org.noear.solon.annotation.Controller; +import org.noear.solon.annotation.Mapping; +import org.noear.solon.annotation.Param; +import org.noear.solon.annotation.SolonMain; +import org.noear.solon.core.handle.Context; + +/** + * @author noear 2024/10/1 created + */ +@SolonMain +@Controller +public class App { + public static void main(String[] args) { + Solon.start(ServerTest.class, args); + } + + @Mapping("hello") + public String hello() { + return "hello"; + } + + @Mapping("async") + public void async(Context ctx) { + try { + ctx.asyncStart(); + ctx.output("async"); + } finally { + ctx.asyncComplete(); + } + } + + @Mapping("async_timeout") + public void async_timeout(Context ctx) { + ctx.asyncStart(100L, null); + } + + @Mapping("session") + public Object session(Context ctx, @Param(value = "name",required = false) String name) { + if (name == null) { + return ctx.session("name"); + } else { + ctx.sessionSet("name", name); + return name; + } + } +} diff --git a/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/features/tomcat/ContentTypeHandler.java b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/features/tomcat/ContentTypeHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..d476d73469a0dfabd76ad7055761524c027f6fb5 --- /dev/null +++ b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/features/tomcat/ContentTypeHandler.java @@ -0,0 +1,20 @@ +package features.tomcat; + +import org.noear.solon.annotation.Component; +import org.noear.solon.annotation.Mapping; +import org.noear.solon.core.handle.Context; +import org.noear.solon.core.handle.Handler; + +/** + * + * @author noear 2025/11/4 created + * + */ +@Mapping("ct0") +@Component +public class ContentTypeHandler implements Handler { + @Override + public void handle(Context ctx) throws Throwable { + ctx.output("hello"); + } +} diff --git a/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/features/tomcat/ServerTest.java b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/features/tomcat/ServerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f8a5350530bcc38d40c083693593c87d693c0bf5 --- /dev/null +++ b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/java/features/tomcat/ServerTest.java @@ -0,0 +1,53 @@ +package features.tomcat; + +import org.junit.jupiter.api.Test; +import org.noear.solon.core.util.MimeType; +import org.noear.solon.core.util.MultiMap; +import org.noear.solon.net.http.HttpResponse; +import org.noear.solon.test.HttpTester; +import org.noear.solon.test.SolonTest; + +@SolonTest(App.class) +public class ServerTest extends HttpTester { + + @Test + public void test() throws Exception { + assert "hello".equals(path("/hello").get()); + } + + @Test + public void async() throws Exception { + assert "async".equals(path("/async").get()); + } + + @Test + public void async_timeout() throws Exception { + assert 500 == path("/async_timeout").head(); + } + + @Test + public void session() throws Exception { + MultiMap cookies = new MultiMap<>(); + try (HttpResponse resp = path("/session?name=n1").exec("GET")) { + assert "n1".equals(resp.bodyAsString()); + + for (String cookie : resp.cookies()) { + String[] nameAndValues = cookie.split(";")[0].split("="); + cookies.add(nameAndValues[0], nameAndValues[1]); + } + } + + assert "n1".equals(path("/session").cookies(cookies).get()); + } + + @Test + public void ct0() { + assert path("/ct0").exec("GET").contentType() == null; + } + + @Test + public void ct1() { + assert path("/hello").exec("GET").contentType() + .startsWith(MimeType.TEXT_PLAIN_VALUE); + } +} \ No newline at end of file diff --git a/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/resources/app.properties b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/resources/app.properties new file mode 100644 index 0000000000000000000000000000000000000000..86a341a08ed77496740b48c6a7ef53fc2b70a4a3 --- /dev/null +++ b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/resources/app.properties @@ -0,0 +1,15 @@ +server.port=8182 +server.request.maxRequestSize=2Mb +server.session.timeout=3600 + +solon.app.name=demoapp +solon.app.group=demo + +solon.output.meta=1 + +custom.user=world + +#solon.view.mapping.vm: org.noear.solon.view.jsp.JspRender +solon.view.mapping.jsp=org.noear.solon.view.jsp.JspRender + +solon.view.prefix=/templates diff --git a/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/resources/static/jinjin.htm b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/resources/static/jinjin.htm new file mode 100644 index 0000000000000000000000000000000000000000..7660a18b04c6b50e29938871d16f7a82b3251dcf --- /dev/null +++ b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/resources/static/jinjin.htm @@ -0,0 +1,10 @@ + + + + + Title + + +我就是静静!(静态文件的静) + + \ No newline at end of file diff --git a/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/resources/templates/helloworld.jsp b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/resources/templates/helloworld.jsp new file mode 100644 index 0000000000000000000000000000000000000000..5a33331dc521e5791f6c78165e5f3d7f1d07ce03 --- /dev/null +++ b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/resources/templates/helloworld.jsp @@ -0,0 +1,18 @@ +<%@ page import="java.util.Random" %> +<%@ page contentType="text/html;charset=UTF-8" language="java" %> + + + ${title} + + +
+ page path: ${ctx.path()} +
+
+ properties: custom.user :${user} +
+
+ ${m.name} : ${message} (我想静静) +
+ + \ No newline at end of file diff --git a/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/resources/templates/helloworld2.jsp b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/resources/templates/helloworld2.jsp new file mode 100644 index 0000000000000000000000000000000000000000..1793a6aa8bb1786ba2305b227b5b52fc92261bd9 --- /dev/null +++ b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/resources/templates/helloworld2.jsp @@ -0,0 +1,20 @@ +<%@ page import="java.util.Random" %> +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ taglib prefix="ct" uri="/tags" %> + + + ${title} + + +
+ page path: ${ctx.path()} +
+
+ properties: custom.user :${user} +
+
+ ${m.name} : ${message} (我想静静) +
+ + + \ No newline at end of file diff --git a/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/resources/templates/tags.tld b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/resources/templates/tags.tld new file mode 100644 index 0000000000000000000000000000000000000000..6042a560c96b9de68cb18f88e8e0c7bda4132cf4 --- /dev/null +++ b/solon-jakarta-projects/solon-server/solon-server-undertow-jakarta/src/test/resources/templates/tags.tld @@ -0,0 +1,18 @@ + + + 自定义标签库 + 1.1 + ct + /tags + + + footer + demo3013.widget.FooterTag + empty + + + \ No newline at end of file