Project

General

Profile

5742b.diff

Tomasz Domin, 05/27/2022 10:27 AM

Download (19.8 KB)

View differences:

new/src/com/goldencode/cache/LFUAgingCache.java 2022-05-27 14:23:43 +0000
2 2
** Module   : LFUAgingCache.java
3 3
** Abstract : LFU cache implementation which adjusts element importance by age of last access
4 4
**
5
** Copyright (c) 2014-2021, Golden Code Development Corporation.
5
** Copyright (c) 2014-2022, Golden Code Development Corporation.
6 6
**
7 7
** -#- -I- --Date-- --------------------------------------Description----------------------------------------
8 8
** 001 ECF 20140922 Created initial version.
......
11 11
** 004 ECF 20190327 Refactored to include more implementation-specific logic, allowing parent
12 12
**                  class to be more generic.
13 13
** 005 ECF 20210519 Refactoring to adjust for changes in the class hierarchy.
14
**     TJD 20220527 Support for text-metrics cache controlled by server
14 15
*/
15 16
/*
16 17
** This program is free software: you can redistribute it and/or modify
......
132 133
   }
133 134
   
134 135
   /**
136
    * Retrieve the Most Frequently Used items out of cache
137
    * Order of result is unimportant
138
    * 
139
    * @param   maxEntries - maximum number of entries to reurn
140
    */
141
   
142
   public Map<K,V> getMFUEntries(int maxEntries)
143
   {
144
      Map<K,V> result=new HashMap<K,V>();
145

  
146
      // reverse order to get MFU elements
147
      Iterator<Entry<K, V>> mfuElementsIterator = sorted.descendingIterator();
148
      while(mfuElementsIterator.hasNext())
149
      {
150
         if (--maxEntries < 0)
151
            break;
152
         Entry<K, V> entry=mfuElementsIterator.next();
153
         result.put(entry.getKey(), entry.getValue());
154
      }
155
      return result;
156
   }
157
   
158
   /**
135 159
    * Get the number of elements which will be expired from the cache each time it becomes full,
136 160
    * and capacity is required for a new element.
137 161
    * 
new/src/com/goldencode/p2j/main/WebClientBuilder.java 2022-05-27 13:16:29 +0000
25 25
**     SBI 20211003 Added http configuration settings for the web client.
26 26
**     EVL 20211222 Added implementation to pass options to client driver from server via socket.
27 27
**     EVL 20220105 Improvements for Windows specific options passing via socket for web client.
28
**     TJD 20220527 Support for text-metrics cache controlled by server
28 29
*/
29 30
/*
30 31
** This program is free software: you can redistribute it and/or modify
......
282 283
      cmd.add("client:web:maxHttpIdleTimeout=" + options.get("maxHttpIdleTimeout"));
283 284
      cmd.add("client:web:maxResponseHeaderSize=" + options.get("maxResponseHeaderSize"));
284 285
      cmd.add("client:web:maxRequestHeaderSize=" + options.get("maxRequestHeaderSize"));
286
      cmd.add("client:text-metrics:cache-size="+options.get("clientTextMetricsCacheSize"));
285 287
      
286 288
      // Sets the web client embedded mode
287 289
      cmd.add("client:web:embedded=" + options.get("embedded"));
new/src/com/goldencode/p2j/main/WebClientBuilderOptions.java 2022-05-27 13:17:21 +0000
2 2
** Module   : WebClientBuilderOptions.java
3 3
** Abstract : store web client parameters read from the directory
4 4
**
5
** Copyright (c) 2014-2021, Golden Code Development Corporation.
5
** Copyright (c) 2014-2022, Golden Code Development Corporation.
6 6
**
7 7
** -#- -I- --Date-- ---------------------------------Description----------------------------------
8 8
** 001 CA  20140206 First version, extracted from ClientBuilderOptions.
......
28 28
**     CA  20210924 Replaced 'sticked' with 'fixed' name, for web client taskbar style.
29 29
**     SBI 20210926 Removed code that updates jvm arguments for new client.
30 30
**     SBI 20211003 Added http configuration settings for the web client.
31
**     TJD 20220527 Support for text-metrics cache controlled by server
31 32
 */
