Example #1
0
    def test_interruptible(self):
        def wait_until_interrupted(cv):
            name = threading.currentThread().getName()
            with cv:
                while not JThread.currentThread().isInterrupted():
                    try:
                        cv.wait()
                    except InterruptedException as e:
                        break

        num_threads = 5
        unfair_condition = Condition()
        threads = [
            Thread(name="thread #%d" % i,
                   target=wait_until_interrupted,
                   args=(unfair_condition, )) for i in range(num_threads)
        ]

        for thread in threads:
            thread.start()
        time.sleep(0.1)

        for thread in threads:
            JThread.interrupt(thread)

        joined_threads = 0
        for thread in threads:
            thread.join(1.)  # timeout just in case so we don't stall regrtest
            joined_threads += 1
        self.assertEqual(joined_threads, num_threads)
Example #2
0
    def test_interruptible(self):

        def wait_until_interrupted(cv):
            name = threading.currentThread().getName()
            with cv:
                while not JThread.currentThread().isInterrupted():
                    try:
                        cv.wait()
                    except InterruptedException as e:
                        break

        num_threads = 5
        unfair_condition = Condition()
        threads = [
            Thread(
                name="thread #%d" % i,
                target=wait_until_interrupted,
                args=(unfair_condition,)) 
            for i in range(num_threads)]

        for thread in threads:
            thread.start()
        time.sleep(0.1)

        for thread in threads:
            JThread.interrupt(thread)

        joined_threads = 0
        for thread in threads:
            thread.join(1.) # timeout just in case so we don't stall regrtest
            joined_threads += 1
        self.assertEqual(joined_threads, num_threads)
class JavaIntegrationTestCase(unittest.TestCase):
    """Verifies that Thread.__tojava__ correctly gets the underlying Java thread"""

    def test_interruptible(self):

        def wait_until_interrupted(cv):
            name = threading.currentThread().getName()
            with cv:
                while not JThread.currentThread().isInterrupted():
                    try:
                        cv.wait()
                    except InterruptedException, e:
                        break

        num_threads = 5
        unfair_condition = Condition()
        threads = [
            Thread(
                name="thread #%d" % i,
                target=wait_until_interrupted,
                args=(unfair_condition,)) 
            for i in xrange(num_threads)]

        for thread in threads:
            thread.start()
        time.sleep(0.1)

        for thread in threads:
            JThread.interrupt(thread)

        joined_threads = 0
        for thread in threads:
            thread.join(1.) # timeout just in case so we don't stall regrtest
            joined_threads += 1
        self.assertEqual(joined_threads, num_threads)
Example #4
0
def be_unfair():
    unfair_condition = Condition()
    threads = [
        Thread(
            name="thread #%d" % i,
            target=wait_until_interrupted,
            args=(unfair_condition,)) 
        for i in xrange(5)]
    for thread in threads:
        thread.start()
    time.sleep(5)

    # threads should not be doing anything now, can verify by looking at some shared state

    # instead of notifying, we will interrupt the threads
    for thread in threads:
        JThread.interrupt(thread)
        # or you can use this equivalent op
        # thread.__tojava__(JThread).interrupt()
    for thread in threads:
        thread.join()
    def test_interruption(self):
        # based on this code http://www.jython.org/jythonbook/en/1.0/Concurrency.html#interruption,
        # which demonstrates __tojava__ works properly

        class ExtendedThread(threading.Thread, ExtendedInterface):
            def returnSomething(self):
                return "yo yo yo"

        def wait_until_interrupted(cv):
            with cv:
                while not Thread.currentThread().isInterrupted():
                    try:
                        # this condition variable is never notified, so will only
                        # succeed if interrupted
                        cv.wait()
                    except InterruptedException as e:
                        break

        unfair_condition = threading.Condition()
        threads = [
           ExtendedThread(
                name="thread #%d" % i,
                target=wait_until_interrupted,
                args=(unfair_condition,))
            for i in range(5)]

        for thread in threads:
            thread.start()
        for thread in threads:
            Thread.interrupt(thread)
        for thread in threads:
            thread.join(5)
        
        # this assertion only succeeds if threads terminated because
        # they were interrupted
        for thread in threads:
            self.assertFalse(thread.isAlive())
