Get version of a .pkg file? - java

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);
}

Related

JDK Mission Control: Modifying Stack data from jfr files

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!

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;
}
}

How to access version of my project in build.gradle from a Java class

I'm quite new to Gradle so the answer might be simple, so I apologize if the answer is simple: I have a testing tool that needs to fetch it's version and compare it to the version of the application it is testing. However , the version of my tool is in my build.graddle as
version '1.0'
I tried different way to access it ( such as) :
task generateSources {
File outDir
outDir = file("$buildDir/classes/test/tests/Version.java")
doFirst {
outDir.exists() || outDir.mkdirs()
new File(outDir).write("public class Version { public static final String VERSION = \"$project.version\"; }")
}
}
compileJava.dependsOn generateSources
compileJava.source generateSources.outputs.files, sourceSets.main.java
I found this piece of code to output the version to another java file, but I fail to see how I'd be able to retrieve that info afterwards ( I mean, my tests are defined in src and I would need to point to a file that doesn't exist at compilation -- correct me if I'm wrong here).
Any idea on how I could accomplish this task?
First of all, you are trying to create java source file in your build/classes (it should contain compiled classes, not sources) directory, but you have to do it in your sources, otherwise it won't be compiled. And if you need this new class to be vailable not for tests, then use src/main/java, not src/test/java/
But anyway, I suppose for your case it's much easier to use some properties file for that and replace some token within it during build. That will allow you to make some static logic to get this property value and use it yet before running the build. So all you need is:
1- to have some properties file in your resources src/main/resources (for example app.properties), where should version variable be stored, with it's value like APP_VERSION_TOKEN
version=%APP_VERSION_TOKEN%
2- configure you Gradle processResources to replace tokens, something like this:
processResources {
filesMatching('**/app.properties') {
filter {
it.replace('%APP_VERSION_TOKEN%', version)
}
}
}
3- make some method to read this file and return the value of the property and use it where you need.
And that's all. For unit tests you can have another file with the same name under src/test/resource with the unchanging value you need for testing.

Using XLLoop for java

XLLoop is opensource framework to java. For example we can use function from java in excel. Below is very simple example of usage:
package org.boris.xlloop.util;
import org.boris.xlloop.FunctionServer;
import org.boris.xlloop.handler.*;
import org.boris.xlloop.reflect.*;
public class ServerExample
{
public static void main(String[] args) throws Exception {
// Create function server on the default port
FunctionServer fs = new FunctionServer();
// Create a reflection function handler and add the Math methods
ReflectFunctionHandler rfh = new ReflectFunctionHandler();
rfh.addMethods("Math.", Math.class);
rfh.addMethods("Math.", Maths.class);
rfh.addMethods("CSV.", CSV.class);
rfh.addMethods("Reflect.", Reflect.class);
// Create a function information handler to register our functions
FunctionInformationHandler firh = new FunctionInformationHandler();
firh.add(rfh.getFunctions());
// Set the handlers
CompositeFunctionHandler cfh = new CompositeFunctionHandler();
cfh.add(rfh);
cfh.add(firh);
fs.setFunctionHandler(new DebugFunctionHandler(cfh));
// Run the engine
System.out.println("Listening on port " + fs.getPort() + "...");
fs.run();
}
}
I understand it and generally programm is working. But if I go to excel, it isn't working.
I try:
=FS("Math.random")
=Math.random()
But I got #NAME? twice
So I suppose that I should make something yet. Could you tell me step by step how configure excel and java to do this? What should I do with xlloop-0.3.2 (Microsoft Excel XLL Add-In) file ?
I tried running the code and I got the following output.
All I had to do was to launch Excel and add the XLLoop addin.
. Press Alt+G or click on the Go button beside Manage Excel Add-ins.
. Click on Browse and provide the path to the xlloop-0.3.2.xll file. If you had downloaded xlloop-0.3.2.zip,extract it and you will find it inside /xlloop/bin
Hope that helps.
Edit:
Launch Excel.
Start the server(run the Main class) and test the formulas.
I tested the following 2(typed them on the Excel formula bar and hit/press Enter) and it worked fine. :)
=FS("Math.sin", 3.14)
=FS("Math.random")
For anyone running into the same problem:
You have to add the correct Addin Version to your excel installation.
If you are getting the error message: "The file format and extension [...] don't match.", it means that you are using a 64bit excel and the 32bit XLLoop addin.
You have to either install a 32bit Excel, or you have to get the 64bit version of the XLLoop Plugin.
We ran into the same problem, and created a 64bit version of the addin: https://github.com/PATRONAS/xlloop

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'

Categories

Resources