32 33
/*
33 34
** This program is free software: you can redistribute it and/or modify
......
88 89

  
89 90
import com.goldencode.p2j.cfg.*;
90 91
import com.goldencode.p2j.net.*;
92
import com.goldencode.p2j.ui.*;
91 93
import com.goldencode.p2j.ui.client.chui.driver.*;
92 94
import com.goldencode.p2j.web.*;
93 95
// must be explicit to avoid abmiguity with Java
......
291 293
      // Read if the drawing batches are cached
292 294
      boolean graphicsCached = cbo.getNode(DIRECTORY_NODE_ID, "graphicsCached", false);
293 295
      
296
      // Get client font metrics cache size from configuration
297
      int clientTextMetricsCacheSize = cbo.getNode(FontTable.FONT_METRICS_NODE_ID,"client-cache-size",0);
298
      
294 299
      String taskBarStyle = cbo.getNode(DIRECTORY_NODE_ID, "taskBarStyle", "fixed").trim();
295 300
      options.put("taskBarStyle", taskBarStyle);
296 301
      
......
320 325
      options.put("pingPongInterval", String.valueOf(pingPongInterval));
321 326
      options.put("maxLostPings", String.valueOf(maxLostPings));
322 327
      options.put("delayBetweenPingTries", String.valueOf(delayBetweenPingTries));
328
      options.put("clientTextMetricsCacheSize", String.valueOf(clientTextMetricsCacheSize));
323 329

  
324 330
      // sets true or false if the web client embedded mode is on or off respectively
325 331
      options.put("embedded", embedded ? "true" : "false");
new/src/com/goldencode/p2j/ui/FontTable.java 2022-05-27 13:21:21 +0000
60 60
** 034 EVL 20220110 Adding support for symbolic font flag.
61 61
** 035 AL2 20220425 Parse and use the web text scale font specification.
62 62
** 036 AL2 20220430 Changed default value for webScale.
63
** 037 TJD 20220525 Support for text-metrics cache controlled by server
63 64
*/
64 65

  
65 66
/*
......
120 121
import java.io.*;
121 122
import java.net.*;
122 123
import java.util.*;
124
import java.util.concurrent.locks.*;
123 125
import java.util.logging.*;
124 126
import javax.xml.parsers.*;
125 127
import org.w3c.dom.*;
......
207 209
   /** The fallback font, in case the default fonts are not specified. */
208 210
   private static final String FALLBACK_DEFAULT_FONT = "System, size 10";
209 211
   
212
   /** The name of the node containing the font metrics management configuration */
213
   public static final String FONT_METRICS_NODE_ID = "font-metrics";
214

  
210 215
   /** A map containing the legacy font metrics. */
211 216
   private static Map<String, LegacyFontMetrics> legacyFontMetrics = new HashMap<>();
212 217
   
......
215 220
   
216 221
   /** A map containing legacy text/font keys and legacy font width and height dimensions */
217 222
   private static Map<String, int[]> sharedLegacyTextDimensions = new HashMap<>();
223

  
224
   /** Runtime cache for legacy text/font keys and legacy font width and height dimensions
225
    * Cache entries are metrics of legacy font stored as int[2]
226
    * Invalid entries are stored as int[0], so client dont need to query server for these
227
    */
228
   private static LFUAgingCache<String, int[]> sharedLegacyTextMetricsCache = new LFUAgingCache<>(10);
229

  
230
   /** Size of a runtime cache for legacy text/font keys and legacy font width and height dimensions */
231
   private static int sharedLegacyTextMetricsCacheSize=0;
218 232
   
233
   private static Lock sharedLegacyTextMetricsLock = new ReentrantLock(); 
234

  
219 235
   /** A set containing the name of the fixed fonts. */
220 236
   private static Set<String> fixedFonts = new TreeSet<>();
221 237
   
......
1263 1279
    */
1264 1280
   static int[] getLegacyTextMetrics(String text, String key)
