JDK Mission Control: Modifying Stack data from jfr files - java

Like this question - I'm trying to load in an existing jfr file that has been recorded on another machine external to our organisation. I now want to deobfuscate the information, either as a plugin for JDK Mission Control, or as a utility for reading in a jfc file and writing out a de-obfuscated version.
My class does the relevant implementation of the API
public class JFRProcessor implements IParserExtension {
//impementation details below
And I have tested it (successfully) with the following
List<File> files = new ArrayList<>();
files.add(new File("/user/rafe/Input001.jfr"));
List<IParserExtension> extensions = new ArrayList<>();
extensions.add(new JFRProcessor());
IItemCollection events = JfrLoaderToolkit.loadEvents(files, extensions);
//write out to xml to validate the change
RecordingPrinter printer = new RecordingPrinter(new PrintWriter(new File("/user/rafe/Output0001.xml")), Verbosity.HIGH, false);
printer.print(events);
When I then try to export this as a jar, I have the fully qualified classname (com.extension.JFRProcessor) in the relevant META-INF/services/org.openjdk.jmc.flightrecorder.parser.IParserExtension file - and JDK Mission Control doesn't do anything with the plugin (when put in the drop-ins directory).
This was then verified by exporting the jar and in a separate project (with the exported jar in the build path):
ServiceLoader<IParserExtension> loader = ServiceLoader.load(IParserExtension.class,
IParserExtension.class.getClassLoader());
Another approach that I took was to write out the events:
I have also tried using the latest SNAPSHOT release of JDK Mission Control with the new Recordings class in org.openjdk.jmc.flightrecorder.writer.api but I am struggling to see how to get between the IItemCollection and any useful data to feed into the Recording instance that I'm trying to rewrite into.
final Recording rec = Recordings.newRecording("/user/rafe/Output-001.jfr");
events.forEach(event -> {
IType<IItem> type = event.getType();
rec.writeEvent(typedValue);
});
Any help would be appreciated for either approach - as I'm struggling to see how to use this without de-obfuscating the data first!

Related

Check if plugin used "compile files" when it should've used "provided"

Little background for context:
The application I support allows third parties to develop plugins that can leverage some of our functionality. We hand them our "externalAPI.jar"; they put it in their project, implement some interfaces, and build their own APK. We find the would-be plugin by asking the package manager for all installed applications and see if each has a "pluginclass.xml" in the assets directory. If it has that XML file, we anticipate its contents being the canonical path of a class that implements our ExternalPluginVX interface, and using a new PathClassLoader(ApplicationInfo.sourceDir, this.getClass().getClassLoader()), we load the class, create a new instance, and start using it.
The problem:
Sometimes third parties will put
compile files ("./libs/externalAPI.jar")
in their gradle files instead of the correct syntax:
provided files ("./libs/externalAPI.jar")
The result of course being things don't work properly. Sometimes they almost work, but then have unpredictability in their behavior - usually involving vicious crashes. Notably, since their APK is well-formed in its own right, and the XML file is there, we'll see the plugin, load the target class successfully, instantiate it successfully, and things go haywire from there when they try and reference back to us.
The question:
Is there a way for my application to check at runtime if the other application compiled our API classes into their APK instead of using provided files like they should have?
A viable solution is to use a DexFile.
Since I already have the ApplicationInfo.sourceDir, I can just construct a DexFile and iterate through its contents.
//this variable's value assigned by iterating through context.getPackageManager().getInstalledApplications(0)
ApplicationInfo pkg;
String interfaceTheyShouldntHave = ExternalPluginVX.class.getCanonicalName(); //"com.project.external.ExternalPluginVX"
DexFile dexFile = new DexFile(pkg.sourceDir);
Enumeration<String> entries = dexFile.entries();
while(entries.hasMoreElements()){
String entry = entries.nextElement();
if(entry.equals(interfaceTheyShouldntHave)){
Toast.makeText(ctxt, "Plugin \"" + pluginName + "\" could not be loaded. Please use 'provided files' instead of 'compile files'", Toast.LENGTH_LONG).show();
return;
}
}

Loading java code at runtime

I got a little project where I have to compute a list. The computation depends on serveal factors.
The point is that these factors change from time to time and the user should be allowed to change this by it's self.
Up to now, the factors are hard-coded and no changes can be done without recompiling the code.
At the moment the code looks like this:
if (someStatement.equals("someString")) {
computedList.remove("something");
}
My idea is to make an editable and human readable textfile, configfile, etc. which is loaded at runtime/ at startup? This file should hold the java code from above.
Any ideas how to do that? Please note: The targeted PCs do not have the JDK installed, only an JRE.
An effective way of going about this is using a static initializer. Static Block in Java A good and concise explanation can be found under this link.
One option here that would allow this would be to use User Input Dialogs from the swing API - then you could store the users answer's in variables and export them to a text file/config file, or just use them right in the program without saving them. You would just have the input dialogs pop up at the very beginning of the program before anything else happens, and then the program would run based off those responses.
You could use Javascript for the configuration file language, instead of java. Java 7 SE and later includes a javascript interpreter that you can call from Java. it's not difficult to use, and you can inject java objects into the javascript environment.
Basically, you'd create a Javascript environment, insert the java objects into it which the config file is expected to configure, and then run the config file as javascript.
Okay, here we go... I found an quite simple solution for my problem.
I am using Janino by Codehaus (Link). This library has an integrated Java compiler and seems to work like the JavaCompiler class in Java 7.
BUT without having the JDK to be installed.
Through Janino you can load and compile *.java files(which are human readable) at runtime, which was exactly what I needed.
I think the examples and code-snippets on their homepage are just painful, so here's my own implementation:
Step one is to implement an interface with the same methods your Java file has which is loaded at runtime:
public interface ZuordnungInterface {
public ArrayList<String> Zuordnung(ArrayList<String> rawList);}
Then you call the Janino classloader when you need the class:
File janinoSourceDir = new File(PATH_TO_JAVAFILE);
File[] srcDir = new File[] { janinoSourceDir };
String encoding = null;
ClassLoader parentClassLoader = this.getClass().getClassLoader();
ClassLoader cl = new JavaSourceClassLoader(parentClassLoader, srcDir,
encoding);
And create an new instance
ZuordnungsInterface myZuordnung = (ZuordnungInterface) cl.loadClass("zuordnung")
.newInstance();
Note: The class which is loaded is named zuordnung.java, so there is no extension needed in the call cl.loadClass("zuordnung").
And finaly the class I want to load and compile at runtime of my program, which can be located wherever you want it to be (PATH_TO_JAVAFILE):
public class zuordnung implements ZuordnungInterface {
public ArrayList<String> Zuordnung(ArrayList<String> rawList){
ArrayList<String> computedList = (ArrayList<String>) rawList.clone();
if (Model.getSomeString().equals("Some other string")) {
computedList.add("Yeah, I loaded an external Java class");
}
return computedList;
}}
That's it. Hope it helps others with similar problems!

Magnolia cms: resources module proper usage

I am learning magnolia cms. I am trying to use the resources module. I have actually 2 problems.
Cannot upload a bunch of files. I have a few files, but in some time I will have to upload some more. Modules import feature wants me to upload an xml file. But I don't know how to generate it properly. Tried to import through JCR, but after that I can't see those files in resources app. Tried to configure the module to search files in file system: I set fileSystemLoader to class info.magnolia.module.resources.loaders.FileSystemResourceLoader and set some path. It did not work for me too. Maybe I just don't understand at what time should be activated files upload feature. At the application start up time it did not work.
How to properly use these resources in my template? What ftl tag should I use?
I don't use STK module.
Thanks for your patience if you decide to help me.
Magnolia version: 5.2 CE
JDK iced tea: 1.7.0_51
OS: Linux/OpenSUSE 12.3
I've used previously (on 4.5.x) script below to perform the task via groovy module. It should work on 5.2 as well.
import static groovy.io.FileType.FILES
import info.magnolia.jcr.util.NodeUtil
import org.apache.commons.lang.StringUtils
import info.magnolia.cms.util.ContentUtil
class Globals {
static def folderName = '//some/folder/in/filesystem/on/server'
}
def loadImageFolder() {
session = ctx.getJCRSession("resources")
parentFolder = session.getNode("/templating-kit/jelinek-image/obrazky-produkty")
new File(Globals.folderName).eachFileRecurse(FILES) {
name = it.name
// set file name
extension = StringUtils.substringAfterLast(name, '.')
name = StringUtils.substringBeforeLast(name, '.')
// persist
resource = NodeUtil.createPath(parentFolder,name , "mgnl:content")
// persistResource
resource.setProperty("mgnl:template", "resources:binary")
resource.setProperty("extension", extension)
binary = resource.addNode("binary", "mgnl:resource")
binary.setProperty("jcr:data", new FileInputStream(it.absolutePath))
binary.setProperty("extension", extension)
binary.setProperty("fileName", name)
binary.setProperty("jcr:mimeType", "image/"+extension)
binary.setProperty("size", it.length())
}
session.save()
}
loadImageFolder()
return 'done'

Get version of a .pkg file?

I have a Java application for MAC OSX that I have coded and made a .pkg of it. While creating the .pkg I gave it a version number also. Now I need to get the version number of this application in my java code so that i can check for updates when the application runs. When I right-click on my app file it doesn't show me the version I entered while creating the package.
Do I need to set the version of my app file that I created using the jar bundler for building the pkg???
Please suggest me how I could accomplish this.
The version number you set while creating the package (in the PackageMaker Project) is the version of the installer, not the version of your .app-File. It is needed, so that another installer can see if he downgrades the current installation or not. The installer will never ever look at the contents it is installing to the system.
To set the version of your your .app-Bundle, right-click your .app-file and select "Show Package Contents" from the appearing menu. Open the folder "Contents", there you will find a file called "Info.plist". You have to edit this file and have to set your version-info for your application there. You can do this by using Property List Editor (included in the Apple Developer Tools) or another tool like BBEdit for example.
To read from your .plist in your application, you need a special library. I recommend the Java property list library from Daniel Dreibrodt (more information about the .plist-Format you'll find in this post on my blog).
Generelly, you should set the version-info of your app-bundle, anyway you use it for updating-purposes or not. If it is not set, the user has no chance to get information about the version he has installed without launching your software.
What you need is not the version of your .pkg file, you need the version of your .app-Bundle. Anyway - the version of your .pkg-file is handled the same way as your .app-file. There is also the Info.plist, where you find the informations. It can also be parsed with the same library.
The pkg is a zip file containing a.o. a file called PackageInfo. PackageInfo is an XML file looking like this:
<pkg-info format-version="2" identifier="com.mycompany.pkg.MyApp" version="1.2.0" overwrite-permissions="false" install-location="/" auth="root">
<payload installKBytes="4717" numberOfFiles="146"/>
<scripts>
<preinstall file="./preinstall"/>
<postinstall file="./postinstall"/>
</scripts>
<bundle-version>
<bundle path="./Applications/MyApp.app" CFBundleShortVersionString="1.2.0" CFBundleVersion="166" id="com.mycompany.MyApp" CFBundleIdentifier="com.mycompany.MyApp">
<bundle path="./Contents/Library/LoginItems/HelperApp.app" CFBundleShortVersionString="1.0" CFBundleVersion="1" id="com.mycompany.HelperApp" CFBundleIdentifier="com.mycompany.HelperApp"/>
</bundle>
</bundle-version>
</pkg-info>
To get the package version, you could use the following XPath:
pkg-info/#version
To get the application version:
pkg-info/bundle-version/bundle/#CFBundleShortVersionString
And the build number is here:
pkg-info/bundle-version/bundle/#CFBundleVersion
I know it is a quite old question but the answers are not satisfying. Here is my solution:
A MacOS .pkg file is an archive in XAR format. So any XAR archive reader can read its contents. I found an XAR reader for Java from Sprylab here. This library has Apache 2.0 license so it is free to use also for commercial products. It is quite old but it works. The file "Distribution" in the archive is in XML format and gives details about the installer bundle, e.g. the version ;)
I am using JSON so I did not want to add an XML reader for reading just one value. So the following code uses the XAR library and a custom XML reader to extract the version of the bundle in the .pkg installer.
public static void main(String [ ] args) throws XarException {
XarSource xar = new FileXarSource(new File("PathToPkgFile/PkgFilename.pkg"));
XarEntry entry = xar.getEntry("Distribution");
String distributionStr = new String(entry.getBytes());
String bundleVersionXml = getSubstringByStr(distributionStr, "<bundle-version>", "</bundle-version>");
String bundleAttrStr = getSubstringByStr(bundleVersionXml, "<bundle", "/>");
String version = getAttributeValue(bundleAttrStr, "CFBundleVersion");
System.out.println(bundleVersionXml);
System.out.println(bundleAttrStr);
System.out.println(version);
}
private static String getSubstringByStr(String xmlString, String start, String end) {
int startIdx = xmlString.indexOf(start);
int endIdx = xmlString.indexOf(end);
return xmlString.substring(startIdx + start.length(), endIdx);
}
private static String getAttributeValue(String tagContentString, String attribute) {
int startIdx = tagContentString.indexOf(attribute) + attribute.length() + "=\"".length();
int endIdx = startIdx + tagContentString.substring(startIdx).indexOf("\"");
return tagContentString.substring(startIdx, endIdx);
}

Getting all classes from the current workspace in Eclipse

am writing an Eclipse plugin, and I was trying to create a method that returns all the classes in the workspace in an ArrayList<\Class<\?>> (I added the "\" to include the generic brackets since html won't let me do so otherwise).
Here is the code I have:
private ArrayList<Class<?>> getAllClasses() throws JavaModelException {
ArrayList<Class<?>> classList = new ArrayList<Class<?>>();
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IWorkspaceRoot root = workspace.getRoot();
IProject[] projects = root.getProjects();
for (IProject project : projects) {
IJavaProject javaProject = JavaCore.create(project);
IPackageFragment[] packages = javaProject.getPackageFragments();
for (IPackageFragment myPackage : packages) {
IClassFile[] classes = myPackage.getClassFiles();
for (IClassFile myClass : classes) {
classList.add(myClass.getClass());
}
}
}
return classList;
}
This, however, doesn't seem to be working. I had some printlines, and I figured out that it also includes irrelevant classes (ie. classes from Java\jre6\lib\rt.jar). Any suggestions?
I'm not sure what you want to do:
In a running Eclipse plug-in, show all classes that are running in the JVM with the plug-in (i.e. classes for other editors, views, Eclipse machinery)?
In a running Eclipse plug-in, show all classes being built in open Java projects in the workspace? (Since you used the word "workspace", I suspect this is what you're looking for.)
Note in the latter case, you will not be able to get actual Java Class<...> objects, because the projects being edited and compiled are not loaded for execution into the same JVM as your plug-in. Your plug-in's code would be executing alongside the Eclipse IDE and Eclipse JDT tool code; the only time classes in open projects would be loaded for execution (producing Class<...> objects somewhere) would be when you launch or debug one of those projects . . . in which case you're dealing with a brand new JVM, and your plug-in is no longer around. Does that make sense?
If I am reading you right, I think you probably want to find "compilation units", not "class files". "Compilation units" correspond with .java source files, while "class files" correspond with pre-built binary class files (often in JARs). Or maybe you need both. Better yet, it sounds like what you really want are the "types" inside those.
Check out the JDT Core guide for some pretty good information that's remarkably difficult to find. Note that some analysis is possible at the Java Model level, but more detailed analyses (e.g. looking "inside" method definitions) will require parsing chunks of code into ASTs and going from there. The Java Model is pretty convenient to use, but the AST stuff can be a little daunting the first time out.
Also consider the Java search engine (documented near the above) and IType.newTypeHierarchy() for finding and navigating types.
Good luck!
You should try :
for (final ICompilationUnit compilationUnit : packageFragment.getCompilationUnits()) {
// now check if compilationUnit.exits
}
You don't get a CompilationUnit for binary types.
Maybe you can use IJavaProject.getAllPackageFragmentRoots() method to get all source folder,and then get ICompilationUnits in it.
I have much simpler solution for an eclipse project if you're just looking for listing java class names for the current package and add them to a list of classes (please replace PARENT_CLASS by the parent class name of all your classes):
List<PARENT_CLASS> arrayListOfClasses = new ArrayList<>();
String currentDir = new java.io.File("").toURI().toString().split("file:/")[1];
System.out.println("currentDir=" + currentDir);
String[] dirStringTab = currentDir.split("/");
String currentPackageName = dirStringTab[dirStringTab.length-1];
System.out.println("currentPackageName=" + currentPackageName);
File dir = new File("./src/" + currentPackageName + "/");
File[] filesList = dir.listFiles();
String javaClassNameWithoutExtension = "";
for (File file : filesList) {
if (file.isFile()) {
System.out.println(javaClassNameWithoutExtension);
javaClassNameWithoutExtension = file.getName().split(".java")[0];
Class c = Class.forName(javaClassNameWithoutExtension);
Object a = c.newInstance();
arrayListOfClasses.add(a);
}
}

Categories

Resources