I have an upload servlet that is working great but leaves it's temporary files lying around. I am trying to use the part.delete() to clean them up as I go, but they are not deleting.
The docs say the container will delete them when it does GC. But even if I wait an hour and eventually shut the server down, they are still there...
What's the trick? It's Eclipse Kepler with Tomcat 7.0.47 on Windows for the moment. But production will be Linux.
Thanks
Code condensed substantially:
#MultipartConfig(location = "C:/tmp",
fileSizeThreshold=1024*1024*10, // 10MB
maxFileSize=1024*1024*10, // 10MB
maxRequestSize=1024*1024*50) // 50MB
#WebServlet("/upload.do")
...
for (Part part : request.getParts()) {
String filename = getFilename(part);
if(!(filename==null)){
part.write("/elsewhere/"+filename);
part.delete();
} else {
out.println("skip field...");
}
}
Hi you can create Servlet Listner like this
#WebListener
public class ContextListner implements ServletRequestListener, ServletContextListener {
public ContextListner() {
}
public void requestDestroyed(ServletRequestEvent sre) {
deleteDir(sre.getServletContext().getRealPath("") + File.separator + UploadConstants.TEMP_DIR);
}
public void contextInitialized(ServletContextEvent sce) {
}
public void contextDestroyed(ServletContextEvent sce) {
deleteDir(sce.getServletContext().getRealPath("") + File.separator + UploadConstants.TEMP_DIR);
}
public void requestInitialized(ServletRequestEvent sre) {
}
private void deleteDir(final String dirPath) {
if (null == dirPath)
return;
File dir = new File(dirPath);
if (dir.exists() && dir.isDirectory()) {
File[] files = dir.listFiles();
if (null != files) {
for (File file : files) {
file.delete();
}
}
}
}
}
And mark your servlet with annotation as mentioned below.
#WebListener(value = "ContextListner")
This will delete temp file under temp directory or your specified directory.
public void requestDestroyed(ServletRequestEvent sre) {
deleteDir(sre.getServletContext().getRealPath("") + File.separator + UploadConstants.TEMP_DIR);
}
This method get call after response send back to client.
Related
We have an integration test setup for testing the behavior of missing but required configuration properties. Among one of these properties is a directory where failed uploads should be written to for later retries. The general behavior for this property should be that the application doesn't even start up and fail immediately when certain constraints are violated.
The properties are managed by Spring via certain ConfigurationProperties among these we have a simple S3MessageUploadSettings class
#Getter
#Setter
#ConfigurationProperties(prefix = "s3")
#Validated
public class S3MessageUploadSettings {
#NotNull
private String bucketName;
#NotNull
private String uploadErrorPath;
...
}
In the respective Spring configuration we now perform certain validation checks, like whether the path exists, is writable and a directory, and throw respective RuntimeExceptions when certain assertions aren't met:
#Slf4j
#Import({ S3Config.class })
#Configuration
#EnableConfigurationProperties(S3MessageUploadSettings.class)
public class S3MessageUploadSpringConfig {
#Resource
private S3MessageUploadSettings settings;
...
#PostConstruct
public void checkConstraints() {
String sPath = settings.getUploadErrorPath();
Path path = Paths.get(sPath);
...
log.debug("Probing path '{}' for existence', path);
if (!Files.exists(path)) {
throw new RuntimeException("Required error upload directory '" + path + "' does not exist");
}
log.debug("Probig path '{}' for being a directory", path);
if (!Files.isDirectory(path)) {
throw new RuntimeException("Upload directory '" + path + "' is not a directoy");
}
log.debug("Probing path '{}' for write permissions", path);
if (!Files.isWritable(path)) {
throw new RuntimeException("Error upload path '" + path +"' is not writable);
}
}
}
Our test setup now looks like this:
public class StartupTest {
#ClassRule
public static TemporaryFolder testFolder = new TemporaryFolder();
private static File BASE_FOLDER;
private static File ACCESSIBLE;
private static File WRITE_PROTECTED;
private static File NON_DIRECTORY;
#BeforeClass
public static void initFolderSetup() throws IOException {
BASE_FOLDER = testFolder.getRoot();
ACCESSIBLE = testFolder.newFolder("accessible");
WRITE_PROTECTED = testFolder.newFolder("writeProtected");
if (!WRITE_PROTECTED.setReadOnly()) {
fail("Could not change directory permissions to readonly")
}
if (!WRITE_PROTECTED.setWritable(false)) {
fail("Could not change directory permissions to writable(false)");
}
NON_DIRECTORY = testFolder.newFile("nonDirectory");
}
#Configuration
#Import({
S3MessageUploadSpringConfig.class,
S3MockConfig.class,
...
})
static class BaseContextConfig {
// common bean definitions
...
}
#Configuration
#Import(BaseContextConfig.class)
#PropertySource("classpath:ci.properties")
static class NotExistingPathContextConfig {
#Resource
private S3MessageUploadSettings settings;
#PostConstruct
public void updateSettings() {
settings.setUploadErrorPath(BASE_FOLDER.getPath() + "/foo/bar");
}
}
#Configuration
#Import(BaseContextConfig.class)
#PropertySource("classpath:ci.properties")
static class NotWritablePathContextConfig {
#Resource
private S3MessageUploadSettings settings;
#PostConstruct
public void updateSettings() {
settings.setUploadErrorPath(WRITE_PROTECTED.getPath());
}
}
...
#Configuration
#Import(BaseContextConfig.class)
#PropertySource("classpath:ci.properties")
static class StartableContextConfig {
#Resource
private S3MessageUploadSettings settings;
#PostConstruct
public void updateSettings() {
settings.setUploadErrorPath(ACCESSIBLE.getPath());
}
}
#Test
public void shouldFailStartupDueToNonExistingErrorPathDirectory() {
ApplicationContext context = null;
try {
context = new AnnotationConfigApplicationContext(StartupTest.NotExistingPathContextConfig.class);
fail("Should not have started the context");
} catch (Exception e) {
e.printStackTrace();
assertThat(e, instanceOf(BeanCreationException.class));
assertThat(e.getMessage(), containsString("Required error upload directory '" + BASE_FOLDER + "/foo/bar' does not exist"));
} finally {
closeContext(context);
}
}
#Test
public void shouldFailStartupDueToNonWritablePathDirectory() {
ApplicationContext context = null;
try {
context = new AnnotationConfigApplicationContext(StartupTest.NotWritablePathContextConfig.class);
fail("Should not have started the context");
} catch (Exception e) {
assertThat(e, instanceOf(BeanCreationException.class));
assertThat(e.getMessage(), containsString("Error upload path '" + WRITE_PROTECTED + "' is not writable"));
} finally {
closeContext(context);
}
}
...
#Test
public void shouldStartUpSuccessfully() {
ApplicationContext context = null;
try {
context = new AnnotationConfigApplicationContext(StartableContextConfig.class);
} catch (Exception e) {
e.printStackTrace();
fail("Should not have thrown an exception of type " + e.getClass().getSimpleName() + " with message " + e.getMessage());
} finally {
closeContext(context);
}
}
private void closeContext(ApplicationContext context) {
if (context != null) {
// check and close any running S3 mock as this may have negative impact on the startup of a further context
closeS3Mock(context);
// stop a running Spring context manually as this might interfere with a starting context of an other test
((ConfigurableApplicationContext) context).stop();
}
}
private void closeS3Mock(ApplicationContext context) {
S3Mock s3Mock = null;
try {
if (context != null) {
s3Mock = context.getBean("s3Mock", S3Mock.class);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != s3Mock) {
s3Mock.stop();
}
}
}
}
When run locally, everything looks fine and all tests pass. Though our CI runs these tests inside a docker container and for some reason changing file permissions seem to end up in a NOOP returning true on the method invocation though not changing anything in regards of the file permission itself.
Neiter File.setReadOnly(), File.setWritable(false) nor Files.setPosixFilePermissions(Path, Set<PosixFilePermission>) seem to have an effect on the actual file permissions in the docker container.
I've also tried to change the directories to real directories, i.e. /root or /dev/pts that are write protected, though as the CI runs the tests as root these directories are writable by the application and the test fails again.
I also considered using an in-memory file system (such as JimFS) though here I'm not sure how to convince the test to make use of the custom filesystem. AFAIK JimFS does not support the constructor needed for declaring it as default filesystem.
Which other possibilities exist from within Java to change a directories permission to readonly/write-protected when run inside a docker container or test successfully for such a directory?
I assume this is due to the permissions and policies of the JVM, and you cannot do anything from your code if the OS has blocked some permissions for your JVM.
You can try to edit java.policy file and set appropriate file permissions.
Perhaps these will be some given files to which write privileges will be set, for example:
grant {
permission java.io.FilePermission "/dev/pts/*", "read,write,delete";
};
More examples in docs: https://docs.oracle.com/javase/8/docs/technotes/guides/security/spec/security-spec.doc3.html.
I would like to display all the artifact name along with its implementation version details deployed in an jboss eap server in a web application deployed in same server.How can i read the manifest file from a war file in another webapplication.
sce.getServletContext().getResourceAsStream("/META-INF/MANIFEST.MF"); works only to get the manifest file from the war servlet context.
public class Version implements ServletContextListener {
private static final Logger LOG = LoggerFactory.getLogger(Version.class);
private static Attributes sMainManifestAttributes;
public static final String ARTIFACT_ID = "Implementation-Title";
public static final String ARTIFACT_VERSION = "Implementation-Version";
/**
* Read the manifest from /META-INF/MANIFEST.MF
*/
#Override
public void contextInitialized(ServletContextEvent sce) {
try {
ServletContext application = sce.getServletContext();
InputStream inputStream = application.getResourceAsStream("/META-INF/MANIFEST.MF");
Manifest manifest = new Manifest(inputStream);
sMainManifestAttributes = manifest.getMainAttributes();
LOG.info("BIH Artifact Name:" + sMainManifestAttributes.getValue(ARTIFACT_ID) + " Artifact Version :"
+ sMainManifestAttributes.getValue(ARTIFACT_VERSION));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
sMainManifestAttributes = null;
}
/**
* Generic querying of the manifest.
*
* #return The result, as run through String.trim()
*/
public static String getValue(String name) {
return sMainManifestAttributes.getValue(name).trim();
}
}
Code
I'm trying to add drop functionality to my program so users can drag and drop files from Windows. Therefore I implemented the SWT DropTargetListener interface and added a SWT DropTarget to my SWT control.
public class MyFileDrop implements DropTargetListener {
public MyFileDrop(final Control control) {
addDropTarget(control);
}
private void addDropTarget(final Control control) {
final DropTarget dropTarget = new DropTarget(control, DND.DROP_COPY | DND.DROP_DEFAULT);
dropTarget.setTransfer(new Transfer[] { FileTransfer.getInstance() });
dropTarget.addDropListener(this);
}
#Override
public void drop(final DropTargetEvent event) {
String[] fileNames = (String[]) event.data;
for (final String fileName : fileNames) {
System.out.println(fileName);
}
}
// other DropTargetListener methods [...]
}
If I drag and drop a folder from Windows Explorer to my control, the folder name gets printed out. But all files inside that dropped folder aren't listed.
Example
folder1
- subfile1.png
- subfile2.exe
file1.png
file2.exe
If I select folder1, file1 and file2 and drag and drop it to my program, this is the output:
path/to/folder1
path/to/file1.png
path/to/file2.exe
Questions
Why aren't the files inside the folder dropped to the program? How can I archive that these files are also dropped?
How can I set the files apart from the folder?
Regards winklerrr
Code
So this is my solution
#Override
public void drop(DropTargetEvent event) {
String[] fileNames = (String[]) event.data;
for (String fileName : fileNames) {
final File file = new File(fileName);
if (file.isDirectory()) {
Collection<File> subFiles = FileUtils.listFiles(file, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE);
for (File subFile : subFiles) {
System.out.println(subFile.getAbsolutePath());
}
} else {
System.out.println(file.getAbsolutePath());
}
}
}
I used the FileUtils from the commons-io package.
Example
folder1
- subfile1.png
- subfile2.exe
file1.png
file2.exe
With the new code, this is now the output:
path/to/folder1/subfile1.png
path/to/folder1/subfile2.exe
path/to/file1.png
path/to/file2.exe
Answers
How can I archive that these files are also dropped?
FileUtils.listFiles(File, IOFileFilter, IOFileFilter)
How can I set the files apart from the folder?
File.isDirectory()
Using Play Framework (version 2.3.x) (Java style), I am trying to serve an .mp3 file to the browser. Since it is a 'large' file I have decided to go with Play's ByteChunks Object, as follows.
#With(MP3Headers.class)
public static Result test() {
Chunks<byte[]> chunks = new ByteChunks() {
public void onReady(Chunks.Out<byte[]> out) {
try {
byte[] song = Files.readAllBytes(Paths.get("public/mp3/song.mp3"));
out.write(song);
} catch(Exception e) {
e.printStackTrace();
} finally {
out.close();
}
}
};
return ok(chunks);
}
For clarification, my Mp3Headers file, which is responsable for setting the headers so that the browser knows what type the payload has:
public class MP3Headers extends Action.Simple {
public Promise<Result> call(Http.Context ctx) throws Throwable {
ctx.response().setContentType("audio/mpeg");
return delegate.call(ctx);
}
}
For completion, my routes file:
# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~
# Home page
GET / controllers.Application.index()
GET /test controllers.Application.test()
# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.at(path="/public", file)
As is to be expected, navigating to localhost:9000/test renders to a nice HTML5 audio player (see picture).
The problem I have is that 'scrolling' in the audio player does not work. If I do scroll, the music pauses, and when I let go (when I 'chose' a position in time), it continues where it first paused.
I hope that I make sense, and I hope that you guys know something more about this. Thanks in advance.
You will need to tell your browser that your server support range requests and implement the ranges responses (ie just provide the part of the music the browser needs). You can get an overview of the request/response cycle in this answer.
#With(MP3Headers.class)
public static Result test() {
final int begin, end;
final boolean isRangeReq;
response().setHeader("Accept-Ranges", "bytes");
if (request().hasHeader("RANGE")) {
isRangeReq = true;
String[] range = request().getHeader("RANGE").split("=")[1].split("-");
begin = Integer.parseInt(range[0]);
if (range.length > 1) {
end = Integer.parseInt(range[1]);
} else {
end = song.length-1;
}
response().setHeader("Content-Range", String.format("bytes %d-%d/%d", begin, end, song.length));
} else {
isRangeReq = false;
begin = 0;
end = song.length - 1;
}
Chunks<byte[]> chunks = new ByteChunks() {
public void onReady(Chunks.Out<byte[]> out) {
if(isRangeReq) {
out.write(Arrays.copyOfRange(song, begin, end));
} else {
out.write(song);
}
out.close();
}
};
response().setHeader("Content-Length", (end - begin + 1) + "");
if (isRangeReq) {
return status(206, chunks);
} else {
return status(200, chunks);
}
}
Note that in this code the song was already loaded in song. Also the parsing of the RANGE header is very dirty (you can get values like RANGE:)
I Found this code very easy implementation.
Put the below action and its private helper method in your controller.
Controller Action
public static Result file(Long id, String filename) throws IOException {
Item item = Item.fetch(id);
File file = item.getFile();
if(file== null || !file.exists()) {
Logger.error("File no longer exist item"+id+" filename:"+filename);
return notFound();
}
String rangeheader = request().getHeader(RANGE);
if(rangeheader != null) {
String[] split = rangeheader.substring("bytes=".length()).split("-");
if(Logger.isDebugEnabled()) { Logger.debug("Range header is:"+rangeheader); }
if(split.length == 1) {
long start = Long.parseLong(split[0]);
long length = file.length()-1l;
return stream(start, length, file);
} else {
long start = Long.parseLong(split[0]);
long length = Long.parseLong(split[1]);
return stream(start, length, file);
}
}
// if no streaming is required we simply return the file as a 200 OK
if(Play.isProd()) {
response().setHeader("Cache-Control", "max-age=3600, must-revalidate");
}
return ok(file);
}
Stream Helper method
private static Result stream(long start, long length, File file) throws IOException {
FileInputStream fis = new FileInputStream(file);
fis.skip(start);
response().setContentType(MimeTypes.forExtension("mp4").get());
response().setHeader(CONTENT_LENGTH, ((length - start) +1l)+"");
response().setHeader(CONTENT_RANGE, String.format("bytes %d-%d/%d", start, length,file.length()));
response().setHeader(ACCEPT_RANGES, "bytes");
response().setHeader(CONNECTION, "keep-alive");
return status(PARTIAL_CONTENT, fis);
}
Complete example link is here Byte range requests in Play 2 Java Controllers
I have a java web application working on struts. I want to access the data in the struts-config.xml file in runtime.
I tried to access it like a simple file, but it's unreachable to the application because it's outside the root directory of the app.
How does struts itself read the file? And how can I immitate it in runtime? I just need to read it like a simple xml file.
Thanks.
Struts does this simply by using ServletContext.
Because Struts ActionServlet extends HttpServlet, they simply do:
URL resource = getServletContext().getResource("/WEB-INF/struts-config.xml");
From there, You can get an InputStream and read the data from the resource.
Should the resource be null, the other alternative would be:
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (loader == null) {
loader = this.getClass().getClassLoader();
}
Enumeration e = loader.getResources(path);
if (e != null && e.hasMoreElements()) {
resource = (URL)e.nextElement();
}
The codes above is just simplified.
In Stuts2, method init_TraditionalXmlConfigurations in class org.apache.struts2.dispatcher.Dispatcher is responsible to init xml configurations. It will search 3 files, struts-default.xml,struts-plugin.xml,struts.xml(they are defined in constant variant DEFAULT_CONFIGURATION_PATHS).
private void init_TraditionalXmlConfigurations() {
String configPaths = initParams.get("config");
if (configPaths == null) {
configPaths = DEFAULT_CONFIGURATION_PATHS;
}
String[] files = configPaths.split("\\s*[,]\\s*");
for (String file : files) {
if (file.endsWith(".xml")) {
if ("xwork.xml".equals(file)) {
configurationManager.addContainerProvider(createXmlConfigurationProvider(file, false));
} else {
configurationManager.addContainerProvider(createStrutsXmlConfigurationProvider(file, false, servletContext));
}
} else {
throw new IllegalArgumentException("Invalid configuration file name");
}
}
}
Then, in method loadConfigurationFiles, it will get all configuration files url:
try {
urls = getConfigurationUrls(fileName);
} catch (IOException ex) {
ioException = ex;
}
And the following implementation is how to get configuration files' url:
protected Iterator<URL> getConfigurationUrls(String fileName) throws IOException {
return ClassLoaderUtil.getResources(fileName, XmlConfigurationProvider.class, false);
}
public static Iterator<URL> getResources(String resourceName, Class callingClass, boolean aggregate) throws IOException {
AggregateIterator<URL> iterator = new AggregateIterator<URL>();
iterator.addEnumeration(Thread.currentThread().getContextClassLoader().getResources(resourceName));
if (!iterator.hasNext() || aggregate) {
iterator.addEnumeration(ClassLoaderUtil.class.getClassLoader().getResources(resourceName));
}
if (!iterator.hasNext() || aggregate) {
ClassLoader cl = callingClass.getClassLoader();
if (cl != null) {
iterator.addEnumeration(cl.getResources(resourceName));
}
}
if (!iterator.hasNext() && (resourceName != null) && ((resourceName.length() == 0) || (resourceName.charAt(0) != '/'))) {
return getResources('/' + resourceName, callingClass, aggregate);
}
return iterator;
}
The code above is how struts loads configuration.
For you, if you want to load struts-config.xml manually, you can use the following code:
String filePath = "your struts-config.xml file path";
URL resource = Thread.currentThread().getContextClassLoader().getResource(filePath);
Then, you can read the file like a simple xml file.