1265 1281
   {
1266
      LegacyTextMetrics ltm = legacyTextMetrics.get(text);
1267
      if (ltm == null)
1268
      {
1269
         if (LOG.isLoggable(Level.FINE))
1270
         {
1271
            LOG.fine("Text [" + text+ "] has no legacy metrics with font [" + key + "].");
1272
         }
1273

  
1274
         return null;
1275
      }
1276
      
1277
      LegacyFontMetrics lfm = ltm.legacyMetrics.get(key);
1278
      if (lfm == null)
1279
      {
1280
         if (LOG.isLoggable(Level.FINE))
1281
         {
1282
            LOG.fine("Text [" + text+ "] has no legacy metrics with font [" + key + "].");
1283
         }
1284

  
1285
         return null;
1286
      }
1287
      
1288
      return new int[] { lfm.width, lfm.height };
1282
      String cacheKey = buildTextMetricsKey(key,text);
1283
      
1284
      int [] textMetrics = null;
1285
      
1286
      // map based operations are fast, so lock is only at beginning
1287
      sharedLegacyTextMetricsLock.lock();
1288
      try
1289
      {
1290
         textMetrics=sharedLegacyTextMetricsCache.get(cacheKey);
1291
         if (textMetrics!=null)
1292
         {
1293
            return textMetrics;
1294
         }
1295
         
1296
         LegacyTextMetrics ltm = legacyTextMetrics.get(text);
1297
         if (ltm == null)
1298
         {
1299
            if (LOG.isLoggable(Level.FINE))
1300
            {
1301
               LOG.fine("Text [" + text+ "] has no legacy metrics with font [" + key + "].");
1302
            }
1303
            // Store invalid metrics value, so client knows it should not query server for this
1304
            sharedLegacyTextMetricsCache.putIfAbsent(cacheKey, new int[0]);
1305
            return null;
1306
         }
1307
      
1308
         LegacyFontMetrics lfm = ltm.legacyMetrics.get(key);
1309
         if (lfm == null)
1310
         {
1311
            if (LOG.isLoggable(Level.FINE))
1312
            {
1313
               LOG.fine("Text [" + text+ "] has no legacy metrics with font [" + key + "].");
1314
            }
1315
            // Store invalid metrics value, so client knows it should not query server for this
1316
            sharedLegacyTextMetricsCache.putIfAbsent(cacheKey, new int[0]);
1317
            return null;
1318
         }
1319
         
1320
         textMetrics = new int[] { lfm.width, lfm.height };
1321
         
1322
         sharedLegacyTextMetricsCache.putIfAbsent(cacheKey, textMetrics);
1323
      }
1324
      finally
1325
      {
1326
         sharedLegacyTextMetricsLock.unlock();
1327
      }
1328
      return textMetrics;
1329
   }
1330
   
1331
   /**
1332
    * buildTextMetricsKey - a helper method to unify TextMetrics keys creation
1333
    * @param key - font key
1334
    * @param text - text
1335
    * @return textMetrics key
1336
    */
1337
   private static String buildTextMetricsKey(String key, String text)
1338
   {
1339
      return key + "#" + text;
1289 1340
   }
1290 1341
   
1291 1342
   /**
......
1297 1348
    */
1298 1349
   static Map<String, int[]> readLegacyTextMetrics()
1299 1350
   {
1300
      return sharedLegacyTextDimensions;
1351
      int currentServerCacheSize = Utils.getDirectoryNodeInt(null,
1352
                                                             FONT_METRICS_NODE_ID +
1353
                                                                "/" +
1354
                                                                "server-cache-size",
1355
                                                             -1,
1356
                                                             false);
1357
      
1358
      Map<String, int[]> trimmedTextDimensions = sharedLegacyTextDimensions;
1359
      
1360
      // return full legacy metrics if server cache size is undefined or set to -1
1361
      if (currentServerCacheSize == -1)
1362
      {
1363
         return sharedLegacyTextDimensions;
1364
      }
1365
      else
1366
      {
1367
         int clientCacheSize = Utils.getDirectoryNodeInt(null,
1368
                                                         FONT_METRICS_NODE_ID +
1369
                                                            "/" +
1370
                                                            "client-cache-size",
1371
                                                         100,
1372
                                                         false);
1373
         
1374
         sharedLegacyTextMetricsLock.lock();
1375
         try
1376
         {
1377
            // reinitialize cache only if cache size configuration has changed in runtime
1378
            if (currentServerCacheSize != sharedLegacyTextMetricsCacheSize ||
1379
                sharedLegacyTextMetricsCache == null)
1380
            {
1381
               sharedLegacyTextMetricsCache = new LFUAgingCache<>(currentServerCacheSize);
1382
               sharedLegacyTextMetricsCacheSize = currentServerCacheSize;
1383
            }
1384
            // send to client the most frequently used elements
1385
            trimmedTextDimensions = sharedLegacyTextMetricsCache.getMFUEntries(clientCacheSize);
1386
         }
1387
         finally
1388
         {
1389
            sharedLegacyTextMetricsLock.unlock();
1390
         }
1391
         
1392
      }
1393
      
1394
      // send configured cache size to client
1395
      return trimmedTextDimensions;
1301 1396
   }
