alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (awt.m)

This example Java source code file (awt.m) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

awt_assert_appkit_thread, awt_debug_log, awtstarter, bool, cfrunloopactivity, jni_version_1_4, jnienv, nsnumber, nsstring, null, product_build, shouldprintverbosedebugging, yes

The awt.m Java example source code

/*
 * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

#import <pthread.h>
#import <objc/runtime.h>
#import <Cocoa/Cocoa.h>
#import <Security/AuthSession.h>
#import <JavaNativeFoundation/JavaNativeFoundation.h>
#import <JavaRuntimeSupport/JavaRuntimeSupport.h>

#import "NSApplicationAWT.h"
#import "PropertiesUtilities.h"
#import "ThreadUtilities.h"
#import "AWT_debug.h"
#import "ApplicationDelegate.h"

#define DEBUG 0


// The symbol is defined in libosxapp.dylib (ThreadUtilities.m)
extern JavaVM *jvm;

// Indicates if AWT is running embedded (in SWT, FX, elsewhere)
static BOOL isEmbedded = NO;

// Indicates that the app has been started with -XstartOnFirstThread
// (directly or via WebStart settings), and AWT should not run its
// own event loop in this mode. Even if a loop isn't running yet,
// we expect an embedder (e.g. SWT) to start it some time later.
static BOOL forceEmbeddedMode = NO;

static bool ShouldPrintVerboseDebugging() {
    static int debug = -1;
    if (debug == -1) {
        debug = (int)(getenv("JAVA_AWT_VERBOSE") != NULL) || (DEBUG != 0);
    }
    return (bool)debug;
}

// This is the data necessary to have JNI_OnLoad wait for AppKit to start.
static BOOL sAppKitStarted = NO;
static pthread_mutex_t sAppKitStarted_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t sAppKitStarted_cv = PTHREAD_COND_INITIALIZER;

void setBusy(BOOL isBusy);
static void BusyObserver(CFRunLoopObserverRef ref, CFRunLoopActivity what, void* arg);
static void NotBusyObserver(CFRunLoopObserverRef ref, CFRunLoopActivity what, void* arg);
static void AWT_NSUncaughtExceptionHandler(NSException *exception);

static CFRunLoopObserverRef busyObserver = NULL;
static CFRunLoopObserverRef notBusyObserver = NULL;

static void setUpAWTAppKit()
{
    BOOL verbose = ShouldPrintVerboseDebugging();
    if (verbose) AWT_DEBUG_LOG(@"setting up busy observers");

    // Add CFRunLoopObservers to call into AWT so that AWT knows that the
    //  AWT thread (which is the AppKit main thread) is alive. This way AWT
    //  will not automatically shutdown.
    busyObserver = CFRunLoopObserverCreate(
            NULL,                        // CFAllocator
            kCFRunLoopAfterWaiting,      // CFOptionFlags
            true,                        // repeats
            NSIntegerMax,                // order
            &BusyObserver,               // CFRunLoopObserverCallBack
            NULL);                       // CFRunLoopObserverContext

    notBusyObserver = CFRunLoopObserverCreate(
            NULL,                        // CFAllocator
            kCFRunLoopBeforeWaiting,     // CFOptionFlags
            true,                        // repeats
            NSIntegerMin,                // order
            &NotBusyObserver,            // CFRunLoopObserverCallBack
            NULL);                       // CFRunLoopObserverContext

    CFRunLoopRef runLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
    CFRunLoopAddObserver(runLoop, busyObserver, kCFRunLoopDefaultMode);
    CFRunLoopAddObserver(runLoop, notBusyObserver, kCFRunLoopDefaultMode);

    CFRelease(busyObserver);
    CFRelease(notBusyObserver);

    setBusy(YES);
}

static void setUpAppKitThreadName()
{
    BOOL verbose = ShouldPrintVerboseDebugging();
    JNIEnv *env = [ThreadUtilities getJNIEnv];

    // Set the java name of the AppKit main thread appropriately.
    jclass threadClass = NULL;
    jstring name = NULL;
    jobject curThread = NULL;

    threadClass = (*env)->FindClass(env, "java/lang/Thread");
    if (threadClass == NULL || (*env)->ExceptionCheck(env)) goto cleanup;
    jmethodID currentThreadID = (*env)->GetStaticMethodID(env, threadClass, "currentThread", "()Ljava/lang/Thread;");
    if (currentThreadID == NULL || (*env)->ExceptionCheck(env)) goto cleanup;
    jmethodID setName = (*env)->GetMethodID(env, threadClass, "setName", "(Ljava/lang/String;)V");
    if (setName == NULL || (*env)->ExceptionCheck(env)) goto cleanup;

    curThread = (*env)->CallStaticObjectMethod(env, threadClass, currentThreadID); // AWT_THREADING Safe (known object)
    if (curThread == NULL || (*env)->ExceptionCheck(env)) goto cleanup;
    name = (*env)->NewStringUTF(env, "AWT-AppKit");
    if (name == NULL || (*env)->ExceptionCheck(env)) goto cleanup;
    (*env)->CallVoidMethod(env, curThread, setName, name); // AWT_THREADING Safe (known object)
    if ((*env)->ExceptionCheck(env)) goto cleanup;

cleanup:
    if (threadClass != NULL) {
        (*env)->DeleteLocalRef(env, threadClass);
    }
    if (name != NULL) {
        (*env)->DeleteLocalRef(env, name);
    }
    if (curThread != NULL) {
        (*env)->DeleteLocalRef(env, curThread);
    }
    if ((*env)->ExceptionCheck(env)) {
        (*env)->ExceptionDescribe(env);
        (*env)->ExceptionClear(env);
    }

    if (verbose) AWT_DEBUG_LOG(@"finished setting thread name");
}


// Returns true if java believes it is running headless
BOOL isHeadless(JNIEnv *env) {
    // Just access the property directly, instead of using GraphicsEnvironment.isHeadless.
    //  This is because this may be called while AWT is being loaded, and calling AWT
    //  while it is being loaded will deadlock.
    static JNF_CLASS_CACHE(jc_Toolkit, "java/awt/GraphicsEnvironment");
    static JNF_STATIC_MEMBER_CACHE(jm_isHeadless, jc_Toolkit, "isHeadless", "()Z");
    return JNFCallStaticBooleanMethod(env, jm_isHeadless);
}

BOOL isSWTInWebStart(JNIEnv* env) {
    NSString *swtWebStart = [PropertiesUtilities javaSystemPropertyForKey:@"com.apple.javaws.usingSWT" withEnv:env];
    return [@"true" isCaseInsensitiveLike:swtWebStart];
}

void setBusy(BOOL busy) {
AWT_ASSERT_APPKIT_THREAD;

    JNIEnv *env = [ThreadUtilities getJNIEnv];
    static JNF_CLASS_CACHE(jc_AWTAutoShutdown, "sun/awt/AWTAutoShutdown");

    if (busy) {
        static JNF_STATIC_MEMBER_CACHE(jm_notifyBusyMethod, jc_AWTAutoShutdown, "notifyToolkitThreadBusy", "()V");
        JNFCallStaticVoidMethod(env, jm_notifyBusyMethod);
    } else {
        static JNF_STATIC_MEMBER_CACHE(jm_notifyFreeMethod, jc_AWTAutoShutdown, "notifyToolkitThreadFree", "()V");
        JNFCallStaticVoidMethod(env, jm_notifyFreeMethod);
    }
}

static void BusyObserver(CFRunLoopObserverRef ref, CFRunLoopActivity what, void* arg) {
AWT_ASSERT_APPKIT_THREAD;

    // This is only called with the selector kCFRunLoopAfterWaiting.
#ifndef PRODUCT_BUILD
    assert(what == kCFRunLoopAfterWaiting);
#endif /* PRODUCT_BUILD */

    setBusy(YES);
}

