I have:
Directory webdir = new Directory(getContext(), "clap://class/webapp");
webdir.setDeeplyAccessible(true);
router.attach("",webdir);
This works when serving all files in the directory by name.
However, it should serve index.html when you visit "/" and it doesn't. I've tried all combinations of path, extra routers etc etc and it's still not working.
When you visit "/" you get a 200 response and an application/octet-stream content type. The response is otherwise empty. The getIndexName on Directory assures me it's index
I've also tried getMetadataService().addExtension("html", MediaType.TEXT_HTML, true); to help it to pick up the index.html file but to no avail and also set the accept header in the request to text/html.
ETA: it is the same (unsolved) problem that's described here: http://restlet-discuss.1400322.n2.nabble.com/Serving-static-files-using-Directory-and-CLAP-from-a-jar-td7578543.html
Can anyone help with this? It's driving me nuts.
After a bit of fiddling I have this workaround in place now, but I'd rather not redirect if possible:
Redirector redirector = new Redirector(getContext(), "/index.html", Redirector.MODE_CLIENT_PERMANENT);
TemplateRoute route = router.attach("/",redirector);
route.setMatchingMode(Template.MODE_EQUALS);
The behaviour is caused by the way the ClapClientHelper class identifies a target as a file or directory. The workaround solution is to replace the ClapClientHelper class with another nearly identical one called say JarClapClientHelper. Copy the source code from ClapClientHelper and change the following snippet in the handleClassLoader method.
// The ClassLoader returns a directory listing in some cases.
// As this listing is partial, it is of little value in the context
// of the CLAP client, so we have to ignore them.
if (url != null) {
if (url.getProtocol().equals("file")) {
File file = new File(url.getFile());
modificationDate = new Date(file.lastModified());
if (file.isDirectory()) {
url = null;
}
//NEW CODE HERE
} else if (url.getProtocol().equals("jar")) {
try {
JarURLConnection conn = (JarURLConnection) url.openConnection();
modificationDate = new Date(conn.getJarEntry().getLastModifiedTime().toMillis());
if (conn.getJarEntry().isDirectory()) {
url = null;
}
} catch (IOException ioe) {
getLogger().log(Level.WARNING,
"Unable to open the representation's input stream",
ioe);
response.setStatus(Status.SERVER_ERROR_INTERNAL);
}
}
}
Now you need to load this helper instead of the default one.
(Thanks to #Thierry Boileau.)
There are two ways, one that does not require any code, and one that is programmatic.
The first one is to let the ServiceLoader find your service:
add a file called META-INF/services/org.restlet.engine.ClientHelper to your solution.
this file should have a single line of text: the full name (including the package) of your helper class
The second one is to add the helper programmatically:
Engine.getInstance().getRegisteredClients().add(0, new JarClapClientHelper(null));
Try using the WAR protocol:
Directory directory = new Directory(getContext(), "war:///");
directory.setIndexName("index.html");
router.attach("/", directory);
Pay attention to the triple slash to identify the war package root, otherwise you will get NullPointerException.
(In my case I have a Maven project and my index file is put on the root of the generated war package).
Related
I am struggling while trying to read the bytes of a PNG image bundled with the resources of a JAR. The file is located in the src/main/resources directory.
Here is my code so far:
byte[] bytes = {};
final InputStream defaultImageStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("/defaultLogo.png");
new DataInputStream(defaultImageStream).readFully(bytes);
The code is executed on a Wildfly 12 server, located in a JAR included in the EAR as an EJB.
It seems than instead of retrieving the resource I asked for, getResourceAsStream returns the enclosing jar:
How can I get that image?
Additional info:
I tried both with an exploded and non-epxloded JAR in the EAR. Same results.
The path to the resource seems correct. Prefixing it by "/resources" ends in the method returning NULL.
I tried using the Class' classloader instead of the thread context's one. Same results.
I envisioned going through all the entries of the enclosed JAR myself, but this both seems overkill and difficult: since I have a JarInputStream and no JarFile, how would I read the data corresponding to an entry?
I think your code is working as intended. Looking at the DataInputStream instance isn't going to tell you much. Look at the content, I think it is the image you want.
You're thinking correctly, the JarInputStream would server the purpose for you.
Your code should be something like below--
try {
JarInputStream jarIS = new JarInputStream(new FileInputStream(
"jarfilePath"));
JarEntry entry = null;
while ((entry = jarIS.getNextJarEntry()) != null) {
String name = entry.getName();
if (name.endsWith("defaultLogo.png")) {
System.out.println( "You got the PNG File"+entry.getAttributes().toString() );
//Now handle your stream as per your requirement.
}
}
} catch (Exception e) {
}
I've been using Java 7's ZipFS support.
https://gist.github.com/stain/5591420
shows the behaviour, which I find a bit odd. Basically you can create a ZIP file system, make a file with a given name, and then also make a folder with the same name.
The reason for this seems to be that internally the folder gets "/" appended to its name - however this new name is not returned, therefore you end up in a strange situation where Files.isDirectory() returns false immediately after a successful Files.createDirectory().
try (FileSystem fs = tempZipFS()) {
Path folder = fs.getPath("folder");
Files.createFile(folder);
assertTrue(Files.isRegularFile(folder));
assertFalse(Files.isDirectory(folder));
// try {
Files.createDirectory(folder);
// } catch (FileAlreadyExistsException ex) {
// Is not thrown!
// }
// but a second createDirectory() fails correctly
try {
Files.createDirectory(folder);
} catch (FileAlreadyExistsException ex) {
}
// Look, it's both a file and folder!
Path child = folder.resolve("child");
Files.createFile(child);
// Can this be tested?
assertTrue(Files.isRegularFile(folder));
// Yes, if you include the final /
assertTrue(Files.isDirectory(fs.getPath("folder/")));
// But not the parent
// assertTrue(Files.isDirectory(child.getParent()));
// Or the original Path
// assertTrue(Files.isDirectory(folder));
}
So as long as you have the "/" as the suffix, you can even work with both, and that's how they are listed if you do a directory listing of the root.
Now the ZIP format itself allows this as it only deals with entries in a ZIP file (even allowing multiple entries with the same name), however normal use of a "FileSystem" would normally not allow multiple entries with the same name ; as can be seen when I try to create the folder twice.
The produced ZIP file can be browsed correctly with infozip, 7Zip and Windows 8; but trying to unzip will obviously fail because the native file system don't support such duality.
So is this a feature, bug or something in between?
I have a homepage, which has a directory for downloads.
I want to read all files of this homepage automatically, from its directory.
For example I have the homepage: www.homepage.org and the subdirectory resources/downloads with the download files I want to show.
I have a Java Server with Spring and JSPs running and tried the following, which didn't work at all:
String path = request.getContextPath();
path += DOWNLOADS;
URL url = null;
String server = request.getServerName();
try {
url = new URL("https", server, request.getLocalPort(), path);
} catch (MalformedURLException e) {
e.printStackTrace();
}
if (url != null) {
String externalForm = url.toExternalForm();
File directory = new File(externalForm);
String[] files = directory.list();
for (String file : files) {
;
}
}
You can use a Servlet. If your resources directory is in your web app's root directory, then use
PrintWriter writer = response.getWriter();
String path = getServletContext().getRealPath("/resources/downloads");
File directory = new File(path);
if(directory.isDirectory()){
String[] files = directory.list();
for (String file : files) {
writer.write(file + "<br/>");
}
} else writer.write(directory.getAbsolutePath() + "could not be found");
I found a solution with using a FTP manager...
You can view the code here
I hope it will help you resolve the problem.
You need to list the directory using the path to the directory not the url. Once you retrieve the files you must create your own html to put it your web if that's what you want.
There is a good example to create a directory listing and browsing with JQuery here
There is a JSP connector included in the package in the download section that you may need to tune up a bit if you don't want to use the JQuery tree.
good luck!
The HTTP protocol does not provide a way to list the "files" in a "directory". Indeed, from the perspective of the protocol spec, there is no such thing as a directory.
If the webserver you are trying to access supports WebDAV, you could use PROPFIND to find out a collection (i.e. directory)'s structure.
Alternatively, if the webserver has directory listing enabled (and there is no index.html or whatever) then you could screen-scrape the listing HTML.
Otherwise, there is no real solution ... using HTTP / HTTPS.
On rereading the question and the answers, it strikes me that the servlet that is trying to retrieve the directory listing and the servlet that hosts the directories could actually be running on the same machine. In that case, you may have the option of accessing the information via the file system. But this will entail the "client" servlet knowing how the "server" servlet represents things. The URLs don't (and cannot) provide you that information.
The answer provided by rickz is one possible approach, though it only works if the client and server servlets are in fact the same servlet.
I'm trying to fetch list of files using a method that apparently works well with non-applet Java code.
I'm fully aware it's messy; I'm just trying to get this to work for a school assignment. (I'm no fan of Java.)
CodeSource src = MemoryButtonHelper.class.getProtectionDomain().getCodeSource();
if (src == null) {
throw new Exception();
}
URL jar = src.getLocation();
System.out.println("Loading from " + jar);
JarFile zf=new JarFile(jar.toString()); //jar.openStream());
final Enumeration<JarEntry> entries = zf.entries();
while (entries.hasMoreElements()) {
final JarEntry ze = entries.nextElement();
if(ze.getName().endsWith(".jpg") || ze.getName().endsWith(".png"))
{
System.out.println("Adding " + ze.getName());
slikeList.add(ze.getName());
}
}
zf.close();
Unfortunately, I'm getting a security exception.
java.security.AccessControlException: access denied (java.lang.RuntimePermission getProtectionDomain)
at java.security.AccessControlContext.checkPermission(AccessControlContext.java:374)
at java.security.AccessController.checkPermission(AccessController.java:546)
at java.lang.SecurityManager.checkPermission(SecurityManager.java:532)
at java.lang.Class.getProtectionDomain(Class.java:2106)
at hr.tvz.programiranje.java.deseti.helpers.MemoryButtonHelper.getSlike(MemoryButtonHelper.java:75)
… ad nauseam …
According to Java Console, exception appears to occur before the println("Loading from " + jar).
This is a bonus point assignment which specifically says that we must fetch the list of images from the JAR file. Since this is my first encounter with the applets, I'm not sure what I can do to fetch the list of images.
..we must fetch the list of images from the JAR file.
Who put them in there in the first place? If the answer is 'we did', the solution is simple.
Include a list of image path/names at a known location (e.g. images/imagelist.txt) in the Jar.
Obtain a reference to the list using getResource(String).
Read the list (probably using a line reader).
Apparently they want me to list the contents of the jar as-is, without extra metadata.
OK. If you can form an URL to (and thereby an InputStream from) the Zip file, it is possible to establish a ZipInputStream from it. This should work whether the URL is to a Zip/Jar cached on the local file-system or still at the server.
Get an URL to the Jar.
Establish a ZipInputStream from the URL.
Iterate the entries using getNextEntry() until null
Examine each one for a potential match and if it does, add it to an ArrayList.
Of course, you'll still need signed & trusted code to call for the protection domain.
Images are definitely in the same JAR
To get an URL to the Jar, try this (untested). Let's assume the applet is com.our.BookingApplet.
Obtain an URL to the Jar in which the class resides, using
URL urlToApplet = this.getClass().getResource("/com/our/BookingApplet.class")
String[] parts = urlToApplet.toString().split("!") will provide two parts, the first will be a String representation of the Jar URL.
Use that String to establish an URL, then use the URL as described in the previous update.
Thanks go to Andrew Thompson for his excellent answer! Definitely upvote his answer instead (or in addition) to this one, since without his help, I wouldn't be able to figure this out.
Here is the portion of the code which I came up with to use to fetch list of .jpg and .png files inside the JAR file. Note that you probably need to change the name of the package where MemoryGame class is stored, as well as change the name of the class itself.
List<String> slikeList = new ArrayList<String>();
URL urlToApplet = MemoryGame.class.getResource("/com/whoever/whatever/gui/MemoryGame.class");
String[] parts = urlToApplet.toString().split("!");
String jarURLString = parts[0].replace("jar:", "");
System.out.println("Loading from " + jarURLString);
URL jar = new URL(jarURLString);
URLConnection jarConnection = jar.openConnection();
JarInputStream jis = new JarInputStream(jarConnection.getInputStream());
JarEntry je = jis.getNextJarEntry();
while(je != null)
{
System.out.println("Inspecting " + je);
if(je.getName().endsWith(".jpg") || je.getName().endsWith(".png"))
{
System.out.println("Adding " + je.getName());
slikeList.add(je.getName());
}
je = jis.getNextJarEntry();
}
In case you wonder, slike means images in Croatian since a lot of variables named in the exercise specification are in Croatian.
Only "Signed" applets are allowed to access file system. If the jar file which you are reading from is located on your local file system, you will need to sign the applet first.
See this link for more information on how to sign applet.
I need to get names of all java packages loaded by the JVM. This is to display a package browser like the ones found in IDEs. I can get the package list of the current classloader and its ancestors by accessing protected "packages" field of the ClassLoader class. But i'm unable to get the packages loaded by other webapps as they have their own class loaders. I'm testing this on Weblogic server
The expected behavior of the Weblogic security model is that you would not have access to the other web applications' class loaders. This is not really something that you will be able to get around - see this article for more information.
You need to walk the tree of classloaders up by using getParent(), find all classes which extend ClassLoader, find all current instances (the debug API should help here). But that probably won't work for Web Servers because of the security policy (web apps are not allowed to peek at each other).
For Tomcat, there is an option to log all classes as they are loaded. This slows down a server pretty much but it might be an option on the development server.
That said, I'm pretty curious why you would need that. The most simple solution would be to list all JAR files, which your app brings along, with jar tvf and strip the last part of the path (the class file).
The only way I can think of doing this would be to modify each webapp so that you can send each a request for their loaded class information. You could then create a new webapp that combines the responses from the existing webapps for display.
If you don't need this information in some nice UI then the Sun JVM has number of -XX vm options that will show you what's going on with regards to class loading.
http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp
I'm not that familiar with JRockit but I'd be surprised if it didn't have similar options.
Ok I managed to get this working on Weblogic. Again my aim was to get java package names in all applications deployed on a given WebLogic server. Why? I had my reasons :)
First you have to get hold of the ear, war or jar file locations of all deployed apps. To do this we get the AppDeployment MBeans from WebLogic and iterate as shown below.
Set<ObjectName> set = utils.getConfigMBeansByType("AppDeployment");
for (ObjectName objectName : set) {
String name = objectName.getKeyProperty("Name");
if (!appCache.contains(name)) {
//System.out.println("Config bean: " + objectName);
Object path = utils.getPropertyValue(objectName,
"AbsoluteSourcePath");
//System.out.println("Path: " + path);
if(path != null){
PackageFinder finder = new PackageFinder();
packages.addAll(finder.findPackages(path.toString()));
}
appCache.add(name);
}
}
In the above code we get the path to the war, ear, jar or the exploded folder and passes it to the PackageFinder class's findPakages method which does all the work.
public Set<String> findPackages(String path){
File file = new File(path);
if(file.exists() && file.isFile()){
InputStream in = null;
try {
in = new BufferedInputStream(new FileInputStream(file));
if(path.toLowerCase().endsWith(".war")){
processWar(in);
}else if(path.toLowerCase().endsWith(".ear")){
processEar(in);
}/*
Rest of the method body removed, I guess you get the idea
*/
return packageNames;
}
public void processJar(InputStream in){
ZipInputStream zin = null;
try {
zin = new ZipInputStream(in);
ZipEntry entry;
while((entry = zin.getNextEntry()) != null){
if(entry.getName().endsWith(".class")){
addPackage(entry.getName());
}
}
} catch (Exception e) {
}
}