Restlet:无法通过目录定位静态文件
Restlet: can't locate static files through Directory
事实证明,通过 Restlet 提供静态文件比想象中的要难。我怀疑与上下文初始化有关的一些深层问题,但我可能是错的。基本上,我还没有能够通过 Directory
获得任何实际内容,尽管所有路由和所有 classic restlets 都工作得很好。
核心问题看起来像在日志消息中显示的那样:
WARNING: No client dispatcher is available on the context. Can't get the target URI: war:///
我尝试了很多不同的基础 URL,其中 none 似乎有所不同。当我调试时,果然 getClientDispatcher()
返回 null。
当我修改 Spring 初始化(错误地)以使用原始上下文而不是子上下文时,我没有得到这个 warning,并且显示了目录列表,但它也显示了大约 7 条 SEVERE
条关于存在安全风险的消息。
我的 Spring XML 看起来像这样:
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<bean id="trackerComponent" class="org.restlet.ext.spring.SpringComponent">
<property name="defaultTarget" ref="trackerApplication" />
</bean>
<bean id="trackerComponentChildContext" class="org.restlet.Context">
<lookup-method name="createChildContext" bean="trackerComponent.context" />
</bean>
<bean id="trackerApplication" class="ca.uhnresearch.pughlab.tracker.application.TrackerApplication">
<property name="root" ref="router" />
</bean>
<!-- Define the router -->
<bean name="router" class="org.restlet.ext.spring.SpringRouter">
<constructor-arg ref="trackerComponentChildContext" />
<property name="attachments">
<map>
<entry key="/" value-ref="staticsDirectory" />
</map>
</property>
</bean>
<bean id="staticsDirectory" class="ca.uhnresearch.pughlab.tracker.resource.SpringDirectory">
<constructor-arg ref="router" />
<constructor-arg value="war:///" />
<property name="listingAllowed" value="true" />
</bean>
...
我的 class Spring 目录只是一个包装器,用于提供来自父 Router
的 Context
,就像其他 Spring 助手一样,它似乎工作得很好:传递相同的上下文——并且 none 个子上下文有一个 getClientDispatcher()
可以工作。
如果需要,这里是 web.xml
的一些相关部分。我不完全确定为什么我需要添加 FILE 以允许客户端访问(毕竟我想通过 Web 容器提供服务)但我确实尝试了一个文件:URL,但没有成功我能看到的任何差异,除了我必须在不寻常的地方添加额外的设置。
<servlet>
<servlet-name>tracker</servlet-name>
<servlet-class>org.restlet.ext.spring.SpringServerServlet</servlet-class>
<init-param>
<param-name>org.restlet.clients</param-name>
<param-value>HTTP HTTPS FILE</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>tracker</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
我真的很感激关于这个的建议——我实际上花了更多的时间来尝试显示 index.html 比我在所有 Restlets 上花费的时间加起来还要多,所以任何想法或指示都会非常欢迎。
更新
Thierry 的回复很有帮助,但我仍然遇到一些问题,仍然无法提供静态文件。唯一的变化是我没有 test.CustomSpringServerServlet
—— 而且我还不完全清楚我需要一个,因为额外的 init-param
似乎被默认接受。
当 运行 一个 Jetty JAR 文件 (mvn jetty:run-war
) 我现在得到一个堆栈回溯:
WARNING: Exception or error caught in status service
java.lang.NoSuchMethodError: org.restlet.Context.getClientDispatcher()Lorg/restlet/Restlet;
at ca.uhnresearch.pughlab.tracker.restlets.DirectoryFactoryBean.getContext(DirectoryFactoryBean.java:16)
at org.restlet.Restlet.handle(Restlet.java:220)
at org.restlet.resource.Finder.handle(Finder.java:449)
at org.restlet.resource.Directory.handle(Directory.java:245)
at org.restlet.routing.Filter.doHandle(Filter.java:156)
...
这个堆栈回溯很容易是我更新的结果——我现在使用的是 Restlets 2.3.1 和 Spring 3.1.4,因为它与之前的 warning 非常相似.
奇怪的是,我没有从 mvn jetty:run
那里得到这个,矛盾的是它现在在顶层提供目录列表,但仍然拒绝提供任何文件。这样就确认了 WAR 协议能够访问某些东西。 war:// 协议没有大量记录,所以我猜测它只是从 war 内容中提供服务,而且它肯定还不能做到这一点。
我仍然希望 file://
协议能够工作——老实说,任何静态文件的服务都很棒,我已经不在乎了。但是,尝试仍然会导致错误:
WARNING: The protocol used by this request is not declared in the list of client connectors. (FILE). In case you are using an instance of the Component class, check its "clients" property.
我不太明白,因为它在 web.xml
的客户列表中,而且没有其他地方我可以把它说得通。
事实证明这已经够难了,我觉得也许我应该拼凑一个 Github 回购协议供人们使用。
编辑:我完全更新了我的答案以描述如何解决您的问题
我对你的问题进行了更深入的调查,我可以重现它。事实上,这是在Spring中配置目录本身时的问题。实际上,它位于负责带客户端调度程序的上下文中。
这里有更多详细信息。事实上,当在默认或 Spring servlet 中创建 Restlet 组件时,客户端协议 WAR
会自动注册。请参阅方法 ServerServlet#init(Component)
。所以在组件的上下文中设置相应的客户端调度程序。
所以实例化的时候需要在Directory
的构造函数中传递这个context。我修改了 Spring 配置以展示如何解决您的问题:
public class DirectoryFactoryBean implements FactoryBean<Directory> {
private Component component;
@Override
public Directory getObject() throws Exception {
Directory directory = new Directory(component.getContext(), "war:///") {
@Override
public Context getContext() {
// Display if the client dispatcher is correctly set!
System.out.println(">> getContext().getClientDispatcher() = "+super.getContext().getClientDispatcher());
return super.getContext();
}
};
directory.setListingAllowed(true);
return directory;
}
@Override
public Class<?> getObjectType() {
return Directory.class;
}
@Override
public boolean isSingleton() {
return true;
}
public Component getComponent() {
return component;
}
public void setComponent(Component component) {
this.component = component;
}
}
这里是 class ``:
的内容
public class CustomSpringServerServlet extends SpringServerServlet {
@Override
protected void init(Application application) {
super.init(application);
}
@Override
protected void init(Component component) {
super.init(component);
ClientList list = component.getClients();
for (Client client : list) {
System.out.println(">> client = "+client);
}
}
}
这里是 web.xml
文件中的配置:
<web-app>
<context-param>
<param-name>org.restlet.application</param-name>
<param-value>org.restlet.tutorial.MyApplication</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>tracker</servlet-name>
<servlet-class>test.CustomSpringServerServlet</servlet-class>
<init-param>
<param-name>org.restlet.component</param-name>
<param-value>myComponent</param-value>
</init-param>
<init-param>
<param-name>org.restlet.clients</param-name>
<param-value>HTTP HTTPS FILE</param-value>
</init-param>
</servlet>
</web-app>
这里是对应的Spring配置:
<bean id="myComponent" class="org.restlet.ext.spring.SpringComponent">
<property name="defaultTarget" ref="router" />
</bean>
<bean name="router" class="org.restlet.ext.spring.SpringRouter">
<property name="attachments">
<map>
<entry key="/dir" value-ref="staticsDirectory" />
</map>
</property>
</bean>
<bean id="staticsDirectory" class="test.DirectoryFactoryBean">
<property name="component" ref="myComponent" />
</bean>
希望对您有所帮助,
蒂埃里
事实证明,通过 Restlet 提供静态文件比想象中的要难。我怀疑与上下文初始化有关的一些深层问题,但我可能是错的。基本上,我还没有能够通过 Directory
获得任何实际内容,尽管所有路由和所有 classic restlets 都工作得很好。
核心问题看起来像在日志消息中显示的那样:
WARNING: No client dispatcher is available on the context. Can't get the target URI: war:///
我尝试了很多不同的基础 URL,其中 none 似乎有所不同。当我调试时,果然 getClientDispatcher()
返回 null。
当我修改 Spring 初始化(错误地)以使用原始上下文而不是子上下文时,我没有得到这个 warning,并且显示了目录列表,但它也显示了大约 7 条 SEVERE
条关于存在安全风险的消息。
我的 Spring XML 看起来像这样:
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<bean id="trackerComponent" class="org.restlet.ext.spring.SpringComponent">
<property name="defaultTarget" ref="trackerApplication" />
</bean>
<bean id="trackerComponentChildContext" class="org.restlet.Context">
<lookup-method name="createChildContext" bean="trackerComponent.context" />
</bean>
<bean id="trackerApplication" class="ca.uhnresearch.pughlab.tracker.application.TrackerApplication">
<property name="root" ref="router" />
</bean>
<!-- Define the router -->
<bean name="router" class="org.restlet.ext.spring.SpringRouter">
<constructor-arg ref="trackerComponentChildContext" />
<property name="attachments">
<map>
<entry key="/" value-ref="staticsDirectory" />
</map>
</property>
</bean>
<bean id="staticsDirectory" class="ca.uhnresearch.pughlab.tracker.resource.SpringDirectory">
<constructor-arg ref="router" />
<constructor-arg value="war:///" />
<property name="listingAllowed" value="true" />
</bean>
...
我的 class Spring 目录只是一个包装器,用于提供来自父 Router
的 Context
,就像其他 Spring 助手一样,它似乎工作得很好:传递相同的上下文——并且 none 个子上下文有一个 getClientDispatcher()
可以工作。
如果需要,这里是 web.xml
的一些相关部分。我不完全确定为什么我需要添加 FILE 以允许客户端访问(毕竟我想通过 Web 容器提供服务)但我确实尝试了一个文件:URL,但没有成功我能看到的任何差异,除了我必须在不寻常的地方添加额外的设置。
<servlet>
<servlet-name>tracker</servlet-name>
<servlet-class>org.restlet.ext.spring.SpringServerServlet</servlet-class>
<init-param>
<param-name>org.restlet.clients</param-name>
<param-value>HTTP HTTPS FILE</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>tracker</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
我真的很感激关于这个的建议——我实际上花了更多的时间来尝试显示 index.html 比我在所有 Restlets 上花费的时间加起来还要多,所以任何想法或指示都会非常欢迎。
更新
Thierry 的回复很有帮助,但我仍然遇到一些问题,仍然无法提供静态文件。唯一的变化是我没有 test.CustomSpringServerServlet
—— 而且我还不完全清楚我需要一个,因为额外的 init-param
似乎被默认接受。
当 运行 一个 Jetty JAR 文件 (mvn jetty:run-war
) 我现在得到一个堆栈回溯:
WARNING: Exception or error caught in status service
java.lang.NoSuchMethodError: org.restlet.Context.getClientDispatcher()Lorg/restlet/Restlet;
at ca.uhnresearch.pughlab.tracker.restlets.DirectoryFactoryBean.getContext(DirectoryFactoryBean.java:16)
at org.restlet.Restlet.handle(Restlet.java:220)
at org.restlet.resource.Finder.handle(Finder.java:449)
at org.restlet.resource.Directory.handle(Directory.java:245)
at org.restlet.routing.Filter.doHandle(Filter.java:156)
...
这个堆栈回溯很容易是我更新的结果——我现在使用的是 Restlets 2.3.1 和 Spring 3.1.4,因为它与之前的 warning 非常相似.
奇怪的是,我没有从 mvn jetty:run
那里得到这个,矛盾的是它现在在顶层提供目录列表,但仍然拒绝提供任何文件。这样就确认了 WAR 协议能够访问某些东西。 war:// 协议没有大量记录,所以我猜测它只是从 war 内容中提供服务,而且它肯定还不能做到这一点。
我仍然希望 file://
协议能够工作——老实说,任何静态文件的服务都很棒,我已经不在乎了。但是,尝试仍然会导致错误:
WARNING: The protocol used by this request is not declared in the list of client connectors. (FILE). In case you are using an instance of the Component class, check its "clients" property.
我不太明白,因为它在 web.xml
的客户列表中,而且没有其他地方我可以把它说得通。
事实证明这已经够难了,我觉得也许我应该拼凑一个 Github 回购协议供人们使用。
编辑:我完全更新了我的答案以描述如何解决您的问题
我对你的问题进行了更深入的调查,我可以重现它。事实上,这是在Spring中配置目录本身时的问题。实际上,它位于负责带客户端调度程序的上下文中。
这里有更多详细信息。事实上,当在默认或 Spring servlet 中创建 Restlet 组件时,客户端协议 WAR
会自动注册。请参阅方法 ServerServlet#init(Component)
。所以在组件的上下文中设置相应的客户端调度程序。
所以实例化的时候需要在Directory
的构造函数中传递这个context。我修改了 Spring 配置以展示如何解决您的问题:
public class DirectoryFactoryBean implements FactoryBean<Directory> {
private Component component;
@Override
public Directory getObject() throws Exception {
Directory directory = new Directory(component.getContext(), "war:///") {
@Override
public Context getContext() {
// Display if the client dispatcher is correctly set!
System.out.println(">> getContext().getClientDispatcher() = "+super.getContext().getClientDispatcher());
return super.getContext();
}
};
directory.setListingAllowed(true);
return directory;
}
@Override
public Class<?> getObjectType() {
return Directory.class;
}
@Override
public boolean isSingleton() {
return true;
}
public Component getComponent() {
return component;
}
public void setComponent(Component component) {
this.component = component;
}
}
这里是 class ``:
的内容public class CustomSpringServerServlet extends SpringServerServlet {
@Override
protected void init(Application application) {
super.init(application);
}
@Override
protected void init(Component component) {
super.init(component);
ClientList list = component.getClients();
for (Client client : list) {
System.out.println(">> client = "+client);
}
}
}
这里是 web.xml
文件中的配置:
<web-app>
<context-param>
<param-name>org.restlet.application</param-name>
<param-value>org.restlet.tutorial.MyApplication</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>tracker</servlet-name>
<servlet-class>test.CustomSpringServerServlet</servlet-class>
<init-param>
<param-name>org.restlet.component</param-name>
<param-value>myComponent</param-value>
</init-param>
<init-param>
<param-name>org.restlet.clients</param-name>
<param-value>HTTP HTTPS FILE</param-value>
</init-param>
</servlet>
</web-app>
这里是对应的Spring配置:
<bean id="myComponent" class="org.restlet.ext.spring.SpringComponent">
<property name="defaultTarget" ref="router" />
</bean>
<bean name="router" class="org.restlet.ext.spring.SpringRouter">
<property name="attachments">
<map>
<entry key="/dir" value-ref="staticsDirectory" />
</map>
</property>
</bean>
<bean id="staticsDirectory" class="test.DirectoryFactoryBean">
<property name="component" ref="myComponent" />
</bean>
希望对您有所帮助, 蒂埃里