static void NotBusyObserver(CFRunLoopObserverRef ref, CFRunLoopActivity what, void* arg) {
AWT_ASSERT_APPKIT_THREAD;

    // This is only called with the selector kCFRunLoopBeforeWaiting.
#ifndef PRODUCT_BUILD
    assert(what == kCFRunLoopBeforeWaiting);
#endif /* PRODUCT_BUILD */

    setBusy(NO);
}

static void AWT_NSUncaughtExceptionHandler(NSException *exception) {
    NSLog(@"Apple AWT Internal Exception: %@", [exception description]);
}

// This is an empty Obj-C object just so that -peformSelectorOnMainThread can be used.
@interface AWTStarter : NSObject { }
+ (void)start:(BOOL)headless;
- (void)starter:(NSArray*)args;
+ (void)appKitIsRunning:(id)arg;
@end

@implementation AWTStarter

+ (BOOL) isConnectedToWindowServer {
    SecuritySessionId session_id;
    SessionAttributeBits session_info;
    OSStatus status = SessionGetInfo(callerSecuritySession, &session_id, &session_info);
    if (status != noErr) return NO;
    if (!(session_info & sessionHasGraphicAccess)) return NO;
    return YES;
}

+ (BOOL) markAppAsDaemon {
    id jrsAppKitAWTClass = objc_getClass("JRSAppKitAWT");
    SEL markAppSel = @selector(markAppIsDaemon);
    if (![jrsAppKitAWTClass respondsToSelector:markAppSel]) return NO;
    return [jrsAppKitAWTClass performSelector:markAppSel] ? YES : NO;
}

