Unable to access static resources in JAR file using embedded Jetty - jar

I basically have the same issue as this question:
Setting Jetty resourcebase to static file embedded in the same jar file
where I am using embedded Jetty, and I want to access some static HTML files in the same JAR file.
Here is how the Jetty server is set up:
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
String res = ApiServer.class.getClassLoader().getResource("res").toExternalForm();
context.setResourceBase(res);
jettyServer = new Server(port);
jettyServer.setHandler(context);
ServletHolder jerseyServlet = context.addServlet(ServletContainer.class, "/*");
jerseyServlet.setInitOrder(0);
// Tells the Jersey Servlet which REST service/class to load.
String classes = new StringJoiner(",")
.add(MyClass1.class.getCanonicalName())
.add(MyClass2.class.getCanonicalName())
.toString();
jerseyServlet.setInitParameter(ServerProperties.PROVIDER_CLASSNAMES, classes);
The folder structure of the JAR is like this:
root
| src (Java classes in here)
| res
| index.html
However it just doesn't work. I have tried to access the URL in various ways:
http://localhost:12345/res/index.html
or
http://localhost:12345/index.html
but neither works.
What am I doing wrong?

The code you have ...
String res = ApiServer.class.getClassLoader().getResource("res").toExternalForm();
context.setResourceBase(res);
Does not work for me, as you cannot use a classloader to obtain a directory reference, only file references. The call ClassLoader.getResource("res") always returns null.
This needs to be fixed first.
Next, your declaration of Jersey is exceedingly greedy.
ServletHolder jerseyServlet = context.addServlet(ServletContainer.class, "/*");
jerseyServlet.setInitOrder(0);
This means that Servlet (ServletContainer.class) is handling 100% of all requests, even requests for static content.
It is impossible for that Servlet, based on your url-pattern, to "not handle" static requests and let Jetty serve those static requests.
Relax this url-pattern, to say /api/* and then you'll be one step closer.
The final thing you need is a DefaultServlet (the component in the Servlet spec, and Jetty that serves static files).
So you'll wind up with the following code ...
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
URL url = ApiServer.class.getClassLoader().getResource("res/index.html");
if (url == null)
throw new FileNotFoundException("Whoops, can't find static resources folder");
URI webroot = url.toURI().resolve("./");
context.setBaseResource(Resource.newResource(webroot));
ServletHolder jerseyServlet = context.addServlet(ServletContainer.class, "/api/*");
jerseyServlet.setInitOrder(0);
// Tells the Jersey Servlet which REST service/class to load.
String classes = new StringJoiner(",")
.add(MyClass1.class.getCanonicalName())
.add(MyClass2.class.getCanonicalName())
.toString();
jerseyServlet.setInitParameter(ServerProperties.PROVIDER_CLASSNAMES, classes);
// always named "default", always last, always on url-pattern "/"
ServletHolder defaultServ = new ServletHolder("default", DefaultServlet.class);
defaultServ.setInitParameter("dirAllowed","true");
context.addServlet(defaultServ,"/");
jettyServer = new Server(port);
jettyServer.setHandler(context);

Related

XUnit Net Core Web API Integration Test: "The ConnectionString property has not been initialized."

Just trying to build an Integration Test project for a NET Core Web API.
So I've followed a few examples, including this one (https://dotnetcorecentral.com/blog/asp-net-core-web-api-integration-testing-with-xunit/) and naturally, I run into issues. When I run the simple GET test I get an exception:
"System.InvalidOperationException : The ConnectionString property has not been initialized."
Any help would be appreciated.
For server = new TestServer(new WebHostBuilder().UseStartup<Startup>());, you need to manually configure the appsettings.json path like
var server = new TestServer(WebHost.CreateDefaultBuilder()
.UseContentRoot(#"D:\Edward\SourceCode\AspNetCore\Tests\IntegrationTestMVC")
// This is the path for project which needs to be test
.UseStartup<Startup>()
);
For a convenience way, I would suggest you try Basic tests with the default WebApplicationFactory.
The WebApplicationFactory constructor infers the app content root path by searching for a WebApplicationFactoryContentRootAttribute on the assembly containing the integration tests with a key equal to the TEntryPoint assembly System.Reflection.Assembly.FullName. In case an attribute with the correct key isn't found, WebApplicationFactory falls back to searching for a solution file (*.sln) and appends the TEntryPoint assembly name to the solution directory. The app root directory (the content root path) is used to discover views and content files.
Reference:How the test infrastructure infers the app content root path
I had to override CreateHostBuilder in my derived WebApplicationFactory in order to add the configuration for the connection string (since it was read from user secrets).
public class CustomApplicationFactory : WebApplicationFactory<Sedab.MemberAuth.Startup>
{
protected override IHostBuilder CreateHostBuilder()
{
var initialData = new List<KeyValuePair<string, string>> {
new KeyValuePair<string, string>("ConnectionStrings:DefaultConnection", "test")
};
return base.CreateHostBuilder().ConfigureHostConfiguration(config => config.AddInMemoryCollection(initialData));
}
}

Tomcat 8 webapp, dynamically add PostResources

I have a webapp which uses a listener to dynamically add servlet instances.
Each servlet instance is defined by a user-defined configuration files, and encapsulates a distinct dataset. Each of these datasets may also include some user-defined static web files (HTML, JPG, CSS etc).
The static resources for each servlet instance are stored outside the webapp folder (to avoid deletion on redeployment), and each servlet instance has a separate folder hierarchy.
In the listener contextInitialized method, I am using
javax.servlet.ServletContext.addServlet
to add each of my servlet instances, and
javax.servlet.ServletRegistration.Dynamic.addMapping
to add the URL mapping for this servlet.
I would like to add other mappings for the static content in external folders.
In Tomcat 7, I extended org.apache.catalina.servlets.DefaultServlet
to change the relativePath to my new document root, but this class
does not work in Tomcat 8 0 - ClassNotFoundException (org.apache.naming.resources.FileDirContext).
Tomcat 8 has a new 'Resources' framework which should make this much more straightforward.
My question is - how can I add a PostResources element to my context dynamically (at web app startup, inside my listener), without editing the web.xml?
In my listener, I should be able to do something like this:
WebResourceRoot root = new StandardRoot(context);
root.createWebResourceSet(WebResourceRoot.ResourceSetType.POST,
"/my/url", "my/filesystem/path", null, "/");
but I cannot figure out how to get the required context (org.apache.catalina.Context), which is a completely different type to the similarly named javax.servlet.ServletContext provided by the ServletContextEvent in the listener contextInitialized method.
Any suggestions?
Thanks.
I think I have figured it out using the MBeanServer.
This seems a bit of a roundabout method though - is there no way of getting to the StandardRoot or StandardContext object from the ServletContext?
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectName;
import javax.servlet.ServletContext;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardEngine;
import org.apache.catalina.Container;
import org.apache.catalina.Server;
import org.apache.catalina.Service;
import org.apache.catalina.WebResourceRoot;
...
private void addPostResources(ServletContext servletContext)
throws Exception /* just for debugging */ {
MBeanServer mbs = MBeanServerFactory.findMBeanServer(null).get(0);
ObjectName name = new ObjectName("Catalina","type","Server");
Server server = (Server)mbs.getAttribute(name, "managedResource");
Service service = server.findService("Catalina");
StandardEngine engine = service.getContainer();
Container hostContainer = engine.findChild(engine.getDefaultHost());
StandardContext standardContext = (StandardContext)hostContainer.findChild(servletContext.getContextPath());
WebResourceRoot root = standardContext.getResources();
root.createWebResourceSet(WebResourceRoot.ResourceSetType.POST,
"/my/url", "my/filesystem/path", null, "/");
}

