Is there a way in JNA to load multiple dependent libraries with Java?
I usually use Native.loadLibrary(...) to load one DLL. But I guess its not working this way because I assign this function call to the instance member.
Let's say I have library foo and library bar. bar has a dependency on foo; it also has a dependency on baz, which we are not mapping with JNA:
public class Foo {
public static final boolean LOADED;
static {
Native.register("foo");
LOADED = true;
}
public static native void call_foo();
}
public class Bar {
static {
// Reference "Foo" so that it is loaded first
if (Foo.LOADED) {
System.loadLibrary("baz");
// Or System.load("/path/to/libbaz.so")
Native.register("bar");
}
}
public static native void call_bar();
}
The call to System.load/loadLibrary will only be necessary if baz is neither on your library load path (PATH/LD_LIBRARY_PATH, for windows/linux respectively) nor in the same directory as bar (windows only).
EDIT
You can also do this via interface mapping:
public interface Foo extends Library {
Foo INSTANCE = (Foo)Native.loadLibrary("foo");
}
public interface Bar extends Library {
// Reference Foo prior to instantiating Bar, just be sure
// to reference the Foo class prior to creating the Bar instance
Foo FOO = Foo.INSTANCE;
Bar INSTANCE = (Bar)Native.loadLibrary("bar");
}
Loading lib transient dependencies with JNA from JAR Resources.
My resources folder res:
res/
`-- linux-x86-64
|-- libapi.so
|-- libdependency.so
-
MyApiLibrary api = (MyApiLibrary) Native.loadLibrary("libapi.so", MyApiLibrary.class, options);
API Explodes:
Caused by: java.lang.UnsatisfiedLinkError: Error loading shared library libdependency.so: No such file or directory
Can be solved by loading dependencies beforehand by hand:
import com.sun.jna.Library;
Native.loadLibrary("libdependency.so", Library.class);
MyApiLibrary api = (MyApiLibrary) Native.loadLibrary("libapi.so", MyApiLibrary.class, options);
Basically you have to build dependency tree in reverse, by hand, by yourself.
I recommend setting
java -Djna.debug_load=true -Djna.debug_load.jna=true
Furthermore, setting jna.library.path to Resource has no effect, because JNA extracts to filesystem, then it loads lib. Lib on filesystem can NOT access other libs within jar.
Context class loader classpath. Deployed native libraries may be
installed on the classpath under ${os-prefix}/LIBRARY_FILENAME, where
${os-prefix} is the OS/Arch prefix returned by
Platform.getNativeLibraryResourcePrefix(). If bundled in a jar file,
the resource will be extracted to jna.tmpdir for loading, and later
removed (but only if jna.nounpack is false or not set).
Javadoc
RTFM and happy coding. JNA v.4.1.0
I was in a similar situation, dealing with multiplatform and several dependent libraries, but needing to load only one. Here is my take.
Suppose you get a set 32/64 win/linux libraries with dependencies.
Suppose you only need to have a JNA binding for libapi
You'll need to organize them into your jar like this:
linux-x86-64
|-- libapi.so
|-- libdependency.so
linux-x86
|-- libapi.so
|-- libdependency.so
win32-x86-64
|-- libapi.dll
|-- libdependency.dll
win32-x86
|-- libapi.dll
|-- libdependency.dll
You can:
determine if executing from a JAR file (avoids performing the operation when executing from your favorite IDE ; see How to get the path of a running JAR file?)
use JNA to determine your current executing platform
extract all appropriate library files into java temp folder (using elements from this answer: https://stackoverflow.com/a/58318009/7237062 (or related answers) should do the trick)
Tell JNA to look into the newly created temp folder
and voilà !
missing in code example is the directory cleanup at application shutdown, but I leave that to you
The main part should look like that:
MainClass.java
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Optional;
import java.util.jar.JarFile;
import com.sun.jna.Platform;
public class MainClass {
private static final String JAVA_IO_TMPDIR = "java.io.tmpdir";
private static final String TEMP_DIR = System.getProperty(JAVA_IO_TMPDIR);
private static final String JNA_LIBRARY_PATH = "jna.library.path";
public static void main(String[] args) {
// ...
// path management here maybe suboptimal ... feel free to improve
// from https://stackoverflow.com/questions/320542/how-to-get-the-path-of-a-running-jar-file
URL current_jar_dir = Overview.class.getProtectionDomain().getCodeSource().getLocation();
Path jar_path = Paths.get(current_jar_dir.toURI());
String folderContainingJar = jar_path.getParent().toString();
ResourceCopy r = new ResourceCopy(); // class from https://stackoverflow.com/a/58318009/7237062
Optional<JarFile> jar = r.jar(MainClass.class);
if (jar.isPresent()) {
try {
System.out.println("JAR detected");
File target_dir = new File(TEMP_DIR);
System.out.println(String.format("Trying copy from %s %s to %s", jar.get().getName(), Platform.RESOURCE_PREFIX, target_dir));
// perform dir copy
r.copyResourceDirectory(jar.get(), Platform.RESOURCE_PREFIX, target_dir);
// add created folders to JNA lib loading path
System.setProperty(JNA_LIBRARY_PATH, target_dir.getCanonicalPath().toString());
} catch(Exception e) {
e.printStackTrace(); // TODO: handle exception ?
}
} else {
System.out.println("NO JAR");
}
// ...
}
ResourceCopy.java (copy here for completeness; taken from https://stackoverflow.com/a/58318009)
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.nio.file.Files;
import java.util.Enumeration;
import java.util.Optional;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* A helper to copy resources from a JAR file into a directory. source :
* https://stackoverflow.com/a/58318009
*/
public final class ResourceCopy {
/**
* URI prefix for JAR files.
*/
private static final String JAR_URI_PREFIX = "jar:file:";
/**
* The default buffer size.
*/
private static final int BUFFER_SIZE = 8 * 1024;
/**
* Copies a set of resources into a temporal directory, optionally
* preserving the paths of the resources.
*
* #param preserve
* Whether the files should be placed directly in the directory
* or the source path should be kept
* #param paths
* The paths to the resources
* #return The temporal directory
* #throws IOException
* If there is an I/O error
*/
public File copyResourcesToTempDir(final boolean preserve, final String... paths) throws IOException {
final File parent = new File(System.getProperty("java.io.tmpdir"));
File directory;
do {
directory = new File(parent, String.valueOf(System.nanoTime()));
} while (!directory.mkdir());
return this.copyResourcesToDir(directory, preserve, paths);
}
/**
* Copies a set of resources into a directory, preserving the paths and
* names of the resources.
*
* #param directory
* The target directory
* #param preserve
* Whether the files should be placed directly in the directory
* or the source path should be kept
* #param paths
* The paths to the resources
* #return The temporal directory
* #throws IOException
* If there is an I/O error
*/
public File copyResourcesToDir(final File directory, final boolean preserve, final String... paths)
throws IOException {
for (final String path : paths) {
final File target;
if (preserve) {
target = new File(directory, path);
target.getParentFile().mkdirs();
} else {
target = new File(directory, new File(path).getName());
}
this.writeToFile(Thread.currentThread().getContextClassLoader().getResourceAsStream(path), target);
}
return directory;
}
/**
* Copies a resource directory from inside a JAR file to a target directory.
*
* #param source
* The JAR file
* #param path
* The path to the directory inside the JAR file
* #param target
* The target directory
* #throws IOException
* If there is an I/O error
*/
public void copyResourceDirectory(final JarFile source, final String path, final File target) throws IOException {
final Enumeration<JarEntry> entries = source.entries();
final String newpath = String.format("%s/", path);
while (entries.hasMoreElements()) {
final JarEntry entry = entries.nextElement();
if (entry.getName().startsWith(newpath) && !entry.isDirectory()) {
final File dest = new File(target, entry.getName().substring(newpath.length()));
final File parent = dest.getParentFile();
if (parent != null) {
parent.mkdirs();
}
this.writeToFile(source.getInputStream(entry), dest);
}
}
}
/**
* The JAR file containing the given class.
*
* #param clazz
* The class
* #return The JAR file or null
* #throws IOException
* If there is an I/O error
*/
public Optional<JarFile> jar(final Class<?> clazz) throws IOException {
final String path = String.format("/%s.class", clazz.getName().replace('.', '/'));
final URL url = clazz.getResource(path);
Optional<JarFile> optional = Optional.empty();
if (url != null) {
final String jar = url.toString();
final int bang = jar.indexOf('!');
if (jar.startsWith(ResourceCopy.JAR_URI_PREFIX) && bang != -1) {
optional = Optional.of(new JarFile(jar.substring(ResourceCopy.JAR_URI_PREFIX.length(), bang)));
}
}
return optional;
}
/**
* Writes an input stream to a file.
*
* #param input
* The input stream
* #param target
* The target file
* #throws IOException
* If there is an I/O error
*/
private void writeToFile(final InputStream input, final File target) throws IOException {
final OutputStream output = Files.newOutputStream(target.toPath());
final byte[] buffer = new byte[ResourceCopy.BUFFER_SIZE];
int length = input.read(buffer);
while (length > 0) {
output.write(buffer, 0, length);
length = input.read(buffer);
}
input.close();
output.close();
}
}
Related
On the first run, I want to copy the given File to a new location with a new file name.
Every subsequent run should overwrite the same destination file created during first run.
During first run, the destination file does not exist. Only the directory exists.
I wrote the following program:
package myTest;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Paths;
import org.apache.commons.io.FileUtils;
public class FileCopy {
public static void main(String[] args) {
TestFileCopy fileCopy = new TestFileCopy();
File sourceFile = new File("myFile.txt");
fileCopy.saveFile(sourceFile);
File newSourceFile = new File("myFile_Another.txt");
fileCopy.saveFile(newSourceFile);
}
}
class TestFileCopy {
private static final String DEST_FILE_PATH = "someDir/";
private static final String DEST_FILE_NAME = "myFileCopied.txt";
public void saveFile(File sourceFile) {
URL destFileUrl = getClass().getClassLoader().getResource(DEST_FILE_PATH
+ DEST_FILE_NAME);
try {
File destFile = Paths.get(destFileUrl.toURI()).toFile();
FileUtils.copyFile(sourceFile, destFile);
} catch (IOException | URISyntaxException e) {
e.printStackTrace();
}
}
}
However, this throws null pointer exception on the following line:
File destFile = Paths.get(destFileUrl.toURI()).toFile();
What am I missing?
Directory someDir is directly under my project's root directory in eclipse.
Both source files myFile.txt and myFile_Another.txt exists directly under my project's root directory in eclipse.
I used this and it works as I am expecting:
public void saveFile1(File sourceFile) throws IOException {
Path from = sourceFile.toPath();
Path to = Paths.get(DEST_FILE_PATH + DEST_FILE_NAME);
Files.copy(from, to, StandardCopyOption.REPLACE_EXISTING);
}
Using Java nio.
In my Java code I use a FileVisitor to traverse a filesystem and creating a structure of Paths, then later on this is converted to a json object for rendering in html.
Running on Windows it runs okay even against a linux filesystem, running on Linux against the same (now local) filesystem it fails to render special characters properly when call toString() on a path
i.e Windows debug output
CreateFolderTree:createJsonData:SEVERE: AddingNode(1):Duarte Lôbo- Requiem
and html displays ok as
Duarte Lôbo- Requiem
but linux debug output gives
CreateFolderTree:createJsonData:SEVERE: AddingNode(1):Duarte L??bo- Requiem
and html displays as two black diamond with question mark in them instead of the ô char
Why is this happening, the Paths are provided by the the FileVisitor class so must be getting constructed properly (i.e I am not hacking it myself) , and then i just call toString() on the path.
Is it a fonts problem, I have had some issues with fonts on the linux system but here I am just returning Strings to the html so cannot see a conection.
Probably an encoding issue, but I cant see a place where I am explicitly setting an encoding
Bulk of code below, debugging showing invalid output for linux is in the createJsonData() method
Edit:I have fixed the logging issue so that the output is written as UTF-8
FileHandler fe = new FileHandler(logFileName, LOG_SIZE_IN_BYTES, 10, true);
fe.setEncoding(StandardCharsets.UTF_8.name());
So we now see Windows is outputting correctly
CreateFolderTree:createJsonData:SEVERE: AddingNode(1):Duarte Lôbo- Requiem
but Linux is outputting
CreateFolderTree:createJsonData:SEVERE: AddingNode(1):Duarte L��bo- Requiem
and if I view this in HexEditor it gives this output for L��bo
4C EF BF BD EF BF BD 62 6F
Edit:Partial Solution
I came across What exactly is sun.jnu.encoding?
and found it was recommended to add this
-Dsun.jnu.encoding=UTF-8
and it worked files were now displayed okay
Unfortunately if user then clicked on such a file and sent back to server I now get this error
java.lang.NullPointerException
at java.base/sun.nio.fs.UnixPath.normalizeAndCheck(Unknown Source)
at java.base/sun.nio.fs.UnixPath.<init>(Unknown Source)
at java.base/sun.nio.fs.UnixFileSystem.getPath(Unknown Source)
at java.base/java.nio.file.Paths.get(Unknown Source)
at com.jthink.songkong.server.callback.ServerFixSongs.configureFileMapping(ServerFixSongs.java:59)
at com.jthink.songkong.server.callback.ServerFixSongs.startTask(ServerFixSongs.java:88)
at com.jthink.songkong.server.CmdRemote.lambda$null$36(CmdRemote.java:107)
I tried adding -Dfile.encoding=UTF-8 both in addtion or instead of the jnu option and that didnt help , the jnu option was the one I needed.
I shoudn't have to add this undocumented sun-jnu-encoding option so it seems to be that the server is broken in some way ?
Code
import com.google.common.base.Strings;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.jthink.songkong.analyse.analyser.Counters;
import com.jthink.songkong.analyse.general.Errors;
import com.jthink.songkong.cmdline.SongKong;
import com.jthink.songkong.fileloader.RecycleBinFolderNames;
import com.jthink.songkong.server.fs.Data;
import com.jthink.songkong.server.fs.PathWalker2;
import com.jthink.songkong.server.fs.State;
import com.jthink.songkong.ui.MainWindow;
import com.jthink.songkong.ui.progressdialog.FixSongsCounters;
import spark.Request;
import spark.Response;
import java.io.IOException;
import java.net.UnknownHostException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
/**
* Count the number of files that can be loaded, for information purposes only
*/
public class CreateFolderTree
{
private Path treeRoot;
Set<Path> keys = new HashSet<Path>();
public static class VisitFolder
extends SimpleFileVisitor<Path>
{
private Set<Path> keys;
private Integer maxDepth;
private int depth;
public VisitFolder(Set<Path> keys, Integer maxDepth)
{
this.keys=keys;
this.maxDepth = maxDepth;
}
/**
*
* #param dir
* #param attrs
* #return
* #throws IOException
*/
/*
* Ignore some dirs
* #param dir
* #param attrs
* #return
* #throws IOException
*/
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException
{
try
{
if (dir.toFile().getName().equals(".AppleDouble"))
{
return FileVisitResult.SKIP_SUBTREE;
}
else if (dir.toString().equals("/proc"))
{
return FileVisitResult.SKIP_SUBTREE;
}
else if (dir.toString().equals("/dev"))
{
return FileVisitResult.SKIP_SUBTREE;
}
else if (RecycleBinFolderNames.isMatch(dir.toFile().getName()))
{
MainWindow.logger.log(Level.SEVERE, "Ignoring " + dir.toString());
return FileVisitResult.SKIP_SUBTREE;
}
else if (dir.toString().toLowerCase().endsWith(".tar"))
{
return FileVisitResult.SKIP_SUBTREE;
}
depth++;
if(depth > maxDepth)
{
depth--;
return FileVisitResult.SKIP_SUBTREE;
}
keys.add(dir);
return super.preVisitDirectory(dir, attrs);
}
catch(IOException e)
{
MainWindow.logger.warning("Unable visit dir:"+dir + ":"+e.getMessage());
return FileVisitResult.SKIP_SUBTREE;
}
}
/**
*
* Tar check due to http://stackoverflow.com/questions/14436032/why-is-java-7-files-walkfiletree-throwing-exception-on-encountering-a-tar-file-o/14446993#14446993
* SONGKONG-294:Ignore exceptions if file is not readable
*
* #param file
* #param exc
* #return
* #throws IOException
*/
#Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException
{
if (file.toString().endsWith(".tar")) {
//We dont log to reports as this is a bug in Java that we are handling not a problem in SongKong
MainWindow.logger.log(Level.SEVERE, exc.getMessage());
return FileVisitResult.CONTINUE;
}
try
{
FileVisitResult result = super.visitFileFailed(file, exc);
return result;
}
catch(IOException e)
{
MainWindow.logger.warning("Unable to visit file:"+file + ":"+e.getMessage());
return FileVisitResult.CONTINUE;
}
}
/**
* SONGKONG-294:Ignore exception if folder is not readable
*
* #param dir
* #param exc
* #return
* #throws IOException
*/
#Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc)
throws IOException
{
depth--;
try
{
FileVisitResult result = super.postVisitDirectory(dir, exc);
return result;
}
catch(IOException e)
{
MainWindow.logger.warning("Unable to count files in dir(2):"+dir);
return FileVisitResult.CONTINUE;
}
}
}
public CreateFolderTree(Path treeRoot)
{
this.treeRoot = treeRoot;
}
public String start(int depth)
{
VisitFolder visitFolder;
try
{
if(treeRoot==null)
{
for (Path path : FileSystems.getDefault().getRootDirectories())
{
visitFolder = new VisitFolder(keys, depth);
Files.walkFileTree(path, visitFolder);
}
}
else
{
visitFolder = new VisitFolder(keys, depth);
Files.walkFileTree(treeRoot, visitFolder);
}
PathWalker2 pw = new PathWalker2();
for (Path key : keys)
{
//SONGKONG-505: Illegal character in Filepath problem prevented reportFile creation
try
{
pw.addPath(key);
}
catch (InvalidPathException ipe)
{
MainWindow.logger.log(Level.SEVERE, ipe.getMessage(), ipe);
}
}
Gson gson = new GsonBuilder().create();
return gson.toJson(createJsonData(pw.getRoot()));
}
catch (Exception e)
{
handleException(e);
}
return "";
}
public void handleException(Exception e)
{
MainWindow.logger.log(Level.SEVERE, "Unable to count files:"+e.getMessage(), e);
Errors.addError("Unable to count files:"+e.getMessage());
MainWindow.logger.log(Level.SEVERE, e.getMessage());
Counters.getErrors().getCounter().incrementAndGet();
SongKong.refreshProgress(FixSongsCounters.SONGS_ERRORS);
}
/**
* Add this node and recursively its children, returning json data representing the tree
*
* #param node
* #return
*/
private Data createJsonData(PathWalker2.Node node)
{
Data data = new Data();
if(node.getFullPath()!=null)
{
data.setId(node.getFullPath().toString());
if(node.getFullPath().getFileName()!=null)
{
MainWindow.logger.severe("AddingNode(1):"+node.getFullPath().getFileName().toString());
data.setText(node.getFullPath().getFileName().toString());
}
else
{
MainWindow.logger.severe("AddingNode(2):"+node.getFullPath().toString());
data.setText(node.getFullPath().toString());
}
}
else
{
try
{
data.setText(java.net.InetAddress.getLocalHost().getHostName());
data.setId("#");
State state = new State();
state.setOpened(true);
data.setState(state);
}
catch(UnknownHostException uhe)
{
data.setText("Server");
}
}
//Recursively add each child folder of this node
Map<String, PathWalker2.Node> children = node.getChildren();
if(children.size()>0)
{
data.setChildren(new ArrayList<>());
for (Map.Entry<String, PathWalker2.Node> next : children.entrySet())
{
data.getChildren().add(createJsonData(next.getValue()));
}
}
else
{
data.setBooleanchildren(true);
}
return data;
}
public static String createFolderJsonData(Request request, Response response)
{
if(Strings.nullToEmpty(request.queryParams("id")).equals("#"))
{
CreateFolderTree cft = new CreateFolderTree(null);
String treeData = cft.start(1).replace("booleanchildren", "children");
return treeData;
}
else
{
CreateFolderTree cft = new CreateFolderTree(Paths.get(request.queryParams("id")));
String treeData = cft.start(2 ).replace("booleanchildren", "children");
return treeData;
}
}
}
import java.nio.file.Path;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
/** Constructs a tree of folders based on a list of filepaths
*
* i.e a give it a list if all folder that contain files that have been modified and it creates a hierachy
* that can then be used to generate a data structure for use by jstree
*
*/
public class PathWalker2
{
private final Node root;
public PathWalker2()
{
root = new Node();
}
public Node getRoot()
{
return root;
}
/**
* Represent a node on the tree (may/not have children)
*/
public static class Node
{
//Keyed on name and node
private final Map<String, Node> children = new TreeMap<>();
private Path fullPath;
public Node addChild(String name)
{
if (children.containsKey(name))
return children.get(name);
Node result = new Node();
children.put(name, result);
return result;
}
public Map<String, Node> getChildren()
{
return Collections.unmodifiableMap(children);
}
public void setFullPath(Path fullPath)
{
this.fullPath = fullPath;
}
public Path getFullPath()
{
return fullPath;
}
}
/**
* #param path
*/
public void addPath(Path path)
{
Node node = root.addChild((path.getRoot().toString().substring(0, path.getRoot().toString().length() - 1)));
//For each segment of the path add as child if not already added
for (int i = 0; i < path.getNameCount(); i++)
{
node = node.addChild(path.getName(i).toString());
}
//Set full path of this node
node.setFullPath(path);
}
}
So as always with encoding problems this has been a lot of work to debug. Not only are there a lot of different things that affect it, they also affect it at different times, so the first task is always to check where does it go wrong first.
As the deal with the � showed, once it goes wrong, it can then go more wrong and if you try to debug starting from the end result, it's like peeling layers from a rotten onion.
In this case the root of the problem was in the OS locale, which was set to POSIX. This old standard makes your OS act like it's from the 70's, with ASCII encoding and other outdated details. The ASCII encoding will prevent the OS from understanding filenames, text or anything containing more exotic characters. This causes weird issues because the JVM is doing just fine by itself, but any time it communicates with the OS (printing to a text file, asking to open a file with a certain name) there's a chance of corruption because the OS doesn't understand what the JVM is saying.
It's like someone is talking to you and every once in a while he puts a word of Chinese in there. You're writing down what he says in English, but every Chinese word you replace with "Didn't understand???".
The locale (in /etc/default/locale) usually contains sane defaults, but as we saw here, you can't always trust that. For any modern systems you'll want locale values like en_EN.UTF-8. You never want to see POSIX there in this day and age.
For html you either need to set a proper charset matching your needs or better stick with ASCII and use html-encoding for all non-ASCII characters.
This works even if no specific charset is defined for you html display.
https://en.wikipedia.org/wiki/Unicode_and_HTML
It seems that your debug output goes through multiple conversions between charsets. The text you send to the console seems to be converted to bytes using UTF-8 as encoding resulting into the conversion from ô to ô. Then there seems to be another conversion from the byte-data back into characters using the system's charset. Windows' console uses cp1252 as charset while Linux has different settings on a per installation basis. In your case it seems to be ASCII leading to the conversion to the two ? for the UTF-8 encoded data because these bytes have values that aren't defined in ASCII.
I don't know the logging framework you're using or what the specific setup of the Logger is you're using, so I can't tell you how to fix that, but for the Linux-variant, you might check the console's charset and change it to UTF-8 to see if that has the desired effect.
I want to change .class file's method. I installed JD Eclipse Decompiler and opened the .class file. I added some codes and save .class file. But, .class file is not changing.
I don't know how to use decompiler. And if is it possible, how to change .class file without using decompiler.
I am using Ubuntu.
Regards
EDIT:
Here is my decompiled code:
/* */ package org.hibernate.id;
/* */
/* */ import java.io.Serializable;
/* */ import java.sql.ResultSet;
/* */ import java.sql.SQLException;
/* */ import java.util.HashMap;
/* */ import java.util.Properties;
/* */ import org.apache.commons.logging.Log;
/* */ import org.apache.commons.logging.LogFactory;
/* */ import org.hibernate.HibernateException;
/* */ import org.hibernate.MappingException;
/* */ import org.hibernate.dialect.Dialect;
/* */ import org.hibernate.type.Type;
/* */ import org.hibernate.util.ReflectHelper;
/* */
/* */ public final class IdentifierGeneratorFactory
/* */ {
/* 25 */ private static final Log log = LogFactory.getLog(IdentifierGeneratorFactory.class);
/* */
/* 64 */ private static final HashMap GENERATORS = new HashMap();
/* */
/* 66 */ public static final Serializable SHORT_CIRCUIT_INDICATOR = new Serializable() {
/* */ public String toString() { return "SHORT_CIRCUIT_INDICATOR";
/* */ }
/* 66 */ };
/* */
/* 70 */ public static final Serializable POST_INSERT_INDICATOR = new Serializable() {
/* */ public String toString() { return "POST_INSERT_INDICATOR";
/* */ }
/* 70 */ };
/* */
/* */ public static Serializable getGeneratedIdentity(ResultSet rs, Type type)
/* */ throws SQLException, HibernateException, IdentifierGenerationException
/* */ {
/* 32 */ if (!(rs.next())) {
/* 33 */ throw new HibernateException("The database returned no natively generated identity value");
/* */ }
/* 35 */ Serializable id = get(rs, type);
/* */
/* 37 */ if (log.isDebugEnabled()) log.debug("Natively generated identity: " + id);
/* 38 */ return id;
/* */ }
/* */
/* */ public static Serializable get(ResultSet rs, Type type)
/* */ throws SQLException, IdentifierGenerationException
/* */ {
/* 45 */ Class clazz = type.getReturnedClass();
/* 46 */ if (clazz == Long.class) {
/* 47 */ return new Long(rs.getLong(1));
/* */ }
/* 49 */ if (clazz == Integer.class) {
/* 50 */ return new Integer(rs.getInt(1));
/* */ }
/* 52 */ if (clazz == Short.class) {
/* 53 */ return new Short(rs.getShort(1));
/* */ }
/* 55 */ if (clazz == String.class) {
/* 56 */ return rs.getString(1);
/* */ }
if(clazz == java.math.BigDecimal.class){
return rs.getBigDecimal(1);
}
/* */
/* 59 */ throw new IdentifierGenerationException("this id generator generates long, integer, short or string78");
/* */ }
/* */
/* */ public static IdentifierGenerator create(String strategy, Type type, Properties params, Dialect dialect)
/* */ throws MappingException
/* */ {
/* */ try
/* */ {
/* 92 */ Class clazz = getIdentifierGeneratorClass(strategy, dialect);
/* 93 */ IdentifierGenerator idgen = (IdentifierGenerator)clazz.newInstance();
/* 94 */ if (idgen instanceof Configurable) ((Configurable)idgen).configure(type, params, dialect);
/* 95 */ return idgen;
/* */ }
/* */ catch (Exception e) {
/* 98 */ throw new MappingException("could not instantiate id generator", e);
/* */ }
/* */ }
/* */
/* */ public static Class getIdentifierGeneratorClass(String strategy, Dialect dialect) {
/* 103 */ Class clazz = (Class)GENERATORS.get(strategy);
/* 104 */ if ("native".equals(strategy)) clazz = dialect.getNativeIdentifierGeneratorClass();
/* */ try {
/* 106 */ if (clazz == null) clazz = ReflectHelper.classForName(strategy);
/* */ }
/* */ catch (ClassNotFoundException e) {
/* 109 */ throw new MappingException("could not interpret id generator strategy: " + strategy);
/* */ }
/* 111 */ return clazz;
/* */ }
/* */
/* */ public static Number createNumber(long value, Class clazz) throws IdentifierGenerationException {
/* 115 */ if (clazz == Long.class) {
/* 116 */ return new Long(value);
/* */ }
/* 118 */ if (clazz == Integer.class) {
/* 119 */ return new Integer((int)value);
/* */ }
/* 121 */ if (clazz == Short.class) {
/* 122 */ return new Short((short)(int)value);
/* */ }
/* */
/* 125 */ throw new IdentifierGenerationException("this id generator generates long, integer, short");
/* */ }
/* */
/* */ static
/* */ {
/* 75 */ GENERATORS.put("uuid", UUIDHexGenerator.class);
GENERATORS.put("hilo", TableHiLoGenerator.class);
GENERATORS.put("assigned", Assigned.class);
GENERATORS.put("identity", IdentityGenerator.class);
GENERATORS.put("select", SelectGenerator.class);
GENERATORS.put("sequence", SequenceGenerator.class);
GENERATORS.put("seqhilo", SequenceHiLoGenerator.class);
GENERATORS.put("increment", IncrementGenerator.class);
GENERATORS.put("foreign", ForeignGenerator.class);
GENERATORS.put("guid", GUIDGenerator.class);
GENERATORS.put("uuid.hex", UUIDHexGenerator.class);
GENERATORS.put("sequence-identity", SequenceIdentityGenerator.class);
}
}
You can follow these steps to modify your java class:
Decompile the .class file as you have done and save it as .java
Create a project in Eclipse with that java file, the original JAR
as library, and all its dependencies
Change the .java and compile
Get the modified .class file and put it again inside the original
JAR.
Use a bytecode editor, like:
http://set.ee/jbe/
Be careful because you need a very good knowledge of the Java bytecode.
You can also change the class at runtime with bytecode weaving (like AspectJ).
Recaf is a decompiler GUI that allows you to edit and recompile files. Here is the relevant portion of the manual:
The easiest way to edit bytecode is to edit it as decompiled source. This does not work for highly complex cases and obfuscation, but for most simple edits its all you’ll ever need to do. Simply open the class, make your changes as plain Java code, and save.
I added some codes and save .class file.
What you see in JD EClipse Decompiler is decompiled representation of byte code in the .class file. Even though you change the text it won't affect the byte code.
when you decompile and change the code you have to go on the root folder of your eclipse project and check your class in bin folder wich is on the same level as src. then open you original jar with zip tool ( 7zip is good for that ) and put the modified class in tha same package inside the jar.
Use java assist Java library for manipulating the Java bytecode (.class file) of an application.
-> Spring , Hibernate , EJB using this for proxy implementation
-> we can bytecode manipulation to do some program analysis
-> we can use Javassist to implement a transparent cache for method return values, by intercepting all method invocations and only delegating to the super implementation on the first invocation.
You can use any decompiler to first decompile the file.
I had once faced a simillar problem where I didn't have source code of the application and had to make a very small change in a file.
Below is what I did:
Extracted the class file from the jar
Opened it in a decompiler (I use JD GUI, you can get it easily from many resources on internet)
You may download it from here
You can actually view all the files in a jar using JD GUI.
Made changes to the file I wanted to and copied all the code from that file
Created a new project in eclipse with only this class (with the same package structure as in the jar), provided the original jar as library and all other dependencies.
Compiled the class, injected the .class file back to the jar from bin folder of my workspace
Tested my change, celebrated it by sipping a cup of coffee :)
You can change the code when you decompiled it, but it has to be recompiled to a class file, the decompiler outputs java code, this has to be recompiled with the same classpath as the original jar/class file
Sometime we need to compile one single file out of thousand files to fix the problem. In such a case, One can create same folder structure like class path, decompile the file into java or copy java file from source code. Make required changes, compile one particular file into class with all dependency/classes in place and finally replace the class file. Finally restart the container. Once war is exploded file will not be replaced.
As far as I've been able to find out, there is no simple way to do it. The easiest way is to not actually convert the class file into an executable, but to wrap an executable launcher around the class file. That is, create an executable file (perhaps an OS-based, executable scripting file) which simply invokes the Java class through the command line.
If you want to actually have a program that does it, you should look into some of the automated installers out there.
Here is a way I've found:
[code]
import java.io.*;
import java.util.jar.*;
class OnlyExt implements FilenameFilter{
String ext;
public OnlyExt(String ext){
this.ext="." + ext;
}
#Override
public boolean accept(File dir,String name){
return name.endsWith(ext);
}
}
public class ExeCreator {
public static int buffer = 10240;
protected void create(File exefile, File[] listFiles) {
try {
byte b[] = new byte[buffer];
FileOutputStream fout = new FileOutputStream(exefile);
JarOutputStream out = new JarOutputStream(fout, new Manifest());
for (int i = 0; i < listFiles.length; i++) {
if (listFiles[i] == null || !listFiles[i].exists()|| listFiles[i].isDirectory())
System.out.println("Adding " + listFiles[i].getName());
JarEntry addFiles = new JarEntry(listFiles[i].getName());
addFiles.setTime(listFiles[i].lastModified());
out.putNextEntry(addFiles);
FileInputStream fin = new FileInputStream(listFiles[i]);
while (true) {
int len = fin.read(b, 0, b.length);
if (len <= 0)
break;
out.write(b, 0, len);
}
fin.close();
}
out.close();
fout.close();
System.out.println("Jar File is created successfully.");
} catch (Exception ex) {}
}
public static void main(String[]args){
ExeCreator exe=new ExeCreator();
FilenameFilter ff = new OnlyExt("class");
File folder = new File("./examples");
File[] files = folder.listFiles(ff);
File file=new File("examples.exe");
exe.create(file, files);
}
}
[/code]`
When I run the code below I get the following error.
C:\Documents and Settings\BOS\Desktop\test>java -jar test.jar
Exception in thread "main" java.lang.NullPointerException
at sun.launcher.LauncherHelper.getMainClassFromJar(Unknown Source)
at sun.launcher.LauncherHelper.checkAndLoadMain(Unknown Source)
I've got these files in \test directory = crimson.jar robosuite-api.jar and test.jar.
Here is the example they give to launch a robot?
import com.kapowtech.robosuite.api.java.rql.*;
public class SimpleRunRobot {
public static void main(String[] args) {
if (args.length < 1) {
System.out.println("Usage: RunRobot <robotURL>");
System.exit(1);
}
try {
// Run the robot
RQLResult result =
RobotExecutor.getRobotExecutor().execute(args[0]);
// Output the results
System.out.println(result);
}
catch (RQLException e) {
System.out.println("An error occurred: " + e);
}
}
}
Why is this giving me that Unknown Source error?
package robosuite.robots;
import com.kapowtech.robosuite.api.java.rql.RQLException;
import com.kapowtech.robosuite.api.java.rql.RQLResult;
import com.kapowtech.robosuite.api.java.rql.RobotExecutor;
import com.kapowtech.robosuite.api.java.rql.construct.RQLObjects;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
*
* <p>
* This is an autogenerated class. It has been generated from the
* <code>library:/test.robot</code> file.
*
* #author RoboSuite
*/
public class Test {
// ----------------------------------------------------------------------
// Class fields
// ----------------------------------------------------------------------
private static final String ROBOT_URL = "library:/test.robot";
private static final RobotExecutor ROBOT_EXECUTOR = RobotExecutor.getRobotExecutor(SingletonRQLEngine.getInstance());
private static final Converter CONVERTER = Converter.getInstance();
// ----------------------------------------------------------------------
// Constructors
// ----------------------------------------------------------------------
/**
* Creates a new Test instance that can be used to execute the
* <code>library:/test.robot</code>.
*/
public Test() {
}
// ----------------------------------------------------------------------
// Instance methods
// ----------------------------------------------------------------------
/**
* Executes this robot.
*
* #param test an input object to the robot.
* #return an array of output objects.
* #throws java.io.IOException if the execution fails for some reason.
*/
public Testst[] run(Test0 test) throws java.io.IOException {
try {
// Prepare input objects
List parameters = new ArrayList();
parameters.add(test);
RQLObjects inputObjects = CONVERTER.convertBeansToRQLObjects(parameters);
// Run robot
RQLResult rqlResult = ROBOT_EXECUTOR.execute(ROBOT_URL, inputObjects);
// Extract output objects
RQLObjects outputObjects = rqlResult.getOutputObjects();
List result = CONVERTER.convertRQLObjectsToBeans(outputObjects);
return (Testst[]) result.toArray(new Testst[result.size()]);
} catch (RQLException e) {
throw new IOException(e.toString());
}
}
/* ------------------------------------------------------------------- */
}
If your using Java 7, Read this.
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7067922
Try
java -cp test.jar
include your other .jar files also
If you are using a manifest file make sure you have defined your main class.
for e.g.
Main-Class: test.MyApp
You have to add the name of the class having main() method in META-INF/manifest file.
Here is the link with more information :
http://java.sun.com/developer/Books/javaprogramming/JAR/basics/manifest.html
Thanks.
Why is this giving me that Unknown Source error?
The "unknown source" messages are not an error. It is the JVM telling you that the code that you are executing was compiled without any debug information; e.g. with the -gLnone option. As a result, the source file names and line numbers that would normally be included in the stacktrace are not available.
In this case, the code is some platform specific stuff that is internal to the JVM. Don't worry about it ...
I'm using a rdf crawler, in that I had a class named as:
import edu.unika.aifb.rdf.crawler.*;
import com.hp.hpl.jena.rdf.model.*;
import com.hp.hpl.jena.util.FileManager;
These are class file termed as error, and I try with jena packages but I had attached, it does not make any changes.
Update:
Full SampleCrawl.java class content:
import java.util.*;
import edu.unika.aifb.rdf.crawler.*;
/**
* Call this class with 3 arguments - URL to crawl to,
* depth and time in seconds
*/
public class SampleCrawl {
/**
* #param uRI
* #param depth
* #param time
*/
#SuppressWarnings("rawtypes")
public SampleCrawl(Vector uRI, Vector hf, int depth, int time){
// Initialize Crawling parameters
CrawlConsole c = new CrawlConsole(uRI,hf,depth,time);
// get an ontology file from its local location
// (OPTIONAL)
c.setLocalNamespace("http://www.daml.org/2000/10/daml-ont","c:\\temp\\rdf\\schemas\\daml-ont.rdf");
// set all the paths to get all the results
c.setLogPath("c:\\temp\\crawllog.xml");
c.setCachePath("c:\\temp\\crawlcache.txt");
c.setModelPath("c:\\temp\\crawlmodel.rdf");
try{
// crawl and get RDF model
c.start();
// This writes all three result files out
c.writeResults();
}catch(Exception e){
}
}
/**
* #param args
* #throws Exception
*/
#SuppressWarnings({ "rawtypes", "unchecked" })
public static void main(String[] args) throws Exception {
if (args.length != 3) {
System.err.println("Usage: java -cp [JARs] SampleCrawl [URL] [depth:int] [time:int]");
System.exit(0);
}
Vector uris = new Vector();
uris.add(args[0]);
// no host filtering - crawl to all hosts
Vector hostfilter = null;
/* You may want to do something else to enable host filtering:
* Vector hostfilter = new Vector();
* hostfilter.add("http://www.w3.org");
*/
int depth = 2;
int time = 60;
try {
depth = Integer.parseInt(args[1]);
time = Integer.parseInt(args[2]);
}
catch (Exception e) {
System.err.println("Illegal argument types:");
System.err.println("Argument list: URI:String depth:int time(s):int");
System.exit(0);
}
new SampleCrawl(uris,hostfilter,depth,time);
}
}
Question:
How to add import edu.unika.aifb.rdf.crawler.; error occurs here
I googled the package that you're trying to import, and it appears that you're using Kaon. Assuming that's so, you have made an error in your import declaration. You have:
import edu.unika.aifb.rdf.crawler.*;
whereas the download available on SourceForge would require:
import edu.unika.aifb.rdf.rdfcrawler.*;
As an aside, it would be helpful if you would include information, such as "I'm trying to use Kaon's rdfcrawler from ..." in your question. Otherwise, we have to try to guess important details in your setup.