Ticket #2241: 2241_final.patch

File 2241_final.patch, 15.6 KB (added by meddle, 15 years ago)

Implementation code

  • modules/org.sophie2.extra.func.browser/src/main/java/org/sophie2/extra/func/browser/view/NativeBrowserFrameView.java

    ### Eclipse Workspace Patch 1.0
    #P sophie
     
    6565                return getBean().makeProp(model.class);          
    6666        } 
    6767 
    68         @Override 
    69         public void initAccess(ResourceAccess access) { 
    70                 super.initAccess(access); 
    71                 // set the first frame 
    72                 setTime(TimePos.INTRO_START); 
     68        private void loadUrl() { 
    7369                String url = model().get().getUrl(); 
    7470                if (url.startsWith("http://") == false) { 
    7571                        url = "http://" + url; 
    7672                } 
    7773                this.bridge.load(url); 
    7874        } 
     75         
     76        @Override 
     77        public void initAccess(ResourceAccess access) { 
     78                super.initAccess(access); 
     79                // set the first frame 
     80                setTime(TimePos.INTRO_START); 
     81                loadUrl(); 
     82        } 
    7983 
    8084        @Override 
    8185        public Prop<NativeBrowserContentSceneElement> contentElement() { 
     
    9498        @Override 
    9599        public void setTime(TimePos time) { 
    96100                super.setTime(time); 
     101                if (TimePos.MAIN_START.equals(time)) { 
     102                        loadUrl(); 
     103                } 
     104                 
    97105                ImmImage grabbedPage = this.bridge.grabPage(); 
    98106                if (grabbedPage != null) { 
    99107                        contentElement().get().image().set(grabbedPage); 
  • modules/org.sophie2.main.media.natlib/src/main/java/org/sophie2/main/media/natlib/decoder/NativeMediaHandler.java

     
    161161         
    162162        private final AudioThread audioThread;  
    163163 
    164  
    165  
    166164        /** 
    167165         * Constructs by the file data. 
    168166         *  
  • modules/org.sophie2.base.natlib/src/main/java/org/sophie2/base/natlib/NativeBridgeRegistry.java

     
     1package org.sophie2.base.natlib; 
     2 
     3import java.util.Comparator; 
     4import java.util.Timer; 
     5import java.util.TimerTask; 
     6import java.util.TreeSet; 
     7 
     8/** 
     9 * Registry for the {@link NativeBridge}s. All the bridges when started are 
     10 * registered and if they are not used for some defined time, they are cleared 
     11 * from the registry and stopped. 
     12 *  
     13 * The registry is singleton for now... 
     14 *  
     15 * All the constants in this class and it's sub-classes are 
     16 * determined through testing. 
     17 *  
     18 * @author meddle 
     19 */ 
     20public class NativeBridgeRegistry { 
     21 
     22        /** 
     23         * The time period in milliseconds, the registry checks  
     24         * if there are native processes that are not active for too long. 
     25         */ 
     26        private final static long UPDATE_PERIOD = 5000; 
     27 
     28        /** 
     29         * The maximum time in milliseconds one process should be  
     30         * not active before it is stopped.  
     31         */ 
     32        private final static long TIME_OUT = 4000; 
     33 
     34        private static NativeBridgeRegistry instance = null; 
     35 
     36        /** 
     37         * Implementation of the {@link Comparator} interface to take in 
     38         * mind the {@link NativeBridge#getLastCalled()} when comparing. 
     39         *  
     40         * @author meddle 
     41         */ 
     42        protected static class BridgeComparator implements Comparator<NativeBridge> { 
     43 
     44                public int compare(NativeBridge bridge1, NativeBridge bridge2) { 
     45                        if (bridge1.getLastCalled() > bridge2.getLastCalled()) { 
     46                                return 1; 
     47                        } else if (bridge1.getLastCalled() < bridge2.getLastCalled()) { 
     48                                return -1; 
     49                        } 
     50 
     51                        return 0; 
     52                } 
     53 
     54        } 
     55 
     56        private final Timer timer; 
     57 
     58        private TreeSet<NativeBridge> bridges; 
     59 
     60        /** 
     61         * The {@link TimerTask} that stops the not active native processes. 
     62         *  
     63         * @author meddle 
     64         */ 
     65        protected class ActivityObserver extends TimerTask { 
     66                 
     67                /** 
     68                 * The percent of the bridges to check for activity. 
     69                 * in decimal fraction.  
     70                 */ 
     71                private final static float SUSPEND_PERCENT = 0.20f; 
     72 
     73                @Override 
     74                @SuppressWarnings("synthetic-access") 
     75                public void run() { 
     76                        synchronized (NativeBridgeRegistry.this.bridges) { 
     77                                TreeSet<NativeBridge> sortedBridges = new TreeSet<NativeBridge>(new BridgeComparator()); 
     78                                sortedBridges.addAll(NativeBridgeRegistry.this.bridges); 
     79 
     80                                NativeBridgeRegistry.this.bridges = sortedBridges; 
     81                                clearSuspended(); 
     82                        } 
     83                } 
     84 
     85                @SuppressWarnings("synthetic-access") 
     86                private void clearSuspended() { 
     87                        int size = NativeBridgeRegistry.this.bridges.size(); 
     88                        int index = (int) Math.ceil(size * SUSPEND_PERCENT); 
     89                        long now = System.currentTimeMillis(); 
     90 
     91                        for (int i = 0; i < index; i++) { 
     92 
     93                                NativeBridge first = NativeBridgeRegistry.this.bridges.first(); 
     94 
     95                                long suspend = now - first.getLastCalled(); 
     96                                if (suspend >= TIME_OUT) { 
     97                                        NativeBridgeRegistry.this.unregisterFirst(); 
     98                                } else { 
     99                                        break; 
     100                                } 
     101                        } 
     102                } 
     103        } 
     104 
     105        private NativeBridgeRegistry() { 
     106                this.bridges = new TreeSet<NativeBridge>(new BridgeComparator()); 
     107                this.timer = new Timer(true); 
     108 
     109                this.timer.schedule(new ActivityObserver(), 0, UPDATE_PERIOD); 
     110        } 
     111 
     112        /** 
     113         * Getter for the {@link NativeBridge}s registered. 
     114         *  
     115         * @return 
     116         *                      The sorted set of the bridges. 
     117         */ 
     118        protected TreeSet<NativeBridge> getBridges() { 
     119                return this.bridges; 
     120        } 
     121 
     122        /** 
     123         * Getter of the instance of the singleton. 
     124         *  
     125         * @return 
     126         *                      The instance. 
     127         */ 
     128        public static NativeBridgeRegistry get() { 
     129                if (instance == null) { 
     130                        instance = new NativeBridgeRegistry(); 
     131                } 
     132 
     133                return instance; 
     134        } 
     135 
     136        /** 
     137         * Registers a {@link NativeBridge} in the registry. 
     138         *  
     139         * @param bridge 
     140         *                              The bridge to register. 
     141         */ 
     142        public void register(NativeBridge bridge) { 
     143                synchronized (this.bridges) { 
     144                        if (!this.bridges.contains(bridge)) { 
     145                                this.bridges.add(bridge); 
     146                        } 
     147                } 
     148        } 
     149 
     150        /** 
     151         * Unregisters and stops a bridge from the registry. 
     152         *  
     153         * @param bridge 
     154         *                              The bridge to unregister. 
     155         */ 
     156        public void unregister(NativeBridge bridge) { 
     157                synchronized (this.bridges) { 
     158                        if (this.bridges.contains(bridge)) { 
     159                                if (bridge.isRunning()) { 
     160                                        bridge.stop(); 
     161                                } 
     162 
     163                                this.bridges.remove(bridge); 
     164                        } 
     165                } 
     166        } 
     167 
     168        /** 
     169         * Unregisters and stops the first bridge in the registry. 
     170         */ 
     171        private void unregisterFirst() { 
     172                synchronized (this.bridges) { 
     173                        NativeBridge first = this.bridges.first();  
     174 
     175                        if (first.isRunning()) { 
     176                                first.stop(); 
     177                        } 
     178 
     179                        this.bridges.remove(first); 
     180                } 
     181        } 
     182 
     183        /** 
     184         * Unregisters all the processes from the registry. 
     185         */ 
     186        public void unregisterAll() { 
     187                while (!this.bridges.isEmpty()) { 
     188                        unregisterFirst(); 
     189                } 
     190        } 
     191} 
  • modules/org.sophie2.extra.func.browser/src/main/java/org/sophie2/extra/func/browser/nat/bridge/BrowserNativeBridge.java

     
    1111import org.sophie2.base.natlib.NativeBridge; 
    1212import org.sophie2.base.natlib.OkResponse; 
    1313import org.sophie2.base.natlib.Response; 
     14import org.sophie2.extra.func.browser.model.BrowserFrameR4; 
    1415import org.sophie2.extra.func.browser.nat.commands.GetInfoCommand; 
    1516import org.sophie2.extra.func.browser.nat.commands.GrabPageCommand; 
    1617import org.sophie2.extra.func.browser.nat.commands.LoadCommand; 
     
    3334 */ 
    3435public class BrowserNativeBridge extends NativeBridge { 
    3536 
     37        private int lastWidth = 0; 
     38        private int lastHeight = 0; 
     39        private String lastUrl = BrowserFrameR4.KEY_URL.getDefault(); 
     40         
     41         
    3642        @Override 
    3743        public void stop() { 
    3844                SwingUtilities.invokeLater(new Runnable() { 
     
    5157         *            The URL of the page to load. 
    5258         */ 
    5359        public void load(final String url) { 
     60                this.lastUrl = url; 
     61                 
    5462                SwingUtilities.invokeLater(new Runnable() { 
    5563                        public void run() { 
    5664                                sendCommand(new LoadCommand(url), OkResponse.class); 
     
    6169        /** 
    6270         * Returns a capture of the current web page. 
    6371         *  
    64          * @return A capture of the current web page. 
     72         * @return  
     73         *                      A capture of the current web page. 
    6574         */ 
    6675        public ImmImage grabPage() { 
    6776                PageGrabResponse response = sendCommand(new GrabPageCommand(), PageGrabResponse.class); 
     
    199208                        } 
    200209                }); 
    201210        } 
    202  
     211         
     212        @Override 
     213        protected Process getNativeProcess() { 
     214                boolean wasRunning = isRunning(); 
     215                 
     216                Process process = super.getNativeProcess(); 
     217                 
     218                if (!wasRunning) { 
     219                        assert isRunning(); 
     220                        resize(this.lastWidth, this.lastHeight); 
     221                        load(this.lastUrl); 
     222                } 
     223                 
     224                return process; 
     225        } 
     226         
    203227        /** 
    204228         * Resizes the native browser. 
    205229         *  
     
    209233         *            New height. 
    210234         */ 
    211235        public void resize(final int width, final int height) { 
     236                this.lastWidth = width; 
     237                this.lastHeight = height; 
     238                 
    212239                sendCommand(new ResizeCommand(width, height), OkResponse.class); 
    213240        } 
    214241 
  • modules/org.sophie2.base.natlib/src/main/java/org/sophie2/base/natlib/NativeBridge.java

     
    2020 *  
    2121 * This class is intended to be extended by every specific native wrapper. It 
    2222 * contains common methods for sending {@link Command}s and receiving 
    23  * {@link Response}s 
     23 * {@link Response}s. 
    2424 *  
    25  * @author nenko, sveto, milo 
     25 * The {@link Process}es in the bridges are killed periodically when 
     26 * unused so write the sub classes stateless enough... 
    2627 *  
     28 * @author nenko, sveto, milo 
     29 * @author meddle 
    2730 */ 
    2831public abstract class NativeBridge { 
    2932 
     
    5053                try { 
    5154                        SophieLog.debug(getExecutableString()); 
    5255                        this.nativeProcess = Runtime.getRuntime().exec(new String[] { getExecutableString() }); 
    53                         this.nativeStderr = new BufferedReader(new InputStreamReader(this.nativeProcess 
    54                                         .getErrorStream())); 
     56                        this.nativeStderr = new BufferedReader( 
     57                                        new InputStreamReader(this.nativeProcess.getErrorStream())); 
    5558                        // SophieLog.trace("sending start-command..."); 
    5659                        try { 
    5760                                int exit = this.nativeProcess.exitValue(); 
     
    7275                                } 
    7376                                this.nativeProcess = null; 
    7477                                this.nativeStderr = null; 
     78                                NativeBridgeRegistry.get().unregister(this); 
    7579                        } 
    7680                } 
    7781                SophieLog.debug("NativeBridge started = " + this.started); 
     
    8387                } catch (InterruptedException e) { 
    8488                        throw new RuntimeException(e); 
    8589                } 
     90 
     91                NativeBridgeRegistry.get().register(this); 
    8692                dumpError(); 
    8793 
    8894        } 
    8995 
    9096        /** 
     97         * Retrieves the native process, if it is not running,  
     98         * <code>start()</code> is called. 
     99         *  
     100         * @return 
     101         *                      The running native process. 
     102         */ 
     103        protected Process getNativeProcess() { 
     104                if (!isRunning()) { 
     105                        start(); 
     106                } 
     107 
     108                return this.nativeProcess; 
     109        } 
     110 
     111        /** 
    91112         * This method sends stop command to the native process and then destroys 
    92113         * it. The stop command is sent if there's any action that would be done 
    93114         * before stopping the process. 
     
    100121                } catch (RuntimeException e) { 
    101122                        SophieLog.info("Problem while stopping native process : ", e); 
    102123                } 
     124 
    103125                this.started = false; 
    104                 this.nativeProcess.destroy(); 
    105                 this.nativeProcess = null; 
     126                if (this.nativeProcess != null) { 
     127                        this.nativeProcess.destroy(); 
     128                        this.nativeProcess = null; 
     129                } 
    106130                this.nativeStderr = null; 
     131        } 
    107132 
     133        private long lastCalled = System.currentTimeMillis(); 
     134 
     135        /** 
     136         * Retrieves when the bridge was last called. In other words 
     137         * when a command was send for the last time. 
     138         *  
     139         * @return 
     140         *                      The time when the bridge was last called. 
     141         */ 
     142        public long getLastCalled() { 
     143                return this.lastCalled; 
    108144        } 
    109145 
    110146        /** 
     
    113149         * @return The native standard input 
    114150         */ 
    115151        protected OutputStream getNativeStdin() { 
    116                 return this.nativeProcess.getOutputStream(); 
     152                return getNativeProcess().getOutputStream(); 
    117153        } 
    118154 
    119155        /** 
     
    122158         * @return The native standard output 
    123159         */ 
    124160        protected InputStream getNativeStdout() { 
    125                 return this.nativeProcess.getInputStream(); 
     161                return getNativeProcess().getInputStream(); 
    126162        } 
    127163 
    128164        /** 
     
    170206                } 
    171207                return getNativeFile(prefix + getExecutableName() + suffix).getAbsolutePath(); 
    172208        } 
    173          
     209 
    174210        private File getNativeFile(String path) { 
    175 //              //TODO: development version 
    176 //              final String nativesFolder = "sophie2-native"; 
    177 //              File root = new File(".").getAbsoluteFile(); 
    178 //              while (!Arrays.asList(root.list()).contains(nativesFolder)) { 
    179 //                      root = root.getParentFile(); 
    180 //              } 
    181 //              return new File(root.getAbsolutePath() + "/sophie2-native/target/" + path); 
    182                  
     211                //              //TODO: development version 
     212                //              final String nativesFolder = "sophie2-native"; 
     213                //              File root = new File(".").getAbsoluteFile(); 
     214                //              while (!Arrays.asList(root.list()).contains(nativesFolder)) { 
     215                //                      root = root.getParentFile(); 
     216                //              } 
     217                //              return new File(root.getAbsolutePath() + "/sophie2-native/target/" + path); 
     218 
    183219                //Release version: 
    184220                File nativeFile = FileEntryManager.get().getWritableFileEntry("natives/" + path); 
    185221                String[] commands = new String[] {"chmod", "+x", nativeFile.getAbsolutePath()}; 
     
    192228                        } 
    193229                } 
    194230                return nativeFile; 
    195                  
     231 
    196232        } 
    197233 
    198234        /** 
     
    299335         * @return A {@link Response} for the sent {@link Command} 
    300336         */ 
    301337        public <T extends Response> T sendCommand(Command command, Class<T> expectedClass) { 
    302                 assert isRunning(); 
     338                long now = System.currentTimeMillis(); 
     339                this.lastCalled = now; 
     340 
    303341                writeCommand(command); 
    304342                // SophieLog.trace("Sent " + command); 
    305343                dumpError(); 
     
    316354         *            to be written to stdin of the process 
    317355         */ 
    318356        protected void writeCommand(Command command) { 
    319                 assert isRunning(); 
     357                //assert isRunning(); 
    320358                try { 
    321359                        int size = command.getData().getSize(); 
    322360                        // SophieLog.trace("command sending to native: ID = " 
     
    347385         * @return A {@link Response} read from the stdout of the process 
    348386         */ 
    349387        protected <T extends Response> T readResponse(Class<T> expectedClass) { 
    350                 assert isRunning(); 
     388                //assert isRunning(); 
    351389                try { 
    352390                        BinData data = Message.readData(getNativeStdout()); 
    353391                        dumpError(); 
  • modules/org.sophie2.main.app.commons/src/main/java/org/sophie2/main/app/commons/app/MainWindowLogic.java

     
    1111import org.sophie2.base.layout.model.MainWindowOptions; 
    1212import org.sophie2.base.layout.model.MainWindowOptions.ScreenMode; 
    1313import org.sophie2.base.layout.model.MainWindowOptions.WindowsState; 
    14 import org.sophie2.base.media.MediaUtil; 
     14import org.sophie2.base.natlib.NativeBridgeRegistry; 
    1515import org.sophie2.core.modularity.FakeModuleRegistry; 
    1616import org.sophie2.core.modularity.FileSearchUtil; 
    1717import org.sophie2.core.mvc.EventFilterBuilder; 
     
    117117                                } 
    118118                        } 
    119119                        if (mainWindow.documents().size() == 0) { 
    120                                 MediaUtil.getDefaultAudioOutput().stop(); 
     120                                //MediaUtil.getDefaultAudioOutput().stop(); 
     121                                NativeBridgeRegistry.get().unregisterAll(); 
    121122                                if (MainAppModule.get().getBundleContext() != null) { 
    122123                                        // In true. 
    123124                                        try {