How to prevent static resources from being handled by front controller servlet which is mapped on /*

I have a servlet which acts as a front controller.
#WebServlet("/*")
However, this also handles CSS and image files. How can I prevent this?
You have 2 options:
Use a more specific URL pattern such as /app/* or *.do and then let all your page requests match this URL pattern. See also Design Patterns web based applications
The same as 1, but you want to hide the servlet mapping from the request URL; you should then put all static resources in a common folder such as /static or /resources and create a filter which checks if the request URL doesn't match it and then forward to the servlet. Here's an example which assumes that your controller servlet is a #WebServlet("/app/*") and that the filter is a #WebFilter("/*") and that all your static resources are in /resources folder.
HttpServletRequest req = (HttpServletRequest) request;
String path = req.getRequestURI().substring(req.getContextPath().length());
if (path.startsWith("/resources/")) {
chain.doFilter(request, response); // Goes to default servlet.
} else {
request.getRequestDispatcher("/app" + path).forward(request, response); // Goes to your controller.
}
See also How to access static resources when mapping a global front controller servlet on /*.
I know this is an old question and I guess #BalusC 's answer probably works fine. But I couldn't modify the URL for the JSF app am working on, so I simply just check for the path and return if it is to static resources:
String path = request.getRequestURI().substring(request.getContextPath().length());
if (path.contains("/resources/")) {
return;
}
This works fine for me.

RequestDispatcher ending in infinite loop

I have two WAR applications and the mode of communication between them is via servlets.
My application (WAR A) opens a child window with the URL of a servlet in another WAR (lets say WAR B).
The servlet (in WAR B) processes the data and should send the processed data back to original application's servlet (i.e WAR A's servlet).
But this process ends in an infinite loop and also the URL parameters sent from WAR-A are null.
Here is the code snippet :
The below script opens a child window with the URL of servlet in WAR-B also passing some URL parameters.
function invokePlugin(invokeURL, custValJSON, meaCompPartJSON) {
window.open(invokeURL + '?custValJSON=' + custValJSON,'');
}
Below is servlet code in WAR-B which extracts the URL parameters and process the data and again send the request back to WAR-A's servlet...
private void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String custValJSON = request.getParameter("custValJSON");
System.out.println("custValJSON : " + custValJSON);
CustomValues custVal = gson.fromJson(custValJSON, CustomValues.class);
if(custVal != null) {
System.out.println("Cust val details : " + custVal.getName());
custVal.setValue("Satya");
}
String destination = "/testPlannerPluginResult";
RequestDispatcher reqDispatch = request.getRequestDispatcher(destination);
request.setAttribute("custValJSON", gson.toJson(custVal));
if(reqDispatch != null) {
reqDispatch.forward(request, response);
}
}
Does anybody have idea on this?
Regards,
Satya
That then just means that the servlet is basically calling itself everytime. I don't immediately see the cause in the information given so far, but apparently the URL which you passed into the getRequestDispatcher() matches the URL of the servlet itself.
I however see a major mistake here:
RequestDispatcher reqDispatch = request.getRequestDispatcher(destination);
request.setAttribute("custValJSON", gson.toJson(custVal));
That can impossibly invoke the servlet which runs in another servlet context (read: another WAR). You need ServletContext#getContext() first to get the other servlet context and then use ServletContext#getRequestDispatcher() to dispatch the request to there.
ServletContext otherServletContext = getServletContext().getContext("/otherContextPath");
RequestDispatcher dispatcher = otherServletContext.getRequestDispatcher(destination);
This only requires that the both WARs are configured to expose the context for sharing. On Tomcat for example, this is to be done by adding crossContext="true" to the <Context>.

How do I change a web reference in a production .NET website?

Our web reference does not seem to be defined in web.config of the website that consumes it. I found that there is a configuration file called "Reference.map" in the "Web References" folder that looks editable, but when I edit them nothing happens. I even renamed the WSDL file in the folder to see if it would get a new one. It did not.
Do I have to do a build just to change the URL of a referenced Web Service?
You can mark a web reference as static or dynamic URL. If you choose dynamic then it will add the URL to the web.config which you can then change in your production environment.
If it is marked as static then it is compiled into the binary and is not changeable without a rebuild.
If it is already dynamic then the code looks for the dynamic URL and then if it can't find it then it uses the default original. Therefore, you can just add an entry into the web config such as:
<applicationSettings>
<MySystem.Properties.Settings>
<setting name="MySystem_MyService" serializeAs="String">
<value>http://mysystem/service.asmx</value>
</setting>
</MySystem.Properties.Settings>
</applicationSettings>
On Compact Framework you have to read the config file on your own class of WebService:
public partial class YourService : System.Web.Services.Protocols.SoapHttpClientProtocol {
/// <remarks/>
public HandTerminalService() {
string appSettings = string.Concat(Assembly.GetExecutingAssembly().GetName().CodeBase, ".config");
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(appSettings);
XmlNode xmlNode = xmlDocument.SelectSingleNode("//configuration/appSettings/add[#key = 'Proxy.YourServiceService']");
if (xmlNode.Attributes["value"].Value != null)
{
this.Url = string.Concat(xmlNode.Attributes["value"].Value, "");
} else
{
this.Url = "http://<IP_or_DNS-Name>:<Port>/YourService.asmx";
}
}

Resources