Project

General

Profile

5742c.diff

Tomasz Domin, 05/30/2022 10:26 AM

Download (21.7 KB)

View differences:

new/src/com/goldencode/cache/LFUAgingCache.java 2022-05-30 12:23:29 +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 return
140
    * @return  Most Frequently Used items out of cache
141
    */
142
   
143
   public Map<K, V> getMFUEntries(int maxEntries)
144
   {
145
      Map<K, V> result = new HashMap<K, V>();
146

  
147
      // reverse order to get MFU elements
148
      Iterator<Entry<K, V>> mfuElementsIterator = sorted.descendingIterator();
149
      while (mfuElementsIterator.hasNext())
150
      {
151
         if (--maxEntries < 0)
152
         {
153
            break;
154
         }
155
         Entry<K, V> entry = mfuElementsIterator.next();
156
         result.put(entry.getKey(), entry.getValue());
157
      }
158
      return result;
159
   }
160
   
161
   /**
135 162
    * Get the number of elements which will be expired from the cache each time it becomes full,
136 163
    * and capacity is required for a new element.
137 164
    * 
new/src/com/goldencode/p2j/main/WebClientBuilder.java 2022-05-30 12:23: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-30 12:23:29 +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-30 14:15:46 +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<>(100000);
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
   /** Lock for synchronizing parallel operations on sharedLegacyTextMetricsCache variables */
234
   private static Lock sharedLegacyTextMetricsLock = new ReentrantLock(); 
235

  
219 236
   /** A set containing the name of the fixed fonts. */
220 237
   private static Set<String> fixedFonts = new TreeSet<>();
221 238
   
......
1263 1280
    */
1264 1281
   static int[] getLegacyTextMetrics(String text, String key)
1265 1282
   {
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 };
1283
      String cacheKey = buildTextMetricsKey(key, text);
1284
      
1285
      int[] textMetrics = null;
1286
      
1287
      // map based operations are fast, so lock is only at beginning
1288
      sharedLegacyTextMetricsLock.lock();
1289
      try
1290
      {
1291
         textMetrics = sharedLegacyTextMetricsCache.get(cacheKey);
1292
         if (textMetrics != null)
1293
         {
1294
            return textMetrics;
1295
         }
1296
         
1297
         LegacyTextMetrics ltm = legacyTextMetrics.get(text);
1298
         if (ltm == null)
1299
         {
1300
            if (LOG.isLoggable(Level.FINE))
1301
            {
1302
               LOG.fine("Text [" + text + "] has no legacy metrics with font [" + key + "].");
1303
            }
1304
            // Store invalid metrics value, so client knows it should not query server for this
1305
            sharedLegacyTextMetricsCache.putIfAbsent(cacheKey, new int[0]);
1306
            return null;
1307
         }
1308
      
1309
         LegacyFontMetrics lfm = ltm.legacyMetrics.get(key);
1310
         if (lfm == null)
1311
         {
1312
            if (LOG.isLoggable(Level.FINE))
1313
            {
1314
               LOG.fine("Text [" + text + "] has no legacy metrics with font [" + key + "].");
1315
            }
1316
            // Store invalid metrics value, so client knows it should not query server for this
1317
            sharedLegacyTextMetricsCache.putIfAbsent(cacheKey, new int[0]);
1318
            return null;
1319
         }
1320
         
1321
         textMetrics = new int[] {lfm.width, lfm.height };
1322
         
1323
         sharedLegacyTextMetricsCache.putIfAbsent(cacheKey, textMetrics);
1324
      }
1325
      finally
1326
      {
1327
         sharedLegacyTextMetricsLock.unlock();
1328
      }
1329
      return textMetrics;
1330
   }
1331
   
1332
   /**
1333
    * buildTextMetricsKey - a helper method to unify TextMetrics keys creation
1334
    * @param key - font key
1335
    * @param text - text
1336
    * @return textMetrics key
1337
    */
