A java agent has to upload a file in the background and return the Url of the uploaded file. While uploading, the agent has to report its progress.
I marked the agent to be "Run in background client thread.
I am stuck in the following dilemma:
I can run the agent from Lotus Script and pass its parameters in an In-Memory file, but the client actually doesn't run in its own thread. Instead, it blocks the whole client.
I can run the agent from a formula, but then I can't pass any parameters!
If I use Lotus Script and take care of the threading on my own in Java, my thread doesn't even get started!
I've read that the Notes client doesn't support multithreading. But I can't make the agent RunOnServer because it is accessing a web server available only for the client.
This is related to another question of mine by the way.
Is there any better solution for this?
If you can't make the agent RunOnServer then you can use LS2J instead of agent. Create your own class with threading and use its properties.
Here is example with custom Java Class and Java Timer:
import java.util.Timer;
import java.util.TimerTask;
public class Test
{
private boolean _isOver;
private int _counter;
private Timer _timer;
private String _url;
public Test()
{
_timer = new Timer("Timer");
}
public void Start() //Add parameters here that you want to use in Java
{
_counter = 0;
_isOver = false;
_url = "";
TimerTask timerTask = new TimerTask()
{
public void run()
{
if (_counter++ == 9)
{
_isOver = true;
_timer.cancel();
_url = "http://stackoverflow.com/";
}
}
};
_timer.scheduleAtFixedRate(timerTask, 30, 5000);
}
public int getCounter() { return _counter; }
public boolean getIsOver() { return _isOver; }
public String getURL() { return _url; }
}
In LotusScript add global LS2J variables:
(Options)
Uselsx "*javacon"
Use "MyJavaLibrary"
(Declarations)
Dim jSession As JavaSession
Dim jClass As JavaClass
Dim jObject As JavaObject
Sub Initialize
Set jSession = New JavaSession()
Set jClass = jSession.GetClass("MyClass")
Set jObject = jClass.CreateObject
End Sub
To start Java object use (in LotusScript of Button):
Call jObject.Start() 'Call with parameters that you want to use in Java
To check state and show progress use (in LotusScript of Timer):
If jObject.getIsOver() Then
s$ = jObject.getURL()
'Show results
Else
i% = jObject.getCounter()
'Show progress
End If
Related
The Info.plist of our Java-based application contains following entries:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
<plist version="0.9">
<dict>
...
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>myApp handler</string>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
</dict>
</array>
...
</dict>
</plist>
It should handle an URL like myapp://foobar/bazz. Opening the application works fine, but how the application should obtain the clicked URL?
For Objective C the answer can be found here: When an OS X app is launched by a registered URL scheme, how do you access the full URL?
The solution for Java is to rewrite the ObjC code into plain C, then translate that into Java, with the help of a set of classes under org.eclipse.swt.internal.cocoa.*.
As a reference for the ObjC-to-C translation, we need Apple's Objective-C Runtime Reference.
Plain C version
First, let's translate
[[NSAppleEventManager sharedAppleEventManager]
setEventHandler:targetObject
andSelector:#selector(handleAppleEvent:withReplyEvent:)
forEventClass:kInternetEventClass
andEventID:kAEGetURL];
into plain C. To invoke a ObjC function in plain C, we use objc_msgSend(). Furthermore, #selector(method_footprint) is substituted by sel_registerName("method_footprint"), and classes are looked up with objc_getClass(). The types id and SEL are equivalent to a pointer (such as void*) or a full-size int (i.e. same size as a void*).
The result:
// id mgr = [NSAppleEventManager sharedAppleEventManager]
SEL sel_ sharedAppleEventManager = sel_registerName("sharedAppleEventManager");
id mgr = objc_msgSend (objc_getClass("NSAppleEventManager"), sharedAppleEventManager);
// and the rest
SEL sel_setEventHandler = sel_registerName("setEventHandler:andSelector:forEventClass:andEventID:");
SEL sel_handleAppleEvent = sel_registerName("handleAppleEvent:withReplyEvent:");
objc_msgSend (mgr, sel_setEventHandler, targetObject, sel_handleAppleEvent, kInternetEventClass, kAEGetURL);
As you can see, we have two subroutine invocations here: The first calls the sharedAppleEventManager message of the NSAppleEventManager class, retrieving a singleton object from that class. The second call is sending the setEventHandler... message to that object, passing 4 arguments (target object, target message, and two event specifiers).
The callback function's declaration, originally:
- (void)handleAppleEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
looks like this in plain C:
void handleAppleEvent (id self, SEL selector, NSAppleEventDescriptor* event, NSAppleEventDescriptor* replyEvent)
This means that when the function gets called, it gets sent not only its object reference (id) but also its method footprint (selector).
The callback code looks like this in ObjC to get to the URL:
NSString url = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
And in plain C:
id desc_id = objc_msgSend (event_id, sel_registerName("paramDescriptorForKeyword:"), '----');
id url_id = objc_msgSend (desc_id, desc_id, sel_registerName("stringValue"));
One part is still missing:
targetObject needs to be initialized before invoking the code above, and a method matching the handleAppleEvent:withReplyEvent: footprint needs to be created in that target object, and then linked to our plain C event handler (handleAppleEvent()).
This means that we have to create an Objective C class, add a method to it, and then create an object instance of it:
// create an NSObject subclass for our target object
char objcClassName[] = "ObjCAppleEventHandler";
id objcClass = objc_allocateClassPair (objc_getClass("NSObject"), objcClassName);
// add the callback method to the class
SEL sel_handleAppleEvent = sel_registerName("handleAppleEvent:withReplyEvent:");
class_addMethod (objcClass, sel_handleAppleEvent, handleAppleEvent, "i#:##");
// register the class
objc_registerClassPair (objcClass)
// create an object instance
id targetObject = class_createInstance (objcClass, 0);
// ... here follows the above code with the setEventHandler invocation
// (note: `SEL sel_handleAppleEvent` appears twice - the 2nd one can be removed)
This concludes the plain C version.
(Note: The above code was written without testing it, so it may contain errors. The Java code below, however, has been tested.)
Java version
Translation from Plain C to Java is now fairly straight-forward.
The aforementioned ObjC Runtime functions are all available from org.eclipse.swt.internal.cocoa.OS.
First, some presets:
static final long class_NSAppleEventManager = OS.objc_getClass("NSAppleEventManager");
static final long sel_sharedAppleEventManager = OS.sel_registerName("sharedAppleEventManager");
static final long sel_setEventHandler = OS.sel_registerName("setEventHandler:andSelector:forEventClass:andEventID:");
static final long sel_handleAppleEvent = OS.sel_registerName("handleAppleEvent:withReplyEvent:");
static final long sel_paramDescriptorForKeyword = OS.sel_registerName("paramDescriptorForKeyword:");
static final long sel_stringValue = OS.sel_registerName("stringValue");
static final long kInternetEventClass = 0x4755524C; // 'GURL'
static final long kAEGetURL = 0x4755524C; // 'GURL'
static final long kCoreEventClass = 0x61657674; // 'aevt'
static final long kAEOpenApplication = 0x6F617070; // 'oapp'
static final long kAEReopenApplication = 0x72617070; // 'rapp'
static final long keyDirectObject = 0x2d2d2d2d; // '----'
The callback function:
static long handleAppleEvent (long id, long sel, long event_id, long reply_id) {
// This is a handler for AppleEvents that are registered with [NSAppleEventManager setEventHandler:...]
// It matches this selector (footprint):
// - (void)handleAppleEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)reply
// Invoke [[event paramDescriptorForKeyword:keyDirectObject] stringValue] to get the direct object containing the URL
long direct_desc_id = OS.objc_msgSend (event_id, sel_paramDescriptorForKeyword, keyDirectObject);
long direct_str_id = OS.objc_msgSend (direct_desc_id, sel_stringValue);
NSString nsStr = new NSString (direct_str_id);
String str = nsStr.getString();
// now 'str' contains the URL
System.out.println ("handleAppleEvent invoked -- argument: "+url);
return 0;
}
And the code to register the callback function:
// Get access to a callback function for receiving the sel_handleAppleEvent message
aeCallback = new Callback(Main.class, "handleAppleEvent", 4);
long aeProc = aeCallback.getAddress();
// Create a ObjC class that provides a method with the sel_handleAppleEvent footprint
String objcClassName = "ObjCAppleEventHandler";
long objcClass = OS.objc_allocateClassPair(OS.class_NSObject, objcClassName, 0);
OS.class_addMethod(objcClass, sel_handleAppleEvent, aeProc, "i#:##");
OS.objc_registerClassPair(objcClass);
long objcHandlerInstance = OS.class_createInstance (objcClass, 0);
// Invoke [[NSAppleEventManager sharedAppleEventManager] setEventHandler:objcHandlerInstance andSelector:sel_handleAppleEvent forEventClass:kInternetEventClass andEventID:kAEGetURL]
long sharedAppleEventMgr = OS.objc_msgSend (class_NSAppleEventManager, sel_sharedAppleEventManager);
OS.objc_msgSend (sharedAppleEventMgr, sel_setEventHandler, objcHandlerInstance, sel_handleAppleEvent, kInternetEventClass, kAEGetURL);
What's left to do is to build an app bundle from this code and then add the CFBundleURLTypes entries to its Info.plist.
A complete sample source file can be downloaded here: http://files.tempel.org/Various/ObjectiveC-bridging.java.zip
With Java 9, this is easy quite easy, and no longer requires Apple's EAWT classes or any ObjC hackery.
Desktop.getDesktop().setOpenURIHandler((event) -> {
System.out.println("Open URI: " + event.getURI());
// do something with the URI
});
The application needs to be bundled, and the CFBundleURLTypes key must be set.
<!-- Open URIs with scheme example:// -->
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>example</string>
</array>
<key>CFBundleURLName</key>
<string></string>
</dict>
</array>
Unfortunately this only captures the URI if the application is already running. If the application was launched by opening a URI, the event is not delivered (see comments on ed22's answer).
In case anyone wanted a version using com.apple.eawt.*
This also uses reflection, so it will compile on any platform (Windows etc.). Make sure not to call the method registering the event handler on any non-Apple system ;)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URI;
import java.util.logging.Level;
import java.util.logging.Logger;
interface OpenUriAppleEventHandler {
public void handleURI(URI uri);
}
class OpenURIEventInvocationHandler implements InvocationHandler {
private OpenUriAppleEventHandler urlHandler;
public OpenURIEventInvocationHandler(OpenUriAppleEventHandler urlHandler) {
this.urlHandler = urlHandler;
}
#SuppressWarnings({ "rawtypes", "unchecked"})
public Object invoke(Object proxy, Method method, Object[] args) {
if (method.getName().equals("openURI")) {
try {
Class openURIEventClass = Class.forName("com.apple.eawt.AppEvent$OpenURIEvent");
Method getURLMethod = openURIEventClass.getMethod("getURI");
//arg[0] should be an instance of OpenURIEvent
URI uri = (URI)getURLMethod.invoke(args[0]);
urlHandler.handleURI(uri);
} catch (Exception ex) {
ex.printStackTrace();
}
}
return null;
}
}
public class OSXAppleEventHelper {
/**
* Call only on OS X
*/
#SuppressWarnings({ "unchecked", "rawtypes" })
public static void setOpenURIAppleEventHandler(OpenUriAppleEventHandler urlHandler) {
try {
Class applicationClass = Class.forName("com.apple.eawt.Application");
Method getApplicationMethod = applicationClass.getDeclaredMethod("getApplication", (Class[])null);
Object application = getApplicationMethod.invoke(null, (Object[])null);
Class openURIHandlerClass = Class.forName("com.apple.eawt.OpenURIHandler", false, applicationClass.getClassLoader());
Method setOpenURIHandlerMethod = applicationClass.getMethod("setOpenURIHandler", openURIHandlerClass);
OpenURIEventInvocationHandler handler = new OpenURIEventInvocationHandler(urlHandler);
Object openURIEvent = Proxy.newProxyInstance(openURIHandlerClass.getClassLoader(), new Class[] { openURIHandlerClass }, handler);
setOpenURIHandlerMethod.invoke(application, openURIEvent);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Use it like this:
//if(isOSX){
OSXAppleEventHelper.setOpenURIAppleEventHandler(new OpenUriAppleEventHandler() {
#Override
public void handleURI(URI url) {
/* do something with the url */
}
});
Request:
This is a very common problem faced by Java devs in my locale. I am really stuck for many days on this. Searched and tried a lot, read the docs. read ALL the stackoverflow questions related to JavaExe. Please only reply if you have done similar thing before and have a comprehensive answer. I would be really grateful to the community!
Senario:
I am using JavaExe to run an application as system service in desktop interactive capability.
To be exact I have an application that captures screenshots of desktops. I want it to run (as admin) on any user login so no one can stop it.
I have a myapp.jar, settings.txt and a lib dir.
I have searched alot and found JavaExe works (by watching its examples)
If anyone has a better way. Please state so.
Problem:
According to my research,
you must create a .properties file that has named like the .exe, and write "RunType = 1" in this file.
you define a static method in your main class : serviceInit()
Do I need to place any class or reference/import? How?
Edit:
My code below works as stand alone .jar and in javaExe.exe too.
It now does makes a system service and runs by as SYSTEM user. but It is NOT interactive to desktop. i.e its not showing any GUI.
package temp;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
public class Temp {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
serviceInit();
}
public static boolean serviceInit(){
new Thread(){
public void run(){
Integer i = 0;
while(i < 999999999){
JOptionPane.showMessageDialog(null,i);
i++;
}
}
}.start();
return true;
}
}
And I dont think that bundling the .jar, lib directory and settings.txt into one .exe is possible?
you should have in your case :
public class MyApp_ServiceManagement
{
static boolean isMsgToDisplay = false;
/////////////////////////////
public static boolean serviceInit()
{
(new Thread()
{
public void run()
{
for(int i=0;i < 6;i++)
{
try { sleep(5*1000); }
catch(Exception ex) {}
isMsgToDisplay = true;
}
}
}).start();
return true;
}
/// is Data ready to be send to the UI ?
public static boolean serviceIsDataForUI()
{
return isMsgToDisplay;
}
/// Data to be send to the UI
public static Serializable serviceDataForUI()
{
isMsgToDisplay = false;
return "hello, I am an interactive Service";
}
}
and for the UI part :
public class MyApp_TaskbarManagement
{
/// To show (or not) the icon in tray
public static boolean taskIsShow()
{
return false;
}
/// Receive the message from Service
public static void taskDataFromService(Serializable data)
{
JOptionPane.showMessageDialog(null, data);
}
/// descr of UI
public static String[] taskGetInfo()
{
return new String[]
{
"UI part of Service"
};
}
}
the main() method is never called in service mode (except one particular case), but if you want keep your main() method you must put a call to main() in serviceInit().
Put serviceInit() in your main class or in another class named XXX_ServiceManagement where XXX is the name of your main class.
Then, serviceInit() must return before a 30 seconds delay.
Don't put a infinite loop, ... in it.
Put your code in a thread, and start it from serviceInit() (or main)
That answer to your problem?
I'm new to blackberry development. There is this question I've come across several times, which is "How to get the Selected item as string". The answers that were given did not fully answer the question:
AutoCompleteField autoCompleteField = new AutoCompleteField(filterList)
{
public void onSelect(Object selection, int SELECT_TRACKWHEEL_CLICK) {
ListField _list = getListField();
if (_list.getSelectedIndex() > -1) {
Dialog.alert("You selected: "+_list.getSelectedIndex());
// get text selected by user and do something...
}
}
The point is how can I get the selected text and "do something". Imagine I want to send the items as a string to a server via post. How would I do that in code?
Thank you! Michael.
These are really (at least) two different things.
To get the selected text, see this answer
To send a HTTP POST request, see this other answer
Normally, it's also bad to make network requests on the UI thread (which is what will callback your onSelect() method). So, it would be better to take the HTTP POST code from the second answer, and put it inside the run() method of a Runnable object you could run on a background thread. Something like this:
private class PostRequest implements Runnable {
private String _postParam;
public PostRequest(String param) {
_postParam = param;
}
public void run() {
// make POST request here, using _postParam
}
}
And use it like this:
AutoCompleteField acf = new AutoCompleteField(list) {
protected void onSelect(Object selection, int type) {
super.onSelect(selection, type);
if (selection != null) {
String selectionAsString = getEditField().getText();
Thread worker = new Thread(new PostRequest(selectionAsString));
worker.start();
}
}
};
I want to know that is it possible to send major Swing classes event/actionlisteners, Events, components via RMI.
Possible scenario: If one client press the button or move the slider every client's button or slider move etc same for other swing elements.
I am expecting the answer in the context of RMI and swing MVC architecture, i want to call the swing component's models e.g ButtonModel and want to send swing ActionEvent on wire and register PropertyChangeListener/PropertyChangeSupport as remote objects for getting updates at client site.
typical examples :
the server should call this method for each client, when ever some change occur in model
public void propertyChange(PropertyChangeEvent evt) {
for (AbstractViewPanel view: registeredViews) {
view.modelPropertyChange(evt);
}
}
in case of an event on one client, each client actionPerformed should be called from server:
#Override
public void actionPerformed(ActionEvent e) {
}
is it feasible? if not then why? where i could face the problems, i mean which classes are transferable (serialized) and which are not...
EDIT: here you see i m invoking Java Swing defaultbuttonmodel remotely, the only thing left when some of it's property or method change the other client's get updates, best would be following swing propertychangelistener if someone can just help me, realizing this, it would be great:
public class RemoteButtonModel extends UnicastRemoteObject implements Model {
private ButtonModel model = new DefaultButtonModel() ;
protected myModel() throws RemoteException {
super();
}
#Override
public void setEnabled(boolean b) throws RemoteException {
if (isEnabled())
model.setEnabled(false);
else{
model.setEnabled(true);
}
}
#Override
public boolean isEnabled() throws RemoteException {
return model.isEnabled();
}
}
I think it would be more efficient to send across something like a "scroll message" or "button pressed" message, utilizing the command pattern. This would allow different clients to act correctly with different implementations.
Edits:
the way I do it in my client/server applications (which is easily adapted to this peer-to-peer architecture you're doing) is with something like this (copy and pasted from my production code, mind you.)
abstract public class UserRequest implements Serializable {
public final String username;
private transient ServersideThread thread;
protected UserRequest(String username) {
this.username = username;
this.thread = null;
}
abstract public EngineMessage engineCallback(GenericEngine engine);
public void setThread(ServersideThread thread) {
if(this.thread == null) {
this.thread = thread;
return;
}
throw new IllegalStateException("Cannot set thread when already set:" + thread.getName());
}
public ServersideThread getThread() {
return this.thread;
}
}
So, with this approach, I would do something like...
public class SliderMoveNotification extends UserRequest {
// need some way to say what slider moved
public final int sliderId;
public final int slideDistance;
public SliderMoveNotification(String username) {
super(username);
sliderId = 0;
sliderDistance = 0;
throw new UnsupportedOperationException("Must supply a slider and a distance");
}
public SliderMoveNotification(String username, int sliderID, int slideDistance) {
super(username);
this.sliderId = sliderId;
this.slideDistance = slideDistance;
}
public EngineMessage engineCallback(GenericEngine engine) {
if(engine instanceof WindowEngine) {
WindowEngine manager = (WindowEngine)engine;
manager.slideWindow(sliderId,slideDistance);
// you wouldn't need engine messages like I do in my client/server
// relationship, but the idea stands.
}
}
}
The Javadoc for every Swing class says that it should not be serialized.
More probably you should be transmitting the associated Model classes.
And event listening via RMI is an anti-pattern. Too much traffic, too many points of failure.
So i'm trying to get my Apache xmlrpc client/server implementation to play ball. Everything works fine except for one crucial issue:
my handler class (mapped through the properties file org.apache.xmlrpc.webserver.XmlRpcServlet.properties) reacts as it should but it's constructor is called at every method invocation. It would seem that the handler class is instantiated at each call which is bad because I have data stored in instance variables that I need to save between calls.
How do I save a reference to the instantiated handler so that I can access it's instance variables?
So, for anyone else who still wants to use XMLRPC here's how I fixed this issue:
http://xmlrpc.sourceforge.net/
far superior to apache xmlrpc, in my opinion.
This is standard behaviour of Apache XMLRPC 3.x. http://ws.apache.org/xmlrpc/handlerCreation.html:
By default, Apache XML-RPC creates a new object for processing each
request received at the server side.
However, you can emulate the behaviour of XMLRPC 2.x, where you registered handler objects instead of handler classes, using a RequestProcessorFactoryFactory. I have written a custom RequestProcessorFactoryFactory that you can use:
public class CustomHandler implements RequestProcessorFactoryFactory {
Map<Class<?>, RequestProcessorFactory> handlers =
Collections.synchronizedMap(
new HashMap<Class<?>, RequestProcessorFactory>());
#Override
public RequestProcessorFactory getRequestProcessorFactory(Class pClass)
throws XmlRpcException {
return handlers.get(pClass);
}
public void addHandler(final Object handler) {
handlers.put(handler.getClass(), new RequestProcessorFactory() {
#Override
public Object getRequestProcessor(XmlRpcRequest pRequest)
throws XmlRpcException {
return handler;
}
});
}
}
This can then be used with e.g. a XMLRPC WebServer like this
WebServer server = ...
PropertyHandlerMapping phm = new PropertyHandlerMapping();
server.getXmlRpcServer().setHandlerMapping(phm);
Custom sh = new CustomHandler();
phm.setRequestProcessorFactoryFactory(sh);
Object handler = ... /** The object you want to expose via XMLRPC */
sh.addHandler(handler);
phm.addHandler(serverName, handler.getClass());
Maybe something to do with javax.xml.rpc.session.maintain set to true?
I know this is a really old post but I managed to solve the problem with Apache's Java XML-RPC.
First, I thought this could be solved with singleton class in Java but it doesn't work and throws "illegal access exception".
These are what I have done:
public class XmlRpcServer {
private static JFrame frame = new JFrame();
private static JPanel pane = new JPanel();
public static XmlRpcServer singleton_inst = new XmlRpcServer();
public XmlRpcServer() {
// I kept the constructor empty.
}
public static void main(String[] args) throws XmlRpcException, IOException {
// In my case, I put the constructor code here.
// Then stuff for XML-RPC server
// Server Part
WebServer ws = new WebServer(8741);
PropertyHandlerMapping mapping = new PropertyHandlerMapping();
mapping.addHandler("SERVER", singleton_inst.getClass());
ws.getXmlRpcServer().setHandlerMapping(mapping);
ws.start();
////
}
// I called doTheJob() from python via XML-RPC
public String doTheJob(String s) throws XmlRpcException {
loop();
return s;
}
// It executed loop() forever
private static void loop() throws XmlRpcException {
// Actual work is here
}
But metaspace increases gradually:
I worked too much on this metaspace issue when looping forever in Java but I couldn't figure out a solution.