+ (void)appKitIsRunning:(id)arg {
    // Headless: NO
    // Embedded: BOTH
    // Multiple Calls: NO
    //  Callers: AppKit's NSApplicationDidFinishLaunchingNotification or +[AWTStarter startAWT:]
AWT_ASSERT_APPKIT_THREAD;

    BOOL verbose = ShouldPrintVerboseDebugging();
    if (verbose) AWT_DEBUG_LOG(@"about to message AppKit started");

    // Signal that AppKit has started (or is already running).
    pthread_mutex_lock(&sAppKitStarted_mutex);
    sAppKitStarted = YES;
    pthread_cond_signal(&sAppKitStarted_cv);
    pthread_mutex_unlock(&sAppKitStarted_mutex);

    if (verbose) AWT_DEBUG_LOG(@"finished messaging AppKit started");
}

+ (void)start:(BOOL)headless
{
    BOOL verbose = ShouldPrintVerboseDebugging();

    // Headless: BOTH
    // Embedded: BOTH
    // Multiple Calls: NO
    //  Caller: JNI_OnLoad

    // onMainThread is NOT the same at SWT mode!
    // If the JVM was started on the first thread for SWT, but the SWT loads the AWT on a secondary thread,
    // onMainThread here will be false but SWT mode will be true.  If we are currently on the main thread, we don't
    // need to throw AWT startup over to another thread.
    BOOL onMainThread = (pthread_main_np() != 0);

    if (verbose) {
        NSString *msg = [NSString stringWithFormat:@"+[AWTStarter start headless:%d] { onMainThread:%d }", headless, onMainThread];
        AWT_DEBUG_LOG(msg);
    }

    if (!headless)
    {
        // Listen for the NSApp to start. This indicates that JNI_OnLoad can proceed.
        //  It must wait because there is a chance that another java thread will grab
        //  the AppKit lock before the +[NSApplication sharedApplication] returns.
        //  See <rdar://problem/3492666> for an example.
        [[NSNotificationCenter defaultCenter] addObserver:[AWTStarter class]
                                                 selector:@selector(appKitIsRunning:)
                                                     name:NSApplicationDidFinishLaunchingNotification
                                                   object:nil];

        if (verbose) NSLog(@"+[AWTStarter start:::]: registered NSApplicationDidFinishLaunchingNotification");
    }

    id st = [[AWTStarter alloc] init];

    NSArray * args = [NSArray arrayWithObjects:
                      [NSNumber numberWithBool: onMainThread],
                      [NSNumber numberWithBool: headless],
                      [NSNumber numberWithBool: verbose],
                      nil];

    if (onMainThread) {
        [st starter:args];
    } else {
        [st performSelectorOnMainThread: @selector(starter:) withObject:args waitUntilDone:NO];
    }

    if (!headless && !onMainThread) {
        if (verbose) AWT_DEBUG_LOG(@"about to wait on AppKit startup mutex");

        // Wait here for AppKit to have started (or for AWT to have been loaded into
        //  an already running NSApplication).
        pthread_mutex_lock(&sAppKitStarted_mutex);
        while (sAppKitStarted == NO) {
            pthread_cond_wait(&sAppKitStarted_cv, &sAppKitStarted_mutex);
        }
        pthread_mutex_unlock(&sAppKitStarted_mutex);

        // AWT gets here AFTER +[AWTStarter appKitIsRunning:] is called.
        if (verbose) AWT_DEBUG_LOG(@"got out of the AppKit startup mutex");
    }

    if (!headless) {
        // Don't set the delegate until the NSApplication has been created and
        // its finishLaunching has initialized it.
        //  ApplicationDelegate is the support code for com.apple.eawt.
        [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
            id<NSApplicationDelegate> delegate = [ApplicationDelegate sharedDelegate];
            if (delegate != nil) {
                OSXAPP_SetApplicationDelegate(delegate);
            }        
        }];
    }
}