1338
   private static String buildTextMetricsKey(String key, String text)
1339
   {
1340
      return key + "#" + text;
1289 1341
   }
1290 1342
   
1291 1343
   /**
......
1297 1349
    */
1298 1350
   static Map<String, int[]> readLegacyTextMetrics()
1299 1351
   {
1300
      return sharedLegacyTextDimensions;
1352
      int currentServerCacheSize = Utils.getDirectoryNodeInt(null,
1353
                                                             FONT_METRICS_NODE_ID +
1354
                                                                "/" +
1355
                                                                "server-cache-size",
1356
                                                             -1,
1357
                                                             false);
1358
      
1359
      Map<String, int[]> trimmedTextDimensions = sharedLegacyTextDimensions;
1360
      
1361
      // return full legacy metrics if server cache size is undefined or set to -1
1362
      if (currentServerCacheSize == -1)
1363
      {
1364
         return sharedLegacyTextDimensions;
1365
      }
1366
      else
1367
      {
1368
         int clientCacheSize = Utils.getDirectoryNodeInt(null,
1369
                                                         FONT_METRICS_NODE_ID +
1370
                                                            "/" +
1371
                                                            "client-cache-size",
1372
                                                         100,
1373
                                                         false);
1374
         
1375
         sharedLegacyTextMetricsLock.lock();
1376
         try
1377
         {
1378
            // reinitialize cache only if cache size configuration has changed in runtime
1379
            if (currentServerCacheSize != sharedLegacyTextMetricsCacheSize ||
1380
                sharedLegacyTextMetricsCache == null)
1381
            {
1382
               sharedLegacyTextMetricsCache = new LFUAgingCache<>(currentServerCacheSize);
1383
               sharedLegacyTextMetricsCacheSize = currentServerCacheSize;
1384
            }
1385
            // send to client the most frequently used elements
1386
            trimmedTextDimensions = sharedLegacyTextMetricsCache.getMFUEntries(clientCacheSize);
1387
         }
1388
         finally
1389
         {
1390
            sharedLegacyTextMetricsLock.unlock();
1391
         }
1392
         
1393
      }
1394
      
1395
      // send configured cache size to client
1396
      return trimmedTextDimensions;
1301 1397
   }
1302 1398
   
