I have a problem with Zip File System Provider: If the zip file is on a remote drive (mapped or not seems to be irrelevant), the virtual file system is readonly, although the file itself is not. I wrote a minimal sample code:
public static void main(String[] args) throws IOException {
File workingDir = new File(args[0]);
File source = new File(workingDir, "in.zip");
File target = new File(workingDir, "out.zip");
Files.copy(source.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
try (FileSystem zipfs = FileSystems.newFileSystem(target.toPath(), null)) {
Path pathInZipfile = zipfs.getPath("test.xml");
System.out.println("zipfile writable: " + target.canWrite());
System.out.println("zipFS writable: " + !zipfs.isReadOnly());
Files.delete(pathInZipfile);
System.out.println("File successfully deleted");
} catch (IOException e) {
e.printStackTrace();
}
}
If workingDir is a local directory, everything works fine. However, if it is a (mapped) remote drive, i get:
zipfile writable: true
zipFS writable: false
Exception in thread "main" java.nio.file.ReadOnlyFileSystemException
at com.sun.nio.zipfs.ZipFileSystem.checkWritable(ZipFileSystem.java:155)
at com.sun.nio.zipfs.ZipFileSystem.deleteFile(ZipFileSystem.java:1335)
at com.sun.nio.zipfs.ZipPath.delete(ZipPath.java:655)
at com.sun.nio.zipfs.ZipFileSystemProvider.delete(ZipFileSystemProvider.java:206)
at java.nio.file.Files.delete(Unknown Source)
at zipfs.ZipFS.main(ZipFS.java:23)
Am I doing something wrong? Is it impossible? Is there a workaround?
I ran into the same thing and I looked at the JDK code.
Findings
In ZipFileSystem.java there are three relevant lines:
zfpath.getFileSystem().provider().checkAccess(zfpath, AccessMode.READ);
if (!Files.isWritable(zfpath))
this.readOnly = true;
zfPath is a Path object. Something in the Windows FileSystem provider blocks write access to a zip archive path. Doesn't seem like there is much to be done about that.
Workaround
What I used as a workaround was to:
Create the zip archive in a temp folder
Populate the zip archive
Copy the temp file to the original mapped drive location
As long as the mapped drive is writable in contexts outside of the zip FileSystem, this method works.
We run in exactly the same problem, and have deviced somewhat barbarian solution.
FileSystems.newFileSystem(Path) accepts Path interface, so it's possible to satisfy it wrapping real Path instance.
Here is the solution that implements required FileSysteProvider.checkAccess():
private static FileSystem createZip(Path zipPath)
throws Exception
{
var fileSystem = zipPath.getFileSystem();
var provider = fileSystem.provider();
return FileSystems.newFileSystem(
new Path()
{
private Path path(Path path)
{
return this == path ? zipPath : path;
}
#Override
public FileSystem getFileSystem()
{
return new FileSystem()
{
public Set<String> supportedFileAttributeViews()
{
return fileSystem.supportedFileAttributeViews();
}
#Override
public FileSystemProvider provider()
{
return new FileSystemProvider()
{
#Override
public void setAttribute(
Path path,
String attribute,
Object value,
LinkOption... options)
throws IOException
{
provider.setAttribute(path(path), attribute, value, options);
}
#Override
public Map<String, Object> readAttributes(
Path path,
String attributes,
LinkOption... options)
throws IOException
{
return provider.
readAttributes(path(path), attributes, options);
}
#Override
public <A extends BasicFileAttributes> A readAttributes(
Path path,
Class<A> type,
LinkOption... options)
throws IOException
{
return provider.readAttributes(path(path), type, options);
}
#Override
public FileSystem newFileSystem(URI uri, Map<String, ?> env)
throws IOException
{
return provider.newFileSystem(uri, env);
}
#Override
public DirectoryStream<Path> newDirectoryStream(
Path dir,
Filter<? super Path> filter)
throws IOException
{
return provider.newDirectoryStream(dir, filter);
}
#Override
public SeekableByteChannel newByteChannel(
Path path,
Set<? extends OpenOption> options,
FileAttribute<?>... attrs)
throws IOException
{
return provider.newByteChannel(path(path), options, attrs);
}
#Override
public void move(
Path source,
Path target,
CopyOption... options)
throws IOException
{
provider.move(path(source), path(target), options);
}
#Override
public boolean isSameFile(Path path, Path path2)
throws IOException
{
return provider.isSameFile(path(path), path(path2));
}
#Override
public boolean isHidden(Path path)
throws IOException
{
return provider.isHidden(path(path));
}
#Override
public String getScheme()
{
return provider.getScheme();
}
#Override
public Path getPath(URI uri)
{
return provider.getPath(uri);
}
#Override
public FileSystem getFileSystem(URI uri)
{
return provider.getFileSystem(uri);
}
#Override
public FileStore getFileStore(Path path)
throws IOException
{
return provider.getFileStore(path(path));
}
#Override
public <V extends FileAttributeView> V getFileAttributeView(
Path path,
Class<V> type,
LinkOption... options)
{
return provider.
getFileAttributeView(path(path), type, options);
}
#Override
public void delete(Path path)
throws IOException
{
provider.delete(path(path));
}
#Override
public void createDirectory(Path dir, FileAttribute<?>... attrs)
throws IOException
{
provider.createDirectory(path(dir), attrs);
}
#Override
public void copy(
Path source,
Path target,
CopyOption... options)
throws IOException
{
provider.copy(path(source), path(target), options);
}
#Override
public void checkAccess(Path path, AccessMode... modes)
throws IOException
{
if ((modes != null) &&
(modes.length == 1) &&
(modes[0] == AccessMode.WRITE))
{
return;
}
provider.checkAccess(path(path), modes);
}
};
}
#Override
public WatchService newWatchService()
throws IOException
{
return fileSystem.newWatchService();
}
#Override
public boolean isReadOnly()
{
return false;
}
#Override
public boolean isOpen()
{
return fileSystem.isOpen();
}
#Override
public UserPrincipalLookupService getUserPrincipalLookupService()
{
return fileSystem.getUserPrincipalLookupService();
}
#Override
public String getSeparator()
{
return fileSystem.getSeparator();
}
#Override
public Iterable<Path> getRootDirectories()
{
return fileSystem.getRootDirectories();
}
#Override
public PathMatcher getPathMatcher(String syntaxAndPattern)
{
return fileSystem.getPathMatcher(syntaxAndPattern);
}
#Override
public Path getPath(String first, String... more)
{
return fileSystem.getPath(first, more);
}
#Override
public Iterable<FileStore> getFileStores()
{
return fileSystem.getFileStores();
}
#Override
public void close() throws IOException
{
fileSystem.close();
}
};
}
#Override
public boolean isAbsolute()
{
return zipPath.isAbsolute();
}
#Override
public Path getRoot()
{
return zipPath.getRoot();
}
#Override
public Path getFileName()
{
return zipPath.getFileName();
}
#Override
public Path getParent()
{
return zipPath.getParent();
}
#Override
public int getNameCount()
{
return zipPath.getNameCount();
}
#Override
public Path getName(int index)
{
return zipPath.getName(index);
}
#Override
public Path subpath(int beginIndex, int endIndex)
{
return zipPath.subpath(beginIndex, endIndex);
}
#Override
public boolean startsWith(Path other)
{
return zipPath.startsWith(other);
}
#Override
public boolean endsWith(Path other)
{
return zipPath.endsWith(other);
}
#Override
public Path normalize()
{
return zipPath.normalize();
}
#Override
public Path resolve(Path other)
{
return zipPath.relativize(other);
}
#Override
public Path relativize(Path other)
{
return zipPath.relativize(other);
}
#Override
public URI toUri()
{
return zipPath.toUri();
}
#Override
public Path toAbsolutePath()
{
return zipPath.toAbsolutePath();
}
#Override
public Path toRealPath(LinkOption... options)
throws IOException
{
return zipPath.toRealPath(options);
}
#Override
public WatchKey register(
WatchService watcher,
Kind<?>[] events,
Modifier... modifiers)
throws IOException
{
return zipPath.register(watcher, events, modifiers);
}
#Override
public int compareTo(Path other)
{
return zipPath.compareTo(other);
}
},
Map.of("create", "true"));
}
This is more like a hack but we think it solves the bug in original ZipFileSystem
Try setting the private field readOnly in ZipFileSystem to false with reflection.
Related
What is the best way to load a .java file call the main method and execute the code. So for instance if I call C:/Test.java it should execute the code.
My code right now is the following:
final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
final DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
final StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
final Iterable<<? extends JavaFileObject>compilationUnits = fileManager.getJavaFileObjectsFromStrings(Arrays.asList("C:/test.java"));
final JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, null, null, compilationUnits);
final boolean success = task.call();
try {
System.out.println("Compiled: " + success);
fileManager.close();
} catch (final IOException e) {
e.printStackTrace();
}
But it also generate a .class file in the folder which I don't want. Is this code good for java 14 or there is a better way?
First you can simplify the code a bit. You use the StandardJavaFileManager and it imports the interface Auto/Closeable. So you can use the try-with-resource:
final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
final DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
try (final StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null)) {
final Iterable<<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromStrings(Arrays.asList("C:/test.java"));
final JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, null, null, compilationUnits);
final boolean success = task.call();
} catch (IOException e) {
e.printStackTrace();
}
Second you need the .class file to execute your java program. There exists a nice post. If you want to execute your test file and you use the command line, you should use the following commands:
javac Test.java
java Test
// or a little bit shorter
java Test.java
If you want to know more about compilation, I can recommend this post or this post.
By implementing JavaFileObject yourself and "patching" JavaFileManager you can keep the compiler in-memory, and then via exposing defineClass() of ClassLoader you can load the resulting byte array and launch main() via reflection.
Here is a condensed variant, it actually does what you asked for, loads source code from file (that's one line with Files.readString() anyway). However that's something what you can probably leave entirely to the patched JavaFileManager, just I focused on keeping even the source code in memory, that's why there is the commented String src = "..."; block.
import java.io.*;
import java.net.*;
import java.nio.file.*;
import java.util.*;
import javax.lang.model.element.*;
import javax.tools.*;
public class SOCompileTestDense {
public static void main(String[] args) throws Exception {
String name="Test";
// String src="public class Test {\r\n" +
// " public static void main(String[] args) {\r\n" +
// " System.out.println(\"Hello \"+args[0]+\"!\");\r\n" +
// " }\r\n" +
// "}";
String src=Files.readString(Paths.get(name+".java"));
System.out.println(src);
List<JFO> sources=Arrays.asList(new JFO(name,src));
Map<String,JFO> files=new HashMap<>();
JavaCompiler jc=ToolProvider.getSystemJavaCompiler();
JavaCompiler.CompilationTask ct=jc.getTask(null, new JFM(jc.getStandardFileManager(null, null, null),files), null, null, null, sources);
System.out.println(ct.call());
Class<?> clazz=new CL().defineClass(name, files.get(name).baos.toByteArray());
clazz.getDeclaredMethod("main", String[].class).invoke(null, (Object)new String[] {"StackOverflow"});
}
static class JFO implements JavaFileObject {
final String name;
final String src;
JFO(String name,String src){
this.name=name;
this.src=src;
}
public URI toUri() {
try {
return new URI(name);
} catch(URISyntaxException use) {use.printStackTrace();};
return null;
}
public String getName() {
return name;
}
ByteArrayOutputStream baos;
public OutputStream openOutputStream() throws IOException {
baos=new ByteArrayOutputStream();
return baos;
}
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
return src;
}
public Kind getKind() {
return Kind.SOURCE;
}
public boolean isNameCompatible(String simpleName, Kind kind) {
return name.equals(simpleName);
}
public InputStream openInputStream() throws IOException {return null;}
public Reader openReader(boolean ignoreEncodingErrors) throws IOException {return null;}
public Writer openWriter() throws IOException {return null;}
public long getLastModified() {return 0;}
public boolean delete() {return false;}
public NestingKind getNestingKind() {return null;}
public Modifier getAccessLevel() {return null;}
}
static class JFM implements JavaFileManager {
final JavaFileManager jfm;
final Map<String, JFO> files;
JFM(JavaFileManager jfm, Map<String, JFO> files){
this.jfm=jfm;
this.files=files;
}
public int isSupportedOption(String option) {
return jfm.isSupportedOption(option);
}
public ClassLoader getClassLoader(Location location) {
return jfm.getClassLoader(location);
}
public Iterable<JavaFileObject> list(Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException {
return jfm.list(location, packageName, kinds, recurse);
}
public String inferBinaryName(Location location, JavaFileObject file) {
return jfm.inferBinaryName(location, file);
}
public boolean handleOption(String current, Iterator<String> remaining) {
return jfm.handleOption(current, remaining);
}
public boolean hasLocation(Location location) {
return jfm.hasLocation(location);
}
public JavaFileObject getJavaFileForInput(Location location, String className, JavaFileObject.Kind kind) throws IOException {
return jfm.getJavaFileForInput(location, className, kind);
}
public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
JFO jfo=new JFO(className,null);
files.put(className, jfo);
return jfo;
}
public String inferModuleName(Location location) throws IOException {
return jfm.inferModuleName(location);
}
public Iterable<Set<Location>> listLocationsForModules(Location location) throws IOException {
return jfm.listLocationsForModules(location);
}
public boolean isSameFile(FileObject a, FileObject b) {return false;}
public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {return null;}
public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) throws IOException {return null;}
public void flush() throws IOException {}
public void close() throws IOException {}
}
static class CL extends ClassLoader {
public Class<?> defineClass(String name,byte array[]) {
return defineClass(name, array, 0, array.length);
}
}
}
And this is a tidy, short version, its output looks as follows:
public class Test {
public static void main(String[] args) {
System.out.println("Hello "+args[0]+"!");
}
}
true
Hello StackOverflow!
A much longer variant with verbose output is available on Ideone, https://ideone.com/DAXIlH, and if you download that one and remove the // in front of various System.out.println(); calls, it will display a lot more stuff, which were simply too much for Ideone.
Practically what I did was implementing all methods as throw new Error(); (see code on Ideone), and gradually filled them out with something meaningful whenever they got actually called. Actually it was pretty straigtforward, the only gotcha I ran into was a couple default methods of the JavaFileManager interface. As eclipse did not provide a stub implementation of those, it was a bit of surprise when code died without touching my throw new Error(); lines.
I extended FileSystemView and overwrote every method in this class. The model looks like this:
public class RemoteSystemFilesView extends FileSystemView {
private IDirectoryService directoryService;
public RemoteSystemFilesView(IDirectoryService aDirectoryService){
this.directoryService = aDirectoryService;
}
....
}
The directoryService object returns directories from the remote UNIX server. Then, I create JFileChooser.
JFileChooser fc = new JFileChooser(new RemoteSystemFilesView(new DirectoryService()));
int returnVal = fc.showOpenDialog(this);
The dialog shows remote dirs and files correctly, but then I doubleClick on one of the displayed folders, I expect to navigate into that folder, but instead folder path appears in the field "File name" and that's it. I can't go to any other directory except root (/). Should I implement something else also in JFileChooser, not just in FileSystemView?
The problem might be that your FileSystemView is actually returning plain java.io.File objects.
Instead try to return a VirtualFile wrapper object that extends java.io.File and returns true for the public boolean exists() and wraps returns VirtualFile instead of java.io.File for all the necessary methods.
This is an example of a VirtualFileSystem that I developed. It uses java.nio.Path because my code is mainly based in them. I hope it gives you a good starting point for understanding how to modify your code.
private static class VirtualFileSystemView extends FileSystemView {
final Path base;
final Set<Path> choices;
private VirtualFileSystemView(final Path base,
final Set<Path> choices) {
this.base = base;
this.choices = choices;
}
#Override
protected File createFileSystemRoot(File f) {
return new VirtualFile(f);
}
#Override
public boolean isComputerNode(File dir) {
return false;
}
#Override
public boolean isFloppyDrive(File dir) {
return false;
}
#Override
public boolean isDrive(File dir) {
return false;
}
#Override
public Icon getSystemIcon(File f) {
return null;
}
#Override
public String getSystemTypeDescription(File f) {
return f.toPath().toString();
}
#Override
public String getSystemDisplayName(File f) {
return f.getName();
}
#Override
public File getParentDirectory(final File dir) {
return new VirtualFile(dir.getParentFile());
}
#Override
public File[] getFiles(final File dir, boolean useFileHiding) {
final List<File> files = new ArrayList<>(choices.size());
choices.stream()
.filter((path) -> (path.getParent().equals(dir.toPath()))).
forEach((path) -> {
files.add(new VirtualFile(path.toFile()));
});
return files.toArray(new File[files.size()]);
}
#Override
public File createFileObject(final String path) {
return new VirtualFile(path);
}
#Override
public File createFileObject(final File dir, final String filename) {
Path fileObject;
if (dir != null) {
fileObject = Paths.get(dir.toPath().toString(), filename);
} else {
fileObject = Paths.get(filename);
}
return new VirtualFile(fileObject.toFile());
}
#Override
public File getDefaultDirectory() {
return new VirtualFile(base.toFile());
}
#Override
public File getHomeDirectory() {
return new VirtualFile(base.toFile());
}
#Override
public File[] getRoots() {
final List<File> files = new ArrayList<>(choices.size());
files.add(new VirtualFile(base.toFile()));
return files.toArray(new File[files.size()]);
}
#Override
public boolean isFileSystemRoot(final File dir) {
boolean isRoot = dir.toPath().getParent() == null;
return isRoot;
}
#Override
public boolean isHiddenFile(final File f) {
return false;
}
#Override
public boolean isFileSystem(final File f) {
return !isFileSystemRoot(f);
}
#Override
public File getChild(final File parent, final String fileName) {
return new VirtualFile(parent, fileName);
}
#Override
public boolean isParent(final File folder, final File file) {
return file.toPath().getParent().equals(folder.toPath());
}
#Override
public Boolean isTraversable(final File f) {
boolean isTraversable = false;
for (final Path path : choices) {
if (path.startsWith(f.toPath())) {
isTraversable = true;
break;
}
}
return isTraversable;
}
#Override
public boolean isRoot(final File f) {
boolean isRoot = false;
for (final Path path : choices) {
if (path.getParent().equals(f.toPath())) {
isRoot = true;
}
}
return isRoot;
}
#Override
public File createNewFolder(final File containingDir) throws IOException {
return new VirtualFile(containingDir);
}
private class VirtualFile extends File {
private static final long serialVersionUID = -1752685357864733168L;
private VirtualFile(final File file) {
super(file.toString());
}
private VirtualFile(String pathname) {
super(pathname);
}
private VirtualFile(String parent, String child) {
super(parent, child);
}
private VirtualFile(File parent, String child) {
super(parent, child);
}
#Override
public boolean exists() {
return true;
}
#Override
public boolean isDirectory() {
return VirtualFileSystemView.this.isTraversable(this);
}
#Override
public File getCanonicalFile() throws IOException {
return new VirtualFile(super.getCanonicalFile());
}
#Override
public File getAbsoluteFile() {
return new VirtualFile(super.getAbsoluteFile());
}
#Override
public File getParentFile() {
File parent = super.getParentFile();
if (parent != null) {
parent = new VirtualFile(super.getParentFile());
}
return parent;
}
}
}
I am compiling and loading classes in memory with JavaCompiler.
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
JavaCompiler.CompilationTask compile = compiler.getTask(null, inMemoryFileManager, diagnostics, null, annotationClasses, compilationUnits);
if (!compile.call()) {
diagnostics.getDiagnostics().forEach(System.err::println);
}
I am using a similar Filemanager and SimpleJavaObject as in this example. I modified it slightly to be able to handle multiple files. If you need my source code please let me know.
The code above runs fine as long as annotationClasses is null.
When I try to add classes to annotation processing however I get the following error:
Class names, '<classNames>', are only accepted if annotation processing is explicitly requested
For classNames I tried Main, Main.java, package.Main and package.Main.java. They all result in the same error.
I know this is a very specific scenario. Did anybody try to do something similar and can help me?
Edit:
/* Container for a Java compilation unit (ie Java source) in memory. */
private static class CompilationUnit extends SimpleJavaFileObject {
private final String source;
public CompilationUnit(String className, String source) {
super(URI.create("file:///" + className.replaceAll("\\.", "/") + ".java"), Kind.SOURCE);
this.source = source;
}
#Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
}
#Override
public OutputStream openOutputStream() {
throw new IllegalStateException();
}
#Override
public InputStream openInputStream() {
return new ByteArrayInputStream(source.getBytes());
}
}
/* Container for Java byte code in memory. */
private static class ByteJavaFileObject extends SimpleJavaFileObject {
private ByteArrayOutputStream byteArrayOutputStream;
public ByteJavaFileObject(String className) {
super(URI.create("byte:///" + className.replaceAll("\\.", "/") + ".class"), Kind.CLASS);
}
#Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return null;
}
#Override
public OutputStream openOutputStream() {
byteArrayOutputStream = new ByteArrayOutputStream();
return byteArrayOutputStream;
}
#Override
public InputStream openInputStream() {
return null;
}
public byte[] getByteCode() {
return byteArrayOutputStream.toByteArray();
}
}
private static class InMemoryFileManager extends ForwardingJavaFileManager {
private final InMemoryClassLoader inMemoryClassLoader;
public InMemoryFileManager(JavaCompiler compiler, Map<String, ByteJavaFileObject> fileObjects) {
super(compiler.getStandardFileManager(null, null, null));
inMemoryClassLoader = new InMemoryClassLoader(fileObjects);
}
#Override
public JavaFileObject getJavaFileForOutput(Location notUsed, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
return inMemoryClassLoader.getFileObject(className);
}
#Override
public ClassLoader getClassLoader(Location location) {
return inMemoryClassLoader;
}
public InMemoryClassLoader getClassLoader() {
return inMemoryClassLoader;
}
}
private static class InMemoryClassLoader extends ClassLoader {
private final Map<String, ByteJavaFileObject> fileObjects;
public InMemoryClassLoader(Map<String, ByteJavaFileObject> fileObjects) {
this.fileObjects = fileObjects;
}
#Override
protected Class<?> findClass(String className) throws ClassNotFoundException {
ByteJavaFileObject fileObject = fileObjects.get(className);
return defineClass(className, fileObject.getByteCode(), 0, fileObject.getByteCode().length);
}
JavaFileObject getFileObject(String className) {
return fileObjects.get(className);
}
}
Is there any way to get the SVN structure as a tree struture in java?
For Eg: if i specify a path http://sample.com/repository/pag/branches/dev/Structure/services/
I want what all entries under services and if it again contains a directory its enteries also in a tree?
Thanks.
Note : i have seen the getDir(). But here i have to keep on iterating it.
If you need all the tree, you may do that with "status" request with report telling that you have an empty working copy. One "status" request should be faster than a number of getDir() requests. An example how to do that with SVNKit
final SVNRepository svnRepository = SVNRepositoryFactory.create(url);
try {
svnRepository.status(revision, "", SVNDepth.INFINITY, new ISVNReporterBaton() {
#Override
public void report(ISVNReporter reporter) throws SVNException {
reporter.setPath("", null, revision, SVNDepth.INFINITY, true);
reporter.finishReport();
}
}, new ISVNEditor() {
#Override
public void targetRevision(long revision) throws SVNException {
}
#Override
public void openRoot(long revision) throws SVNException {
System.out.println("<root>");
}
#Override
public void deleteEntry(String path, long revision) throws SVNException {
}
#Override
public void absentDir(String path) throws SVNException {
}
#Override
public void absentFile(String path) throws SVNException {
}
#Override
public void addDir(String path, String copyFromPath, long copyFromRevision) throws SVNException {
System.out.println("directory: " + path);
}
#Override
public void openDir(String path, long revision) throws SVNException {
}
#Override
public void changeDirProperty(String name, SVNPropertyValue value) throws SVNException {
}
#Override
public void closeDir() throws SVNException {
}
#Override
public void addFile(String path, String copyFromPath, long copyFromRevision) throws SVNException {
System.out.println("file: " + path);
}
#Override
public void openFile(String path, long revision) throws SVNException {
}
#Override
public void changeFileProperty(String path, String propertyName, SVNPropertyValue propertyValue) throws SVNException {
}
#Override
public void closeFile(String path, String textChecksum) throws SVNException {
}
#Override
public SVNCommitInfo closeEdit() throws SVNException {
return null;
}
#Override
public void abortEdit() throws SVNException {
}
#Override
public void applyTextDelta(String path, String baseChecksum) throws SVNException {
}
#Override
public OutputStream textDeltaChunk(String path, SVNDiffWindow diffWindow) throws SVNException {
return null;
}
#Override
public void textDeltaEnd(String path) throws SVNException {
}
});
} finally {
svnRepository.closeSession();
}
So normally in a Spring controller you'd just return a ModelAndView object and forward the request to the JSP.
What I need to do is actually get the contents of that processed JSP so I can then send it in a JSONP response (ex: callback("processed HTML from JSP");)
I know I could just use HttpClient to get the contents but was wondering if there's a way to avoid that extra step by calling something like:
String contents = processJSP(modelAndView);
Updated for geek to show my final solution:
First you need a fake HttpResponse to hold the response:
#Service
public class SpringUtils {
private static final Logger LOG = Logger.getLogger(SpringUtils.class);
#Autowired private ViewResolver viewResolver;
#Autowired private LocaleResolver localeResolver;
public String renderView(HttpServletRequest request, ModelAndView mav) {
try {
View view = viewResolver.resolveViewName(mav.getViewName(), localeResolver.resolveLocale(request));
HttpServletResponse localResponse = new MyHttpServletResponseWrapper(new DummyResponse());
view.render(mav.getModel(), request, localResponse);
return localResponse.toString();
} catch (Exception e) {
return "";
}
}
public boolean doesViewExist(HttpServletRequest request, String viewName) {
try {
if (viewResolver.resolveViewName(viewName, localeResolver.resolveLocale(request)) != null) {
return true;
}
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
return false;
}
static class MyHttpServletResponseWrapper extends HttpServletResponseWrapper {
private StringWriter sw = new StringWriter();
public MyHttpServletResponseWrapper(HttpServletResponse response) {
super(response);
}
public PrintWriter getWriter() throws IOException {
return new PrintWriter(sw);
}
public ServletOutputStream getOutputStream() throws IOException {
throw new UnsupportedOperationException();
}
public String toString() {
return sw.toString();
}
}
}
DummyResponse
public class DummyResponse implements HttpServletResponse {
public DummyResponse() {
}
public void setAppCommitted(boolean appCommitted) {}
public boolean isAppCommitted() { return false; }
public int getContentCount() { return -1; }
public boolean getIncluded() { return false; }
public void setIncluded(boolean included) {}
public String getInfo() { return null; }
public ServletResponse getResponse() { return null; }
public OutputStream getStream() { return null; }
public void setStream(OutputStream stream) {}
public void setSuspended(boolean suspended) {}
public boolean isSuspended() { return false; }
public void setError() {}
public boolean isError() { return false; }
public ServletOutputStream createOutputStream() throws IOException {
return null;
}
public void finishResponse() throws IOException {}
public int getContentLength() { return -1; }
public String getContentType() { return null; }
public PrintWriter getReporter() { return null; }
public void recycle() {}
public void write(int b) throws IOException {}
public void write(byte b[]) throws IOException {}
public void write(byte b[], int off, int len) throws IOException {}
public void flushBuffer() throws IOException {}
public int getBufferSize() { return -1; }
public String getCharacterEncoding() { return null; }
public void setCharacterEncoding(String charEncoding) {}
public ServletOutputStream getOutputStream() throws IOException {
return null;
}
public Locale getLocale() { return null; }
public PrintWriter getWriter() throws IOException { return null; }
public boolean isCommitted() { return false; }
public void reset() {}
public void resetBuffer() {}
public void setBufferSize(int size) {}
public void setContentLength(int length) {}
public void setContentType(String type) {}
public void setLocale(Locale locale) {}
public Cookie[] getCookies() { return null; }
public String getHeader(String name) { return null; }
public Collection<String> getHeaders(String arg0) { return null; }
public Collection<String> getHeaderNames() { return null; };
public String[] getHeaderValues(String name) { return null; }
public String getMessage() { return null; }
public int getStatus() { return -1; }
public void reset(int status, String message) {}
public void addCookie(Cookie cookie) {}
public void addDateHeader(String name, long value) {}
public void addHeader(String name, String value) {}
public void addIntHeader(String name, int value) {}
public boolean containsHeader(String name) { return false; }
public String encodeRedirectURL(String url) { return null; }
public String encodeRedirectUrl(String url) { return null; }
public String encodeURL(String url) { return null; }
public String encodeUrl(String url) { return null; }
public void sendAcknowledgement() throws IOException {}
public void sendError(int status) throws IOException {}
public void sendError(int status, String message) throws IOException {}
public void sendRedirect(String location) throws IOException {}
public void setDateHeader(String name, long value) {}
public void setHeader(String name, String value) {}
public void setIntHeader(String name, int value) {}
public void setStatus(int status) {}
public void setStatus(int status, String message) {}
}
Probably not possible easy way.
Maybe injecting view resolver into controller and calling render with special response will help, but not sure :
ViewResolver viewResoler = // injected
View view = viewReslover.resolveViewName(String viewName, Locale locale);
HttpServletResponse xresponse = // custom response, buffers data
view.render(Map model, HttpServletRequest request, HttpServletResponse xresponse);
String content = // extract conten from data from xresponse
'