I'm running an embedded Jetty 8 server that loads a few *.war files at startup:
for (File aWebAppDirectory : aWebAppDirectories) {
if (aWebAppDirectory.exists() && aWebAppDirectory.isDirectory()) {
for (File warFile : aWebAppDirectory.listFiles(new WarFileFilter())) { String basename = warFile.getName().replaceFirst("\\.war$", "");
fHandlers.addHandler(new WebAppContext(warFile.getAbsolutePath(), "/" + basename));
}
}
}
These war-files have some dependencies on a few classes that may or may not exist in the classpath.
Right now if one of my servlets is missing a dependency, my entire embedded Jetty service fails. (Because of NoClassDefFoundExceptions)
I need a method that allows me to catch exceptions for failing servlets and simply doesn't activate them. I'm looking for the same thing that TomCat does when a servlet fails to load: It still loads the rest.
I haven't found any solutions after some time searching on Google.
Anyone know how I can tackle this problem using embedded Jetty 8?
If anyone is curious how I fixed this, I simply made sure that all my servlets have a wrapper servlet that basically has no dependencies. The wrapper tries to initialize a delegate with dependencies and explicitly checks for NoClassDefFountException. If this happens, the delegate is set to null, and all calls to the wrapper interface will result in an exception.
So on a high level:
public class ServletWrapper extends HttpServlet{
private ServletDelegate fDelegate;
//If this is false, the delegate does not work, and we should not forward anything to it.
private boolean fAvailable = false;
public ServletWrapper(){
try{
fDelegate = new ServletDelegate();
fAvailable = true;
} catch (NoClassDefFoundError e) {
fAvailable = false;
}
}
#Override
protected void doPost( HttpServletRequest request, HttpServletResponse response )
throws ServletException, IOException {
if ( !fAvailable || fDelegate==null ) {
response.sendError( HttpServletResponse.SC_SERVICE_UNAVAILABLE, LSP_MISSING_ERROR_MESSAGE );
return;
}
fDelegate.doPost(request,response);
}
}
It's simple enough, and it works.
Related
I have brief understanding on multi threading in Java web apps. However, I came across an issue while developing our android app which communicates with the server via REST.
Our web application is based on Apache Wicket 8.6 and contains additional HttpServlet endpoints. One endpoint is applied while uploading images from the app via post. This works fine as long as I am uploading only one image at a time. If I perform multiple upload requests in my android app in a quick succession (some milliseconds), only the last upload is performed successfully (it is working fine when I put a second break between the uploads). It seems, as if all requests except the last one are missing the image content read from the input stream of the servlet request. Thus, I am guessing, that I am having a threading problem with my servlet. I would appreciate, if someone could guide me in the right direction to solve this issue.
public class MyServlet extends HttpServlet{
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
boolean proceed =true;
Map<String,String[]> parameters = req.getParameterMap();
if(parameters!=null){
//read some parameters which specify the request
}
if(proceed) {
InputStream is = req.getInputStream();
if(is!=null) {
//The result of this output is 0 for the first requests only for the last request it is 1
System.err.println("Stream size: "+is.available());
//do something
}
}
//do something....
}
}
I could, of course, write the images in my android app in one request using multipart but still I'd like to make the servlet thread safe for the occasion that it gets two requests at the same time.
I appreciate your help.
So, after some additional research I discovered, that the input stream is not empty even is.available() returns 0
My problem was something different. I save the uploaded image in a ModeShape repository. The repository session is stored in the wicket application instance. THus, only one ModeSHape repository session exists. When writing the image, it seems as if there was a problem with the ModeShape session. Thus, I put the ModeShape session in a synchronized block and now everything runs fine.
public class MyServlet extends HttpServlet{
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
boolean proceed =true;
Map<String,String[]> parameters = req.getParameterMap();
if(parameters!=null){
//read some parameters which specify the request
}
if(proceed) {
String fileName = req.getHeader("fileName");
if(!StringUtils.isEmpty(fileName)) {
InputStream is = req.getInputStream();
if(is!=null) {
//The result of this output is 0 for the first requests only for the last request it is 1
System.err.println("Stream size: "+is.available());
WicketApplication app=null;
try {
app = (WicketApplication)Application.get("wicket");
} catch (Exception e) {
LOG.error("Error while generating WicketApplication object for project "+project.getName(),e);
}
if(app!=null) {
final Session repoSession = app.getRepoSession();
synchronized (repoSession) {
//write image to repository
}
}
}
}
}
//do something....
}
}
Is it possible to have my app update the config settings at runtime? I can easily expose the settings I want in my UI but is there a way to allow the user to update settings and make them permanent ie save them to the config.yaml file? The only way I can see it to update the file by hand then restart the server which seems a bit limiting.
Yes. It is possible to reload the service classes at runtime.
Dropwizard by itself does not have the way to reload the app, but jersey has.
Jersey uses a container object internally to maintain the running application. Dropwizard uses the ServletContainer class of Jersey to run the application.
How to reload the app without restarting it -
Get a handle to the container used internally by jersey
You can do this by registering a AbstractContainerLifeCycleListener in Dropwizard Environment before starting the app. and implement its onStartup method as below -
In your main method where you start the app -
//getting the container instance
environment.jersey().register(new AbstractContainerLifecycleListener() {
#Override
public void onStartup(Container container) {
//initializing container - which will be used to reload the app
_container = container;
}
});
Add a method to your app to reload the app. It will take in the list of string which are the names of the service classes you want to reload. This method will call the reload method of the container with the new custom DropWizardConfiguration instance.
In your Application class
public static synchronized void reloadApp(List<String> reloadClasses) {
DropwizardResourceConfig dropwizardResourceConfig = new DropwizardResourceConfig();
for (String className : reloadClasses) {
try {
Class<?> serviceClass = Class.forName(className);
dropwizardResourceConfig.registerClasses(serviceClass);
System.out.printf(" + loaded class %s.\n", className);
} catch (ClassNotFoundException ex) {
System.out.printf(" ! class %s not found.\n", className);
}
}
_container.reload(dropwizardResourceConfig);
}
For more details see the example documentation of jersey - jersey example for reload
Consider going through the code and documentation of following files in Dropwizard/Jersey for a better understanding -
Container.java
ContainerLifeCycleListener.java
ServletContainer.java
AbstractContainerLifeCycleListener.java
DropWizardResourceConfig.java
ResourceConfig.java
No.
Yaml file is parsed at startup and given to the application as Configuration object once and for all. I believe you can change the file after that but it wouldn't affect your application until you restart it.
Possible follow up question: Can one restart the service programmatically?
AFAIK, no. I've researched and read the code somewhat for that but couldn't find a way to do that yet. If there is, I'd love to hear that :).
I made a task that reloads the main yaml file (it would be useful if something in the file changes). However, it is not reloading the environment. After researching this, Dropwizard uses a lot of final variables and it's quite hard to reload these on the go, without restarting the app.
class ReloadYAMLTask extends Task {
private String yamlFileName;
ReloadYAMLTask(String yamlFileName) {
super("reloadYaml");
this.yamlFileName = yamlFileName;
}
#Override
public void execute(ImmutableMultimap<String, String> parameters, PrintWriter output) throws Exception {
if (yamlFileName != null) {
ConfigurationFactoryFactory configurationFactoryFactory = new DefaultConfigurationFactoryFactory<ReportingServiceConfiguration>();
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator validator = validatorFactory.getValidator();
ObjectMapper objectMapper = Jackson.newObjectMapper();
final ConfigurationFactory<ServiceConfiguration> configurationFactory = configurationFactoryFactory.create(ServiceConfiguration.class, validator, objectMapper, "dw");
File confFile = new File(yamlFileName);
configurationFactory.build(new File(confFile.toURI()));
}
}
}
You can change the configuration in the YAML and read it while your application is running. This will not however restart the server or change any server configurations. You will be able to read any changed custom configurations and use them. For example, you can change the logging level at runtime or reload other custom settings.
My solution -
Define a custom server command. You should use this command to start your application instead of the "server" command.
ArgsServerCommand.java
public class ArgsServerCommand<WC extends WebConfiguration> extends EnvironmentCommand<WC> {
private static final Logger LOGGER = LoggerFactory.getLogger(ArgsServerCommand.class);
private final Class<WC> configurationClass;
private Namespace _namespace;
public static String COMMAND_NAME = "args-server";
public ArgsServerCommand(Application<WC> application) {
super(application, "args-server", "Runs the Dropwizard application as an HTTP server specific to my settings");
this.configurationClass = application.getConfigurationClass();
}
/*
* Since we don't subclass ServerCommand, we need a concrete reference to the configuration
* class.
*/
#Override
protected Class<WC> getConfigurationClass() {
return configurationClass;
}
public Namespace getNamespace() {
return _namespace;
}
#Override
protected void run(Environment environment, Namespace namespace, WC configuration) throws Exception {
_namespace = namespace;
final Server server = configuration.getServerFactory().build(environment);
try {
server.addLifeCycleListener(new LifeCycleListener());
cleanupAsynchronously();
server.start();
} catch (Exception e) {
LOGGER.error("Unable to start server, shutting down", e);
server.stop();
cleanup();
throw e;
}
}
private class LifeCycleListener extends AbstractLifeCycle.AbstractLifeCycleListener {
#Override
public void lifeCycleStopped(LifeCycle event) {
cleanup();
}
}
}
Method to reload in your Application -
_ymlFilePath = null; //class variable
public static boolean reloadConfiguration() throws IOException, ConfigurationException {
boolean reloaded = false;
if (_ymlFilePath == null) {
List<Command> commands = _configurationBootstrap.getCommands();
for (Command command : commands) {
String commandName = command.getName();
if (commandName.equals(ArgsServerCommand.COMMAND_NAME)) {
Namespace namespace = ((ArgsServerCommand) command).getNamespace();
if (namespace != null) {
_ymlFilePath = namespace.getString("file");
}
}
}
}
ConfigurationFactoryFactory configurationFactoryFactory = _configurationBootstrap.getConfigurationFactoryFactory();
ValidatorFactory validatorFactory = _configurationBootstrap.getValidatorFactory();
Validator validator = validatorFactory.getValidator();
ObjectMapper objectMapper = _configurationBootstrap.getObjectMapper();
ConfigurationSourceProvider provider = _configurationBootstrap.getConfigurationSourceProvider();
final ConfigurationFactory<CustomWebConfiguration> configurationFactory = configurationFactoryFactory.create(CustomWebConfiguration.class, validator, objectMapper, "dw");
if (_ymlFilePath != null) {
// Refresh logging level.
CustomWebConfiguration webConfiguration = configurationFactory.build(provider, _ymlFilePath);
LoggingFactory loggingFactory = webConfiguration.getLoggingFactory();
loggingFactory.configure(_configurationBootstrap.getMetricRegistry(), _configurationBootstrap.getApplication().getName());
// Get my defined custom settings
CustomSettings customSettings = webConfiguration.getCustomSettings();
reloaded = true;
}
return reloaded;
}
Although this feature isn't supported out of the box by dropwizard, you're able to accomplish this fairly easy with the tools they give you.
Before I get started, note that this isn't a complete solution for the question asked as it doesn't persist the updated config values to the config.yml. However, this would be easy enough to implement yourself simply by writing to the config file from the application. If anyone would like to write this implementation feel free to open a PR on the example project I've linked below.
Code
Start off with a minimal config:
config.yml
myConfigValue: "hello"
And it's corresponding configuration file:
ExampleConfiguration.java
public class ExampleConfiguration extends Configuration {
private String myConfigValue;
public String getMyConfigValue() {
return myConfigValue;
}
public void setMyConfigValue(String value) {
myConfigValue = value;
}
}
Then create a task which updates the config:
UpdateConfigTask.java
public class UpdateConfigTask extends Task {
ExampleConfiguration config;
public UpdateConfigTask(ExampleConfiguration config) {
super("updateconfig");
this.config = config;
}
#Override
public void execute(Map<String, List<String>> parameters, PrintWriter output) {
config.setMyConfigValue("goodbye");
}
}
Also for demonstration purposes, create a resource which allows you to get the config value:
ConfigResource.java
#Path("/config")
public class ConfigResource {
private final ExampleConfiguration config;
public ConfigResource(ExampleConfiguration config) {
this.config = config;
}
#GET
public Response handleGet() {
return Response.ok().entity(config.getMyConfigValue()).build();
}
}
Finally wire everything up in your application:
ExampleApplication.java (exerpt)
environment.jersey().register(new ConfigResource(configuration));
environment.admin().addTask(new UpdateConfigTask(configuration));
Usage
Start up the application then run:
$ curl 'http://localhost:8080/config'
hello
$ curl -X POST 'http://localhost:8081/tasks/updateconfig'
$ curl 'http://localhost:8080/config'
goodbye
How it works
This works simply by passing the same reference to the constructor of ConfigResource.java and UpdateConfigTask.java. If you aren't familiar with the concept see here:
Is Java "pass-by-reference" or "pass-by-value"?
The linked classes above are to a project I've created which demonstrates this as a complete solution. Here's a link to the project:
scottg489/dropwizard-runtime-config-example
Footnote: I haven't verified this works with the built in configuration. However, the dropwizard Configuration class which you need to extend for your own configuration does have various "setters" for internal configuration, but it may not be safe to update those outside of run().
Disclaimer: The project I've linked here was created by me.
I have been trying to get a Windows service running from my JAR file. WinRun4j seems to be able to do the job, but I can't get it to work. I am especially finding it quite difficult to debug. I tried several methods for logging (writing to a .txt file, WinRun4j's EventLog class) but I can't seem to generate any output.
The service installs fine (eventually..) and I can start it. It should start a Jetty server that generates an XML file that can be reached over HTTP. The app works for a stand-alone version, just not for the service. The service is started, but as soon as I call the URL it stops without generating an error.
This is my Service class:
package com.some.package;
import org.boris.winrun4j.AbstractService;
import org.boris.winrun4j.ServiceException;
/**
* A basic service.
*/
public class StockService extends AbstractService {
private StockServer srv;
public int serviceMain(String[] args) throws ServiceException {
while (!shutdown) {
try {
Thread.sleep(5000);
} catch(InterruptedException e) {
}
if(srv == null) {
try {
srv = new StockServer();
srv.start();
} catch (Exception e) {
}
}
}
return 0;
}
}
I found out that the service didn't want to start if I started the Jetty server from the serviceMain class. I had to start a new thread. So StockServer extends Thread:
public class StockServer extends Thread {
private Server server;
public void run() {
if (server == null) {
try {
server = new Server(8080);
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/example");
StockServlet stockServlet = new StockServlet();
context.addServlet(new ServletHolder(stockServlet), "/stock/*");
server.setHandler(context);
server.setStopAtShutdown(true);
server.start();
server.join();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
}
Since it runs perfectly fine as Java application I just don't know how to get this thing debugged. I hope one of you can point me in the right direction :).
I ended up using the Java Service Wrapper (JSW). This seemed a lot more complex but ended up to be quite easy. It also provides logging by default so I could easily fix the errors. The JSW had problems finding the correct JDK, since JSW is 32bit and I installed JDK1.7 64 bit (and 1.6 32bit). Installing JDK1.7 32bit fixed it. That might have been the problem with WinRun4j as well, but that is something I will never know :).
I'm quite confused on how to properly setup a logger in an Java EE OSGi environment. Here are my requirements:
Only 1 log file per EBA (grouping of bundles)
Multiple log files per application server (due to multiple EBAs)
Do not want to perform ClassLoader magic (its fine if a library I use does this, I just don't want to have to write it)
Must rotate the log file at the end of the day and only maintain 7 log files at once
Preferably does not require creating a logging bundle per EBA. No other developer will buy in if I tell them to write their own logging interop for each application.
Must work with WebSphere Application Server 8.5.5
First I tried to use SLF4j on top of log4j like our other Java EE applications do, however nothing could find my log4j.properties. I tried variations of importing SLF4j, and also had issues where loading it in 1 bundle prevented it from loading in another.
Next I looked into PAX logger, but it appears to log globally, not per EBA.
Attempting to use the OSGi LogService prevents my bundle from deploying to WebSphere, plus I'm not sure how I could get it to meet my requirements anyway.
I'm at the point where the only option I can see is to write my own bundle that keeps a registry for bundle -> log file (using FrameworkUtil.getBundle on the client class) and implement a full logging framework within it. If that has classloader isolation issues then possibly push to an EJB to do the actual logging. I'm really hoping that's not my only solution.
Can anyone point me to some documentation that will help me out??
Thank you!
People are in general confused about the Log Service ... The Log Service does not store any logs, it just acts as a dispatcher. I understand the confusion since the Log Service is mandated to have a small buffer for the initial start up and provides an API to get the buffer.
For what you want, you should add a Log Listener with the Log Reader services. What you want is quite easy with Declarative Services. This is a component that implements your requirements :
#Component(provide = {}, immediate = true) public class Logger extends Thread
implements LogListener {
final BlockingQueue<LogEntry> queue = new ArrayBlockingQueue<LogEntry>(1000);
File root;
#Reference void setLR(LogReaderService lr) {
lr.addLogListener(this);
}
#Activate void activate(BundleContext context, Map<String,Object> props) {
if ( props.containsKey("root"))
root = new File((String) props.get("root"));
else
root = context.getDataFile("logs");
root.mkdirs();
start();
}
#Deactivate void deactivate() {
interrupt();
}
#Override public void logged(LogEntry entry) {
queue.offer(entry); // ignore full silently
}
public void run() {
while (true)
try {
LogEntry entry = queue.take();
File file = getPath(entry);
if (file.isFile()) {
long days = TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis()
- file.lastModified());
if (days > 2) file.delete();
}
try (OutputStream raf = new FileOutputStream(file, true)) {
String s = String.format("%tT [%03d] %s%n", entry.getTime(), entry
.getBundle().getBundleId(), entry.getMessage());
raf.write(s.getBytes("UTF-8"));
}
} catch (InterruptedException ie) {
return;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private File getPath(LogEntry entry) {
long rollover = TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis()) % 7;
String eba = "eba"; // fill in eba from entry.getBundle()?
return new File(root, "log-" + eba + "-" + rollover + ".msg");
}
}
This could of course be done a bit more efficient, but that is left as an exercise.
I am writing a simple application that lets a user upload images. After the upload, the user can tag them or remove them.
I figured out how to upload the files and save them once the files are uploaded. I am keeping tracking of a global path where images are kept. In the database I keep the meta data about the images like file name, tags, etc.
I am using Java/JSP (specifically Stripes framework but my problem is generic).
My question is where do I keep these image files once they are uploaded?
Right now I have two web applications deployed on a Tomcat server. One main web application and other one is the where I upload the images.
But this does not work as I can not see the uploaded images in the main application until I redeploy/restart Tomcat.
It seems like Tomcat does not pick newly uploaded images automatically.
Does anyone have any solutions?
This is a simple project, so I do not want to store them in a database or use Apache for images. That is all just too complicated for this small project.
Thanks.
Definitely don't store the images in the database, but you will want to store the image path in the database. This will allow you to store the image just about anywhere.
Since you are using two tomcat applications, your best bet may be to store the images outside of either app and stream the image back to the user instead of letting tomcat manage the files. Otherwise, I would ask why you are trying to do this with two web apps.
However, storing uploaded images inside the web-app directory is not a wise thing to do, and you know it.
By the way, you might want to look this stackoverflow thread, lately discussed where to store the images. It might not solve your issue, surely will give you more confidence on what you are doing.
I've solved this in different ways.
First, the non-portable way, is that Glassfish (and I do believe Tomcat as well) allows you to map an external directory in to the webapps hierarchy. This works really well and does exactly what you want. It lets you store your images in an external directory away from your webapp, yet still serve them up.
However, this technique is not portable.
The way to I've done it portably is by creating a filter.
You place the filter someplace obvious, say "/images".
What the filter does is this:
it checks for the image (or anything, it works with any static resource) in a special directory within the webapp. For our example we'll use the url "/webapp/images".
if the file DOES NOT exist, we copy the file from your external location in to the appropriate spot within the webapp. So, let's say the reqyest url is "/images/banner.gif". And that your files are stored on disk at "/home/app/images". So, our source file is "/home/app/images/banner.gif". We then copy it to where we want it in the webapp tree. We use "ServletContext.getRealPath" for this. So, the destination will be "ServletContext.get RealPath("/webapp/images/banner.gif"). Just copy the source to the destination.
if the file already existed, or now exists, simply forward to the actual image at /webapp/images/banner.gif.
Effectively you end up having a file cache within your webapps deployment tree. The down side is that it's a cache, so it needs to be maintained (i.e. you should check if the original is newer than your cache, make sure you delete if the source is deleted, etc.). Also, it duplicates your resources, so your images will consume, eventually, twice as much disk space. Finally, there's the initial copy cost at start up.
However, it DOES work, and it prevents you from having to serve up static resources using your own code. (Which is the 3rd solution, map a filter/servlet to intercept the URLs and simply stream it your self.)
I would look at the construct within Tomcat (assuming it exists) to do the mapping for you. I know it exists in Glassfish. (Google alternatedocroot for Glassfish to see how it works.)
I was using two web applications to avoid over writing the uploaded images in case I redeploy a new main application war file.
But as you mention there is no other option but to stream them through a Servlet or something I guess I can keep them outside tomcat directory.
I wanted to avoid writing this Streaming Servlet. Just too small project to deal with all the mess (like proper content type, 404, etc.) while writing the streaming servlet.
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Image streaming Servlet.
*/
public class ImageDisplayServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* #see HttpServlet#HttpServlet()
*/
public ImageDisplayServlet() {
super();
}
/**
* #see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String relativePath = trimToEmpty(request.getPathInfo());
// Make sure no one try to screw with us.
// This is important as user can literally access any file if we are not careful
if(isXSSAttack(relativePath) == false) {
String pathToFile = this.getServletContext().getRealPath(request.getPathInfo());
File file = new File(pathToFile);
System.out.println("Looking for file " + file.getAbsolutePath());
// show a 404 page
if(!file.exists() || !file.isFile()) {
httpError(404, response);
} else {
try {
streamImageFile(file, response);
} catch(Exception e) {
// Tell the user there was some internal server error.\
// 500 - Internal server error.
httpError(500, response);
e.printStackTrace();
}
}
} else {
// what to do if i think it is a XSS attack ?!?
}
}
private void streamImageFile(File file, HttpServletResponse response) {
// find the right MIME type and set it as content type
response.setContentType(getContentType(file));
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
response.setContentLength((int) file.length());
// Use Buffered Stream for reading/writing.
bis = new BufferedInputStream(new FileInputStream(file));
bos = new BufferedOutputStream(response.getOutputStream());
byte[] buff = new byte[(int) file.length()];
int bytesRead;
// Simple read/write loop.
while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
bos.write(buff, 0, bytesRead);
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
// To late to do anything about it now, we may have already sent some data to user.
}
}
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
// To late to do anything about it now, we may have already sent some data to user.
}
}
}
}
private String getContentType(File file) {
if(file.getName().length() > 0) {
String[] parts = file.getName().split("\\.");
if(parts.length > 0) {
// only last part interests me
String extention = parts[parts.length - 1];
if(extention.equalsIgnoreCase("jpg")) {
return "image/jpg";
} else if(extention.equalsIgnoreCase("gif")) {
return "image/gif";
} else if(extention.equalsIgnoreCase("png")) {
return "image/png";
}
}
}
throw new RuntimeException("Can not find content type for the file " + file.getAbsolutePath());
}
private String trimToEmpty(String pathInfo) {
if(pathInfo == null) {
return "";
} else {
return pathInfo.trim();
}
}
private void httpError(int statusCode, HttpServletResponse response) {
try {
response.setStatus(statusCode);
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
writer.append("<html><body><h1>Error Code: " + statusCode + "</h1><body></html>");
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
private boolean isXSSAttack(String path) {
boolean xss = false;
// Split on the bases of know file separator
String[] parts = path.split("/|\\\\");
// Now verify that no part contains anything harmful
for(String part : parts) {
// No double dots ..
// No colons :
// No semicolons ;
if(part.trim().contains("..") || part.trim().contains(":") || part.trim().contains(";")) {
// Fire in the hole!
xss = true;
break;
}
}
return xss;
}
/**
* #see HttpServlet#doPost(Ht/promotions/some.jpgtpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
Ok Here is a Servlet that I quickly wrote that can stream images:
Here is the List of limitations and know issues:
May have XSS vulnerability use with care
Not production ready use as reference
Images need to in the web application directory. Can be easily change but I too lazy (it is not worth it the project is too small)
Only stream jpg,gif or png files.
Usage:
Let say you deploy this web application called images as separate application.
http://www.example.com/images/promotions/promo.jpg
means there should be a directory in "promotions" with image "promo.jpg" with in this images web application.
PS: Do not ask why I am doing this Servlet Container only solution that sucks big time.
<servlet>
<description></description>
<display-name>ImageDisplayServlet</display-name>
<servlet-name>ImageDisplayServlet</servlet-name>
<servlet-class>com.example.images.ImageDisplayServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ImageDisplayServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
Oh ya configure your Servlet like above for best results :P