1303 1399
   /**
......
1573 1669
         {
1574 1670
            String fontKey = fontEntry.getKey();
1575 1671
            LegacyFontMetrics fontMx = fontEntry.getValue();
1576
            String textKey = fontKey + "#" + text;
1672
            String textKey = buildTextMetricsKey(fontKey, text);
1577 1673
            
1578 1674
            dimensions.put(textKey, new int[] { fontMx.width, fontMx.height });
1579 1675
         }
new/src/com/goldencode/p2j/ui/chui/ThinClient.java 2022-05-30 14:21:04 +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-30 14:14:20 +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
   /**
......
1207 1209
      WorkArea wa = locate();
1208 1210
      String tkey = getTextKey(fontKey, text);
1209 1211
      int[] metrics = new int[] { width, height };
1210
      wa.textMetrics.put(tkey, metrics);
1212
      wa.textMetricsCache.put(tkey, metrics);
1211 1213
   }
1212 1214

  
1213 1215
   /**
......
1286 1288

  
1287 1289
   /**
1288 1290
    * Get the metrics for the specified text and font from the
1289
    * {@link WorkArea#textMetrics cache}.
1291
    * {@link WorkArea#textMetricsCache cache} or {@link WorkArea#textMetrics metrics}.
1290 1292
    * <p>
1291 1293
    * If not present, ask the server-side for the metrics.
1292 1294
    *
......
1312 1314

  
1313 1315
   /**
1314 1316
    * Get the metrics for the specified text and font from the
1315
    * {@link WorkArea#textMetrics cache}.
1317
    * {@link WorkArea#textMetricsCache cache} or {@link WorkArea#textMetrics metrics}.
1316 1318
    * <p>
1317 1319
    * If not present, ask the server-side for the metrics.
1318 1320
    *
......
1337 1339
   
1338 1340
   /**
1339 1341
    * Get the metrics for the specified text and font from the
1340
    * {@link WorkArea#textMetrics cache}.
1342
    * {@link WorkArea#textMetricsCache cache} or {@link WorkArea#textMetrics metrics}.
1341 1343
    * <p>
1342 1344
    * If not present, ask the server-side for the metrics.
1343 1345
    * 
......
1358 1360
                                       ScreenDriver<?> driver)
1359 1361
   {
1360 1362
      WorkArea wa = locate();
1361
      String tkey = getTextKey(key, text);
1362
      int[] metrics = wa.textMetrics.get(tkey);
1363
      
1364
      if (metrics != null)
1363
      String cacheKey = getTextKey(key, text);
1364
      int[] metrics = null;
1365
      
1366
      synchronized (wa.textMetricsCache)
1367
      {
1368
         metrics = wa.textMetricsCache.get(cacheKey);
1369
      }
1370
      
1371
      // metrics.length is 0 for server miss entries
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 legacy text metrics initialized from server, it never has metrics.length == 0
1379
      if (wa.textMetrics != null)
1380
      {
1381
         metrics = wa.textMetrics.get(cacheKey);
1382
      }
1383
      // check server-side only if not in cache and not in text metrics from server
1373 1384
      if (metrics == null)
1374 1385
      {
1386
         metrics = wa.server.getLegacyTextMetrics(text, key);
1387
      }
1388
      
1389
      if (metrics == null || metrics.length == 0)
1390
      {
1375 1391
         // let the driver measure the string
1376 1392
         FontDetails<?> fd = wa.fontCache.get(key);
1377 1393
         
......
1396 1412
         }
1397 1413
      }
1398 1414
      
1399
      // cache it
1400
      wa.textMetrics.put(tkey, metrics);
1401
      
1415
      // put result into cache
1416
      synchronized (wa.textMetricsCache)
1417
      {
1418
         wa.textMetricsCache.put(cacheKey, metrics);
1419
      }
1402 1420
      return metrics;
1403 1421
   }
1404 1422

  
......
1454 1472
      /** Access to the client's environment.  May be <code>null</code> if not on a Windows OS. */
1455 1473
      private EnvironmentDaemon ed;
1456 1474
      
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[]>();
1475
      /** A full text metrics from server. Only used if client cache metrics is not configured*/
1476
      private Map<String, int[]> textMetrics = null;
1477

  
1478
      /** A cache of text metrics.  0-index is the width, 1-index is the height.
1479
       *  Default size will help in case server will not send anything at startup
1480
       */
1481
      private LRUCache<String, int[]> textMetricsCache = new LRUCache<String, int[]>(10000);
1459 1482
      
1460 1483
      /** Flag to track if the work area has been initialized. */
1461 1484
      private boolean initialized = false;
......
1477 1500
       * Initialize this work area.
1478 1501
       * 
1479 1502
       * @param    server
1503
       * @param    configured TextMetrics cache size
1480 1504
       *           The server exports.
1481 1505
       */
1482
      public void initialize(ServerExports server)
1506
      public void initialize(ServerExports server, int textMetricsCacheSize)
1483 1507
      {
1484 1508
         initialized = true;
1485 1509
         if (OutputManager.getDriver().isChui())
......
1497 1521
            fontCache.clear();
1498 1522
            windowFontTables.clear();
1499 1523
            envFontTables.clear();
1500
            textMetrics.clear();
1501 1524
            
1502 1525
            // initialize only in GUI mode
1503 1526
            this.server = server;
......
1523 1546
            
1524 1547
            // pre-populate the legacy text metrics for all the fonts in the font-table
1525 1548
            Map<String, int[]> legacyTextMetrics = server.readLegacyTextMetrics();
1526
            textMetrics.putAll(legacyTextMetrics);
1527

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