Example #6
0
class FDRIModule(DataSourceIngestModule):

    _logger = Logger.getLogger(FDRIModuleFactory.moduleName)

    def log(self, level, msg):
        self._logger.logp(level, self.__class__.__name__,
                          inspect.stack()[1][3], msg)

    def __init__(self, settings):
        self.context = None
        self.localSettings = settings
        self.extensions = []
        self.deleteAfter = False
        self.doRecognition = True
        self.userPaths = {
            "0": "", 
            "1": "", 
            "2": ""
        }

        # True to create the DFXML file
        self.createDFXML    = C_CREATE_DFXML

        # Time acumulators to determine the 
        # cumulative time needed to compute
        # MD5, SHA1 and SHA256
        self.needed_time_MD5    = 0.0
        self.needed_time_SHA1   = 0.0
        self.needed_time_SHA256 = 0.0

        #CONFIGURATION_PATH = Case.getCurrentCase().getModuleDirectory() + "\\FDRI.json"
        
        # Error list in acordance with .exe code
        # for unknow errors please run executable via command line
        self.errorList = {
            1: ' FDRI.exe Parameters error',
            2: ' Error loading parameter file ',
            3: ' Error parsing parameter file ',
            4: ' Error finding image directory ',
            5: ' Error initializing recognition network ',
            6: ' Error initializing shape predictor ',
            7: ' Error initializing detection network ',
            8: ' Didn\'t find any positive faces ',
            9: ' Didn\'t find any target faces ',
            10: ' CUDA out of memory ',
            11: ' Didn\'t find any usable CUDA devices '
        }

    # Where any setup and configuration is done
    # 'context' is an instance of org.sleuthkit.autopsy.ingest.IngestJobContext.
    # See: http://sleuthkit.org/autopsy/docs/api-docs/4.4/classorg_1_1sleuthkit_1_1autopsy_1_1ingest_1_1_ingest_job_context.html
    def startUp(self, context):
        # Supported file format from dlib
        acceptedFiles = ['.jpg', '.jpeg', '.png']
        i = 0
        for ext in acceptedFiles:
            if self.localSettings.getFlag(i):
                self.extensions.append(ext)
            i += 1

        if not self.extensions:
            raise IngestModuleException(
                "Need to select at least one type of file!")

        # self.generate_hash controls whether MD5,SHA1 and SHA256 hashes
        # are added to the DFXML generated file.
        if self.localSettings.getFlag(3):
            self.generate_hash = True
        else:
            self.generate_hash = False

        #
        # Checking for default detectors and auxiliary files
        #
        self.pathToExe = os.path.join(os.path.dirname(
            os.path.abspath(__file__)), "FDRI.exe")

        self.defaultPaths = {
            '0': os.path.join(os.path.dirname(os.path.abspath(__file__)),
                                            "mmod_human_face_detector.dat"),
            '1': os.path.join(os.path.dirname(os.path.abspath(__file__)), 
                                            "dlib_face_recognition_resnet_model_v1.dat"),
            '2': os.path.join(os.path.dirname(os.path.abspath(__file__)), 
                                            "shape_predictor_5_face_landmarks.dat")
        }

        save_file = False
        if os.path.exists(GLOBAL_CONFIGURATION_PATH):
            with open(GLOBAL_CONFIGURATION_PATH, "r") as out:
                content = json.load(out)
                save_file = content['save_files']
                self.userPaths = content['paths']

        for code in self.userPaths:
            if not self.userPaths[code]:
                    # user didn't set models path, we assume module location
                    self.userPaths[code] = self.defaultPaths[code]
        
        # Update global config file
        #self.log(Level.INFO, GLOBAL_CONFIGURATION_PATH)
        with open(GLOBAL_CONFIGURATION_PATH, "w") as out:
            json.dump({"save_files": save_file,
                       "paths": self.userPaths}, out)

            folder_positive_photos = self.localSettings.getPath("1")

            # No folder for positive photos was given: recognition OFF
            if len(folder_positive_photos) == 0:
                self.doRecognition = False
                Msg_S = "Face recognition OFF (no folder with positive photo(s) given)"
                self.log(Level.INFO,Msg_S)
            elif not os.path.exists(folder_positive_photos):
                # The folder with positive photo doesn' exist: recognition will
                # be OFF
                self.doRecognition = False
                Msg_S = "Folder with positive photos NOT found: '%s'" %\
                                                (folder_positive_photos)
                self.log(Level.WARNING,Msg_S)
            else:
                # Ok, recognition is ON
                self.doRecognition = True
                Msg_S = "Face recognition ON (folder positive photo(s):'%s')"%\
                                                (folder_positive_photos)
                self.log(Level.INFO,Msg_S)

                
                with open(Case.getCurrentCase().getModuleDirectory() + "\\config.json", 'w') as safe_file:
                    json.dump({"flags": self.localSettings.getAllFlags(), "wanted_folder": folder_positive_photos}, safe_file)

        # Activate for DEBUG
        #with open(CONFIGURATION_PATH, "w") as out:
        #    json.dump({"flags": self.localSettings.getAllFlags(),
        #               "paths": self.localSettings.getAllPaths()}, out)
        self.context = context


    #--------------------------------------------------------------------
    # Added by Patricio 
    # 2018-07-21
    #--------------------------------------------------------------------
    def shutDown(self):
        """shutdown code"""
        # Elaspsed time
        FDRIModuleFactory.g_elapsed_time_secs = time.time() -\
                                        FDRIModuleFactory.g_start_time

        Log_S = "Total elapsed time: %f secs" %\
                (FDRIModuleFactory.g_elapsed_time_secs)
        self.log(Level.INFO, Log_S)


    # Where the analysis is done.
    # The 'dataSource' object being passed in is of type org.sleuthkit.datamodel.Content.
    # See: http://www.sleuthkit.org/sleuthkit/docs/jni-docs/4.4.1/interfaceorg_1_1sleuthkit_1_1datamodel_1_1_content.html
    # 'progressBar' is of type org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress
    # See: http://sleuthkit.org/autopsy/docs/api-docs/4.4/classorg_1_1sleuthkit_1_1autopsy_1_1ingest_1_1_data_source_ingest_module_progress.html
    def process(self, dataSource, progressBar):

        # we don't know how much work there is yet
        progressBar.switchToIndeterminate()

        # Start timer for file copy operation
        start_copy_time = time.time()

        # case insensitive SQL LIKE clause is used to query the case database
        # FileManager API: http://sleuthkit.org/autopsy/docs/api-docs/4.4.1/classorg_1_1sleuthkit_1_1autopsy_1_1casemodule_1_1services_1_1_file_manager.html
        fileManager = Case.getCurrentCase().getServices().getFileManager()

        files = []
        for extension in self.extensions:
            try:
                files.extend(fileManager.findFiles(
                    dataSource, "%" + extension))
            except TskCoreException:
                self.log(Level.INFO, "Error getting files from: '" +
                         extension + "'")

        numFiles = len(files)
        if not numFiles:
            self.log(Level.WARNING, "Didn't find any usable files!")
            return IngestModule.ProcessResult.OK

        # Check if the user pressed cancel while we were busy
        if self.context.isJobCancelled():
            return IngestModule.ProcessResult.OK

        output_dir = Case.getCurrentCase().getModuleDirectory()
        module_dir = os.path.join(output_dir,dataSource.getName(),C_FDRI_DIR)
        
        # Create top-level DIR to save FDIR's created files
        full_dirname_dataSource = os.path.join(output_dir,dataSource.getName())
        if not os.path.exists(full_dirname_dataSource):
            os.mkdir(full_dirname_dataSource)

        # TEMP is needed by Autopsy
        temp_dir = os.path.join(Case.getCurrentCase().getTempDirectory(),
                                                        dataSource.getName())
        if not os.path.exists(temp_dir):
            os.mkdir(temp_dir)

        temp_dir = os.path.join(temp_dir, C_FDRI_DIR)
        if not os.path.exists(temp_dir):
            os.mkdir(temp_dir)

        # We always copy the files (except if a copy already exists)
        # as we will want to change them.
        # We detect the existence of a previous copy if the creation of the dir
        # 'module_dir' triggers an exception
        try:
            os.mkdir(module_dir)
        except:
            self.log(Level.INFO, "Directory already exists for this module")


        #----------------------------------------
        # Init file which holds filenames + size 
        #----------------------------------------
        file_path = os.path.join(module_dir,C_FILE_WITH_FNAMES_AND_SIZES)
        fnames_and_sizes_F = open(file_path,"w") 
        fnames_and_sizes_F.write(C_SEP_S)
        fnames_and_sizes_F.write("# Filename:size (bytes)\n") 
        timestamp_S = datetime.now().strftime('%Y-%m-%d_%Hh%Mm%Ss')
        fnames_and_sizes_F.write("# START: %s\n" % (timestamp_S))
        fnames_and_sizes_F.write(C_SEP_S)

        # Dict to detect identical files
        files_hash_D = {}

        # Flag to record whether files were copied or not
        were_files_copied = True

        # Minimum size (in bytes) for an image file to be processed
        total_files = 0 
        total_small_files = 0

        # A initial version mispelled 'Annotated"...
        avoid_prefix_1 = "Anotated_"
        avoid_prefix_2 = "Annotated_"
        try:
            dir_img = os.path.join(module_dir,"img") 
            os.mkdir(dir_img)
            
            dir_small_files = os.path.join(module_dir,"small_files") + "\\"
            os.mkdir(dir_small_files)

            for file in files:
                total_files = total_files + 1

                filename_S = file.getName()
                Log_S = ""
                if filename_S.find(avoid_prefix_1) is 0:
                    Log_S = "%s file found '%s': skipping" %\
                                    (avoid_prefix_1,filename_S)
                elif filename_S.find(avoid_prefix_2) is 0:
                    Log_S = "%s file found '%s': skipping" %\
                                    (avoid_prefix_2,filename_S)
                if len(Log_S):
                    # Annotated_ found
                    # Log and skip this file
                    self.log(Level.INFO, Log_S)
                    continue

                file_size = file.getSize()
                filename, file_extension = os.path.splitext(file.getName())
                # Record filename and file size in C_FILE_WITH_FNAMES_AND_SIZES
                fnames_and_sizes_F.write("%s:%d\n" %(file.getName(),file_size))

                # If file size is more than C_FILE_MIN_SIZE
                # TODO:: User Choice as option
                if file_size >= C_FILE_MIN_SIZE:
                    new_fname = "%s__id__%s%s" %\
                            (filename,str(file.getId()),file_extension)
                    fullpath_dest = os.path.join(dir_img,new_fname)
                    ContentUtils.writeToFile(file, File(fullpath_dest))


                # We copy small files to a different DIR, so that we
                # can look at them, if needed
                if file_size < C_FILE_MIN_SIZE:
                    total_small_files = total_small_files + 1

                    dest_filename = "%s%s__id__%d%s" %\
                      (dir_small_files,filename,file.getId(),file_extension)
                    ContentUtils.writeToFile(file, File(dest_filename))

                    Log_S = "Skipping file: %s (%d bytes)" %\
                                        (file.getName(),file.getSize())
                    # LOG
                    self.log(Level.INFO, Log_S)

                #--------------------------------
                # Code to detect repeated files
                # We simply use a dictionary 
                # keyed by the MD5 of the file
                # Patricio
                #--------------------------------
                if file_size > 0:
                    md5_hash = self.create_hash(file, "md5")
                    if md5_hash in files_hash_D:
                        # hash already exists: repetition
                        files_hash_D[md5_hash].append(file.getName())
                    else:
                        # hash doesn't yet exist in dictionary: 1st time
                        files_hash_D[md5_hash] = [file_size,file.getName()]
                        
        ##except:
        except Exception, e:            
            were_files_copied = False
            self.log(Level.INFO,"Image folder already exists, skiping file copy")
            self.log(Level.INFO,"Exception: " + str(e))


        #----------------------------------------
        # Close filename+size file
        # Patricio
        #----------------------------------------
        timestamp_S = datetime.now().strftime('%Y-%m-%d_%Hh%Mm%Ss')
        fnames_and_sizes_F.write("# DONE: %s\n" % (timestamp_S))
        if were_files_copied is False:
            Msg_S = "# Exception occurred\n"
            fnames_and_sizes_F.write(Msg_S)
        fnames_and_sizes_F.close()

        #----------------------------------------
        # Dump hash with repeated files
        # (only if files were copied)
        #----------------------------------------
        if were_files_copied is True:
            file_path = os.path.join(module_dir,C_REPEATED_FILES_LOG)
            repeated_files_log_F = open(file_path,"w") 
            repeated_files_log_F.write(C_SEP_S)
            repeated_files_log_F.write("# Repeated files\n")
            timestamp_S = datetime.now().strftime('%Y-%m-%d_%Hh%Mm%Ss')
            repeated_files_log_F.write("# %s\n" % (timestamp_S))
            repeated_files_log_F.write(C_SEP_S)


            for key, info_L in files_hash_D.iteritems():
                if len(info_L) > 2:
                    # only list with more than 2 entries 
                    # (one entry is the file size)
                    S = ""
                    for datum in info_L:
                        S = "%s%s:" % (S,datum)
                    repeated_files_log_F.write("%s\n" %(S))

            repeated_files_log_F.write(C_SEP_S)
            repeated_files_log_F.write("# DONE: %s\n" % (timestamp_S))
            repeated_files_log_F.write(C_SEP_S)
            repeated_files_log_F.close()

        #----------------------------------------
        # Log stats
        #----------------------------------------
        # shutdown copy file timer
        elapsed_copy_time_secs = time.time() - start_copy_time

        Log_S = "%d image files (%d of these were left out -- size <= "\
                "%d bytes)" % (total_files, total_small_files,C_FILE_MIN_SIZE)
        self.log(Level.INFO, Log_S)
        total_copied_files = total_files - total_small_files
        Log_S = "Files copy operation (%d files) took %f secs" %\
                (total_copied_files,elapsed_copy_time_secs)
        self.log(Level.INFO, Log_S)

        #----------------------------------------
        # Start processing timer
        #----------------------------------------
        start_FDRIexe_time = time.time()

        # Location where the output of executable will appear
        timestamp = datetime.now().strftime('%Y-%m-%d_%Hh%Mm%Ss')
        workspace = os.path.join(module_dir,timestamp)
        configFilePath = os.path.join(workspace,C_PARAMS_JSON_FNAME)

        os.mkdir(workspace)

        with open(configFilePath, "w") as out:
            json.dump({
                "paths": self.userPaths,#self.localSettings.getAllPaths(),
                "wanted_faces" : self.localSettings.getPath("1"),
                "imagesPath": os.path.join(module_dir,"img"),
                "doRecognition": self.doRecognition,
                "workspace": workspace,
            }, out)

        #
        # Different calls can also be provided to specify the image size
        #
        # Note that 2GB of GPU memory handle around 2000*2000 images
        # Note that 4GB of GPU memory handle around 3500*3500 images
        # Note that 8GB of GPU memory handle around 6000*6000 images
        #
        # Example:
        #                                                   Required    Minimum size Maximum size
        # target=lambda: self.thread_work(self.pathToExe, configFilePath, 1200*1200, 2000*2000))
        # target=lambda: self.thread_work(self.pathToExe, configFilePath, 1200*1200))
        executable_thread = Thread(
            target=lambda: self.thread_work(self.pathToExe, configFilePath))
        executable_thread.start()

        while(executable_thread.isAlive()):
            if self.context.isJobCancelled():
                self.log(Level.INFO, "User cancelled job! Terminating thread")
                JThread.interrupt(executable_thread)
                self.log(Level.INFO, "Thread terminated")
                self.deleteFiles(module_dir)
                return IngestModule.ProcessResult.OK
            time.sleep(1)

        # Checking if cancel was pressed before starting another job
        if self.context.isJobCancelled():
            return IngestModule.ProcessResult.OK

        #----------------------------------------
        # Compute time takne by FDRI.exe
        #----------------------------------------
        elapsed_FDRIexe_time_secs = time.time() - start_FDRIexe_time
        Log_S = "Process of image files by FDRI.exe took %f secs" %\
                (elapsed_FDRIexe_time_secs)
        self.log(Level.INFO, Log_S)

        #----------------------------------------
        # Start timer of last stage
        #----------------------------------------
        self.log(Level.INFO, "START of last stage")
        start_last_stage_time = time.time()

 
        # Use blackboard class to index blackboard artifacts for keyword search
        blackboard = Case.getCurrentCase().getServices().getBlackboard()

        # Tag files with faces
        artifact_type = BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT

        # Copy files from workspace to temp_dir
        tree_source      = os.path.join(workspace, C_ANNOTATED_DIR)
        tree_destination = os.path.join(temp_dir, C_ANNOTATED_DIR)
        copy_tree(tree_source,tree_destination)

        # Add images with the wanted faces to blackboard
        outPositiveFile = os.path.join(workspace,C_FDRI_WANTED_FNAME)
        if os.path.exists(outPositiveFile):
            with open(outPositiveFile, "r") as out:
                for line in out:
                    file_id = line.split('__id__')[1].split('.')
                    interestingFile = self.findByID(files, file_id[0])

                    if interestingFile == None:
                        continue

                    # Creating new artifacts with faces found
                    artifactList = interestingFile.getArtifacts(artifact_type)
                    if artifactList:
                        self.log(
                            Level.INFO, "Artifact already exists! ignoring")
                    else:
                        art = interestingFile.newArtifact(artifact_type)
                        att = BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(),
                                                  FDRIModuleFactory.moduleName, dataSource.getName() + "/Wanted faces")
                        art.addAttribute(att)
                        try:
                            # index the artifact for keyword search
                            blackboard.indexArtifact(art)
                        except Blackboard.BlackboardException as e:
                            self.log(
                                Level.SEVERE, "Error indexing artifact " + art.getDisplayName())

                        # Adding derivated files to case
                        # These are files with borders on the found faces
                        # Code to deal with filenames with multiple "."
                        # Patricio, 2018.08.09
                        interestingFName = interestingFile.getName()
                        try:
                            name, extension = self.split_fname(interestingFName)
                        except Exception, e:
                            Err_S = "Error in splitting name/extension of '%s' (skipping file)" % (interestingFName)
                            self.log(Level.SEVERE,Err_S)
                            self.log(Level.SEVERE,"Exception: " + str(e))
                            continue

                        # Still here? Good.
                        f_path = "%s__id__%s.%s" %\
                                (name,str(interestingFile.getId()),extension)

                        # We need path relative to temp folder for Autopsy API
                        f_temp_path = os.path.join("Temp",dataSource.getName(),
                                C_FDRI_DIR, C_ANNOTATED_DIR, f_path)
                        f_abs_path = os.path.join(workspace,
                                            C_ANNOTATED_DIR, f_path)

                        # Temporary fix
                        if os.path.exists(f_abs_path):
                            f_size = os.path.getsize(f_abs_path)
                            case = Case.getCurrentCase().getSleuthkitCase()

                            try:
                                abstract_f = case.getAbstractFileById(
                                    interestingFile.getId())

                                # https://sleuthkit.org/autopsy/docs/api-docs/4.4/classorg_1_1sleuthkit_1_1autopsy_1_1casemodule_1_1services_1_1_file_manager.html
                                label_S = C_ANNOTATED_LABEL + interestingFName
                                case.addDerivedFile(label_S, f_temp_path, 
                                       f_size, 0, 0, 0, 0, True, abstract_f,
                                       "", FDRIModuleFactory.moduleName, 
                                       FDRIModuleFactory.moduleVersion, 
                                       "Image with faces",
                                       TskData.EncodingType.NONE)
                            except:
                                self.log(Level.SEVERE,"Error getting abs file")

                    if self.generate_hash:
                        dfxml_path = os.path.join(workspace,C_DFXML_FNAME)
                        self.complete_dfxml( dfxml_path, interestingFile)