- (void)starter:(NSArray*)args {
    NSAutoreleasePool *pool = [NSAutoreleasePool new];

    BOOL onMainThread = [[args objectAtIndex:0] boolValue];
    BOOL headless = [[args objectAtIndex:1] boolValue];
    BOOL verbose = [[args objectAtIndex:2] boolValue];

    BOOL wasOnMainThread = onMainThread;

    // Add the exception handler of last resort
    NSSetUncaughtExceptionHandler(AWT_NSUncaughtExceptionHandler);

    // Headless mode trumps either ordinary AWT or SWT-in-AWT mode.  Declare us a daemon and return.
    if (headless) {
        // Note that we don't install run loop observers in headless mode
        // because we don't need them (see 7174704)
        if (!forceEmbeddedMode) {
            setUpAppKitThreadName();
        }
        [AWTStarter markAppAsDaemon];
        return;
    }

    if (forceEmbeddedMode) {
        if (verbose) NSLog(@"in SWT or SWT/WebStart mode");

        // Init a default NSApplication instance instead of the NSApplicationAWT.
        // Note that [NSApp isRunning] will return YES after that, though
        // this behavior isn't specified anywhere. We rely on that.
        NSApplicationLoad();
    }

    // This will create a NSApplicationAWT for standalone AWT programs, unless there is
    //  already a NSApplication instance. If there is already a NSApplication instance,
    //  and -[NSApplication isRunning] returns YES, AWT is embedded inside another
    //  AppKit Application.
    NSApplication *app = [NSApplicationAWT sharedApplication];
    isEmbedded = ![NSApp isKindOfClass:[NSApplicationAWT class]];

    if (!isEmbedded) {
        // Install run loop observers and set the AppKit Java thread name
        setUpAWTAppKit();
        setUpAppKitThreadName();
    }

    // AWT gets to this point BEFORE NSApplicationDidFinishLaunchingNotification is sent.
    if (![app isRunning]) {
        if (verbose) AWT_DEBUG_LOG(@"+[AWTStarter startAWT]: ![app isRunning]");

        // This is where the AWT AppKit thread parks itself to process events.
        [NSApplicationAWT runAWTLoopWithApp: app];
    } else {
        // We're either embedded, or showing a splash screen
        if (isEmbedded) {
            if (verbose) AWT_DEBUG_LOG(@"running embedded");

            // We don't track if the runloop is busy, so set it free to let AWT finish when it needs
            setBusy(NO);
        } else {
            if (verbose) AWT_DEBUG_LOG(@"running after showing a splash screen");
        }

        // Signal so that JNI_OnLoad can proceed.
        if (!wasOnMainThread) [AWTStarter appKitIsRunning:nil];

        // Proceed to exit this call as there is no reason to run the NSApplication event loop.
    }

    [pool drain];
}

@end


JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
    BOOL verbose = ShouldPrintVerboseDebugging();
    if (verbose) AWT_DEBUG_LOG(@"entered JNI_OnLoad");

    // Headless: BOTH
    // Embedded: BOTH
    // Multiple Calls: NO
    //  Caller: JavaVM classloader

    // Keep a static reference for other archives.
    OSXAPP_SetJavaVM(vm);

    JNIEnv *env = NULL;

    // Need JNIEnv for JNF_COCOA_ENTER(env); macro below
    jint status = (*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4);
    if (status != JNI_OK || env == NULL) {
        AWT_DEBUG_LOG(@"Can't get JNIEnv");
        return JNI_VERSION_1_4;
    }

JNF_COCOA_ENTER(env);

    // Launcher sets this env variable if -XstartOnFirstThread is specified
    char envVar[80];
    snprintf(envVar, sizeof(envVar), "JAVA_STARTED_ON_FIRST_THREAD_%d", getpid());
    if (getenv(envVar) != NULL) {
        forceEmbeddedMode = YES;
        unsetenv(envVar);
    }

    if (isSWTInWebStart(env)) {
        forceEmbeddedMode = YES;
    }

    BOOL headless = isHeadless(env);

    // We need to let Foundation know that this is a multithreaded application, if it isn't already.
    if (![NSThread isMultiThreaded]) {
        [NSThread detachNewThreadSelector:nil toTarget:nil withObject:nil];
    }

    [AWTStarter start:headless];

JNF_COCOA_EXIT(env);

    if (verbose) AWT_DEBUG_LOG(@"exiting JNI_OnLoad");

    return JNI_VERSION_1_4;
}

Other Java examples (source code examples)

Here is a short list of links related to this Java awt.m source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.