1302 1397
   
1303 1398
   /**
......
1573 1668
         {
1574 1669
            String fontKey = fontEntry.getKey();
1575 1670
            LegacyFontMetrics fontMx = fontEntry.getValue();
1576
            String textKey = fontKey + "#" + text;
1671
            String textKey = buildTextMetricsKey(fontKey, text);
1577 1672
            
1578 1673
            dimensions.put(textKey, new int[] { fontMx.width, fontMx.height });
1579 1674
         }
new/src/com/goldencode/p2j/ui/chui/ThinClient.java 2022-05-27 13:25:13 +0000
2811 2811
**     EVL 20220516          The call to moveToTop(boolean) should be now called with explicit cast.  This
2812 2812
**                           is Frame class specific method.
2813 2813
**     AL2 20220524          Replaced getTempDirectory with readHtmlBrowserWhitelist.
2814
**     TJD 20220527          Support for text-metrics cache controlled by server
2814 2815
*/
2815 2816

  
2816 2817
/*
......
3683 3684
      // negotiate the UI theme (before requesting fonts and colors from server)
3684 3685
      ThemeManager.init(tc.server, cfg.getString("client", "driver", "theme", null));
3685 3686
      
3687
      // read cache size from configuration
3688
      // 0 means - use the same size server sends to client
3689
      String textMetricsCacheSize=cfg.getString("client", "text-metrics", "cache-size", "0");
3690
      int requestedTextMetricsCacheSize = 0;
3691
      if (textMetricsCacheSize != null)
3692
      {
3693
         try
3694
         {
3695
            requestedTextMetricsCacheSize = Integer.valueOf(textMetricsCacheSize);
3696
         }
3697
         catch(NumberFormatException e)
3698
         {
3699
         }
3700
      }
3701

  
3702
      
3686 3703
      // initialize the font manager
3687
      FontManager.init(tc.server);
3704
      FontManager.init(tc.server, requestedTextMetricsCacheSize);
3688 3705
      
3689 3706
      // initialize the client side widget ID worker
3690 3707
      WidgetIdHelper.initWorker(new ClientIdHelper());
new/src/com/goldencode/p2j/ui/client/FontManager.java 2022-05-27 13:26:23 +0000
2 2
** Module   : FontManager.java
3 3
** Abstract : client-side font management class
4 4
**
5
** Copyright (c) 2011-2020, Golden Code Development Corporation.
5
** Copyright (c) 2011-2022, Golden Code Development Corporation.
6 6
**
7 7
** -#- -I- --Date-- ---------------------------------Description----------------------------------
8 8
** 001 SIY 20111115 Created initial version
......
55 55
** 035 HC  20200416 Extended direct font control to allow to use system font names.
56 56
** 036 AL2 20220425 Added web text scale to font key generation.
57 57
**                  Onyl web scaled fonts are checked to be locally installed.
58
** 037 TJD 20220525 Support for text-metrics cache controlled by server
58 59
*/
59 60

  
60 61
/*
......
115 116
import java.util.*;
116 117
import java.util.logging.*;
117 118

  
119
import com.goldencode.cache.*;
118 120
import com.goldencode.p2j.security.*;
119 121
import com.goldencode.p2j.ui.*;
120 122
import com.goldencode.p2j.ui.chui.*;
......
1053 1055
    * @param    server
1054 1056
    *           The server exports.
1055 1057
    */
1056
   public static void init(ServerExports server)
1058
   public static void init(ServerExports server, int textMetricsCacheSize)
1057 1059
   {
1058 1060
      // do not use locate() here
1059 1061
      WorkArea wa = local.get();
1060 1062
      
1061
      wa.initialize(server);
1063
      wa.initialize(server, textMetricsCacheSize);
1062 1064
   }