class ChooseCorrectToJavaTest(unittest.TestCase):

    # Verifies fix for http://bugs.jython.org/issue1795
    #
    # Note that we use threading.Thread because we have imported
    # java.lang.Thread as Thread

    def test_extended_thread(self):
        class ExtendedThread(threading.Thread, ExtendedInterface):
            def returnSomething(self):
                return "yo yo yo"

        result = [None]

        def f(r):
            r[0] = 47

        t = ExtendedThread(target=f, args=(result, ))
        self.assertEqual(UseExtendedInterface().countWords(t), 3)

        # Also verify that t still works as a regular thread
        t.start()
        t.join()
        self.assertFalse(t.isAlive())
        self.assertEqual(result[0], 47)

    def test_interruption(self):
        # based on this code http://www.jython.org/jythonbook/en/1.0/Concurrency.html#interruption,
        # which demonstrates __tojava__ works properly

        class ExtendedThread(threading.Thread, ExtendedInterface):
            def returnSomething(self):
                return "yo yo yo"

        def wait_until_interrupted(cv):
            with cv:
                while not Thread.currentThread().isInterrupted():
                    try:
                        # this condition variable is never notified, so will only
                        # succeed if interrupted
                        cv.wait()
                    except InterruptedException, e:
                        break

        unfair_condition = threading.Condition()
        threads = [
            ExtendedThread(name="thread #%d" % i,
                           target=wait_until_interrupted,
                           args=(unfair_condition, )) for i in xrange(5)
        ]

        for thread in threads:
            thread.start()
        for thread in threads:
            Thread.interrupt(thread)
        for thread in threads:
            thread.join(5)

        # this assertion only succeeds if threads terminated because
        # they were interrupted
        for thread in threads:
            self.assertFalse(thread.isAlive())