1063 1065

  
1064 1066
   /**
......
1359 1361
   {
1360 1362
      WorkArea wa = locate();
1361 1363
      String tkey = getTextKey(key, text);
1362
      int[] metrics = wa.textMetrics.get(tkey);
1363
      
1364
      if (metrics != null)
1364
      int[] metrics = null;
1365
      
1366
      synchronized (wa.textMetrics)
1367
      {
1368
         metrics = wa.textMetrics.get(tkey);
1369
      }
1370
      
1371
      // invalid metrics.length is 0
1372
      if (metrics != null && metrics.length > 0)
1365 1373
      {
1366 1374
         // found from cache, fast exit
1367 1375
         return metrics;
1368 1376
      }
1369

  
1370
      // check server-side
1371
      metrics = wa.server.getLegacyTextMetrics(text, key);
1372 1377
      
1378
      // check server-side only if not in cache  
1373 1379
      if (metrics == null)
1374 1380
      {
1381
         metrics = wa.server.getLegacyTextMetrics(text, key);
1382
      }
1383
      
1384
      if (metrics == null || metrics.length == 0)
1385
      {
1375 1386
         // let the driver measure the string
1376 1387
         FontDetails<?> fd = wa.fontCache.get(key);
1377 1388
         
......
1397 1408
      }
1398 1409
      
1399 1410
      // cache it
1400
      wa.textMetrics.put(tkey, metrics);
1401
      
1411
      synchronized (wa.textMetrics)
1412
      {
1413
         wa.textMetrics.put(tkey, metrics);
1414
      }
1402 1415
      return metrics;
1403 1416
   }
1404 1417

  
......
1454 1467
      /** Access to the client's environment.  May be <code>null</code> if not on a Windows OS. */
1455 1468
      private EnvironmentDaemon ed;
1456 1469
      
1457
      /** A cache of text metrics.  0-index is the width, 1-index is the height. */
1458
      private final Map<String, int[]> textMetrics = new HashMap<String, int[]>();
1470
      /** A cache of text metrics.  0-index is the width, 1-index is the height.
1471
       *  Default size will help in case server will not send anything at startup
1472
       */
1473
      private LRUCache<String, int[]> textMetrics = new LRUCache<String, int[]>(100);
1459 1474
      
1460 1475
      /** Flag to track if the work area has been initialized. */
1461 1476
      private boolean initialized = false;
......
1477 1492
       * Initialize this work area.
1478 1493
       * 
1479 1494
       * @param    server
1495
       * @param    configured TextMetrics cache size
1480 1496
       *           The server exports.
1481 1497
       */
1482
      public void initialize(ServerExports server)
1498
      public void initialize(ServerExports server, int textMetricsCacheSize)
1483 1499
      {
1484 1500
         initialized = true;
1485 1501
         if (OutputManager.getDriver().isChui())
......
1497 1513
            fontCache.clear();
1498 1514
            windowFontTables.clear();
1499 1515
            envFontTables.clear();
1500
            textMetrics.clear();
1501 1516
            
1502 1517
            // initialize only in GUI mode
1503 1518
            this.server = server;
......
1521 1536
               this.systemFonts[i] = createFont(sysFonts[i]);
1522 1537
            }
1523 1538
            
1539
            
1524 1540
            // pre-populate the legacy text metrics for all the fonts in the font-table
1525 1541
            Map<String, int[]> legacyTextMetrics = server.readLegacyTextMetrics();
1526
            textMetrics.putAll(legacyTextMetrics);
1527

  
1542
            
1543
            // if client cache size is not defined by configuration option client-cache-size
1544
            if (textMetricsCacheSize == 0)
1545
            {
1546
               // if server sends a very set of items set it to a minimal value
1547
               if (legacyTextMetrics == null || legacyTextMetrics.size() < 100)
1548
               {
1549
                  textMetrics = new LRUCache<String,int[]>(100);
1550
               }
1551
               else
1552
               {
1553
                  // if client cache is not defined in config use size of data from server
1554
                  textMetrics = new LRUCache<String,int[]>(legacyTextMetrics.size());
1555
               }
1556
            }
1557
            else
1558
            {
1559
               // if client cache size is defined in config - use it
1560
               textMetrics = new LRUCache<String,int[]>(textMetricsCacheSize);
1561
            }
1562
            
1563
            // copy text metrics received from server into local cache
1564
            if (legacyTextMetrics != null)
1565
            {
1566
               for (String key:legacyTextMetrics.keySet())
1567
               {
1568
                  textMetrics.put(key, legacyTextMetrics.get(key));
1569
               }
1570
            }
1571
            
1528 1572
            // the rest of the fonts are installing here
1529 1573
            driver.releaseWindow();
1530 1574