I'm currently developing a small single-page Web app using Mojolicious. The app has a Javascript frontend (using Backbone) that talks to a REST-ish API; the layout of the source is roughly:
use Mojolicious::Lite;
# ... setup code ...
get '/' => sub {
my $c = shift;
# fetch+stash data for bootstrapped collections...
$c->render('app_template');
};
get '/api_endpoint' => sub {
my $c = shift;
# fetch appropriate API data...
$c->render(json => $response);
};
# ... more API endpoints ...
app->start;
The app template uses EP, but very minimally; the only server-side template directives just insert JSON for bootstrapped collections. It's deployed via Apache as a plain CGI script. (This isn't optimal, but it's for low-traffic internal use, and more intricate server configuration is problematic in context.) Perl CGI is configured via mod_perl.
This works most of the time, but occasionally the renderer somehow gets the idea that it should cache the template and ignore changes to it. The debug records in error_log show "Rendering cached template" rather than the normal "Rendering template", and my new changes to the template stop appearing in the browser. I can't find a reliable way to stop this, though it will eventually stop on its own according to conditions I can't discern.
How can I make the app notice template changes reliably? Alternatively, how can I disable template caching completely?
How can I make the app notice template changes reliably?
This is what the morbo development server is for. Morbo wouldn't be used for your live code deployment, but for a development environment where you are continually changing your code and templates. Generally changes to live code and templates are meant to be handled by restarting the application server, or Apache in your case. (Hypnotoad has a hot-restart capability for this purpose)
Alternatively, how can I disable template caching completely?
To do this, add the following setup code (outside of routes, after use Mojolicious::Lite):
app->renderer->cache->max_keys(0);
For old answer see below.
I turned the findings of this answer into a plugin and released it on CPAN as Mojolicious::Plugin::Renderer::WithoutCache after discussing wit Grinnz on IRC, where they encouraged a release.
You can use it like this:
use Mojolicious::Lite;
plugin 'Renderer::WithoutCache';
It will create a new Cache object that does nothing, and install that globally into the renderer. That way, it doesn't need to be created every time like my initial answer below did.
In theory, this should be faster than Grinnz' approach (which is more sensible), and since you explicitly don't want to cache, you obviously want things to be as fast as possible, right? It's supposedly faster because the real Mojo::Cache would still need to go and try to set the cache, but then abort because there are no more free keys, and it also would try to look up the values from the cache every time.
I benchmarked this with both Dumbbench and Benchmark. Both of them showed negligible results. I ran them each a couple of times, but they fluctuated a lot, and it's not clear which one is faster. I included output of a run where my implementation was faster, but it still shows how minuscule the difference is.
Benchmark with Dumbbench:
use Dumbbench;
use Mojolicious::Renderer;
use Mojolicious::Controller;
use Mojolicious::Plugin::Renderer::WithoutCache::Cache;
my $controller = Mojolicious::Controller->new;
my $renderer_zero_keys = Mojolicious::Renderer->new;
$renderer_zero_keys->cache->max_keys(0);
my $renderer_nocache = Mojolicious::Renderer->new;
$renderer_nocache->cache( Mojolicious::Plugin::Renderer::WithoutCache::Cache->new );
my $bench = Dumbbench->new(
target_rel_precision => 0.005,
initial_runs => 5000,
);
$bench->add_instances(
Dumbbench::Instance::PerlSub->new(
name => 'max_keys',
code => sub {
$renderer_zero_keys->render( $controller, { text => 'foobar' } );
}
),
Dumbbench::Instance::PerlSub->new(
name => 'WithoutCache',
code => sub {
$renderer_nocache->render( $controller, { text => 'foobar' } );
}
),
);
$bench->run;
$bench->report;
__END__
max_keys: Ran 8544 iterations (3335 outliers).
max_keys: Rounded run time per iteration: 5.19018e-06 +/- 4.1e-10 (0.0%)
WithoutCache: Ran 5512 iterations (341 outliers).
WithoutCache: Rounded run time per iteration: 5.0802e-06 +/- 5.6e-09 (0.1%)
Benchmark with Benchmark:
use Benchmark 'cmpthese';
use Mojolicious::Renderer;
use Mojolicious::Controller;
use Mojolicious::Plugin::Renderer::WithoutCache::Cache;
my $controller = Mojolicious::Controller->new;
my $renderer_zero_keys = Mojolicious::Renderer->new;
$renderer_zero_keys->cache->max_keys(0);
my $renderer_nocache = Mojolicious::Renderer->new;
$renderer_nocache->cache( Mojolicious::Plugin::Renderer::WithoutCache::Cache->new );
cmpthese(
-5,
{
'max_keys' => sub {
$renderer_zero_keys->render( $controller, { text => 'foobar' } );
},
'WithoutCache' => sub {
$renderer_nocache->render( $controller, { text => 'foobar' } );
},
}
);
__END__
Rate max_keys WithoutCache
max_keys 190934/s -- -2%
WithoutCache 193846/s 2% --
I recon in a heavy load environment with lots of calls it would eventually make a difference, but that is very hard to prove. So if you don't like to think about the internals of the cache, this plugin might be useful.
Old answer:
Looking at Mojolicious::Plugin::EPRenderer I found out that there is a cache. It's a Mojo::Cache instance, which has the methods get, set and max_keys, and inherits from Mojo::Base (like probably everything in Mojolicious).
The ::EPRenderer gets a $renderer, which is a Mojolicious::Renderer. It holds the Mojo::Cache instance. I looked at $c with Data::Printer, and found out that there is a $c->app that holds all of those.
Knowing this, you can easily make your own cache class that does nothing.
package Renderer::NoCache;
use Mojo::Base -base;
sub get {}
sub set {}
sub max_keys {}
Now you stick it into $c.
package Foo;
use Mojolicious::Lite;
get '/' => sub {
my $c = shift;
$c->app->renderer->cache( Renderer::NoCache->new );
$c->render(template => 'foo', name => 'World');
};
app->start;
__DATA__
## foo.html.ep
Hello <%= $name =%>.
Now every attempt to get or set the cache simply does nothing. It will try caching, but it will never find anything.
Of course it's not great to make a new object every time. It would be better to make that object once at startup and get it into the internal permanent version of app. You have CGI, so it might not make a difference.
You could also just monkey-patch the get out of Mojo::Cache. This more hacky approach will do the same thing:
package Foo;
use Mojolicious::Lite;
*Mojo::Cache::get = sub { };
get '/' => sub {
my $c = shift;
$c->render(template => 'foo', name => 'World');
};
app->start;
But beware: we just disabled fetching from every cache in your application that uses Mojo::Cache. This might not be what you want.
Related
I'm trying to use the following Lua script using C# StackExchange library:
private const string LuaScriptToExecute = #"
local current
current = redis.call(""incr"", KEYS[1])
if current == 1 then
redis.call(""expire"", KEYS[1], KEYS[2])
return 1
else
return current
end
Whenever i'm evaluating the script "as a string", it works properly:
var incrementValue = await Database.ScriptEvaluateAsync(LuaScriptToExecute,
new RedisKey[] { key, ttlInSeconds });
If I understand correctly, each time I invoke the ScriptEvaluateAsync method, the script is transmitted to the redis server which is not very effective.
To overcome this, I tried using the "prepared script" approach, by running:
_setCounterWithExpiryScript = LuaScript.Prepare(LuaScriptToExecute);
...
...
var incrementValue = await Database.ScriptEvaluateAsync(_setCounterWithExpiryScript,
new[] { key, ttlInSeconds });
Whenever I try to use this approach, I receive the following error:
ERR Error running script (call to f_7c891a96328dfc3aca83aa6fb9340674b54c4442): #user_script:3: #user_script: 3: Lua redis() command arguments must be strings or integers
What am I doing wrong?
What is the right approach in using "prepared" LuaScripts that receive dynamic parameters?
If I look in the documentation: no idea.
If I look in the unit test on github it looks really easy.
(by the way, is your ttlInSeconds really RedisKey and not RedisValue? You are accessing it thru KEYS[2] - shouldnt that be ARGV[1]? Anyway...)
It looks like you should rewrite your script to use named parameters and not arguments:
private const string LuaScriptToExecute = #"
local current
current = redis.call(""incr"", #myKey)
if current == 1 then
redis.call(""expire"", #myKey, #ttl)
return 1
else
return current
end";
// We should load scripts to whole redis cluster. Even when we dont have any.
// In that case, there will be only one EndPoint, one iteration etc...
_myScripts = _redisMultiplexer.GetEndPoints()
.Select(endpoint => _redisMultiplexer.GetServer(endpoint))
.Where(server => server != null)
.Select(server => lua.Load(server))
.ToArray();
Then just execute it with anonymous class as parameter:
for(var setCounterWithExpiryScript in _myScripts)
{
var incrementValue = await Database.ScriptEvaluateAsync(
setCounterWithExpiryScript,
new {
myKey: (RedisKey)key, // or new RedisKey(key) or idk
ttl: (RedisKey)ttlInSeconds
}
)// .ConfigureAwait(false); // ? ;-)
// when ttlInSeconds is value and not key, just dont cast it to RedisKey
/*
var incrementValue = await
Database.ScriptEvaluateAsync(
setCounterWithExpiryScript,
new {
myKey: (RedisKey)key,
ttl: ttlInSeconds
}
).ConfigureAwait(false);*/
}
Warning:
Please note that Redis is in full-stop mode when executing scripts. Your script looks super-easy (you sometimes save one trip to redis (when current != 1) so i have a feeling that this script will be counter productive in greater-then-trivial scale. Just do one or two calls from c# and dont bother with this script.
First of all, Jan's comment above is correct.
The script line that updated the key's TTL should be redis.call(""expire"", KEYS[1], ARGV[1]).
Regarding the issue itself, after searching for similar issues in RedisStackExchange's Github, I found that Lua scripts do not work really well in cluster mode.
Fortunately, it seems that "loading the scripts" isn't really necessary.
The ScriptEvaluateAsync method works properly in cluster mode and is sufficient (caching-wise).
More details can be found in the following Github issue.
So at the end, using ScriptEvaluateAsync without "preparing the script" did the job.
As a side note about Jan's comment above that this script isn't needed and can be replaced with two C# calls, it is actually quite important since this operation should be atomic as it is a "Rate limiter" pattern.
I'm trying to figure out how to use the Ldap class in Symfony3. I've successfully created and bound a connection but I can't get any results on a query. To make sure that the query actually works, I ran a bare php version:
if($lconn = ldap_connect('ds.mydomain.ca')){
ldap_set_option($lconn, LDAP_OPT_REFERRALS, 0);
ldap_set_option($lconn, LDAP_OPT_PROTOCOL_VERSION, 3);
if($lbind = ldap_bind($lconn,'webuser','password')){
$filter ="(&(sn=Smith)(givenname=J*))";
if(!$result = ldap_search($lconn, "dc=ds, dc=mydomain, dc=ca", $filter)) throw \Exception("Error in search query: ".ldap_error($lconn));
$output = ldap_get_entries($lconn, $result);
}else{
$output='bind failed';
}
} else {
$output= 'connection failed';
}
It returns the expected number of results.
On the other hand, this query done with Symfony 3's Ldap component returns 0 results:
//use Symfony\Component\Ldap\Ldap
$ldap = Ldap::create('ext_ldap', array(
'host' => 'ds.mydomain.ca',
'version' => 3,
'debug' => true,
'referrals' => false,
));
$ldap->bind('webuser', 'password');
$q = $ldap->query("dc=ds, dc=nrc, dc=ca", "(&(sn=Smith)(givenname=J*))");
$output = $q->execute();
Any idea why the Symfony ldap query fails when all its options should be identical to those I used for the bare php query?
I reposted this question on the Symfony github. #ChadSikorra was there too. And he made it clear what my issue was. Here's his explanation:
If you look at the collection class, nothing is done with the result
resource until initialize() is called in the class. If you do
return array('output' => array('bare' => $bare, 'symfony' =>
$symf->toArray())); it will call initialize and you'll see the
entries populated in the class. Unless there's something else going
on.
Do you still experience this issue with the latest 3.1+ versions?
Sorry but I don't go very often on Stack Overflow and spend most of my time on Github so I didn't see your question before.
As #ChadSikorra said, you should be using the toArray() method of the resulting Collection class, or you should iterate on the results directly.
The implementation is made so that the results are traversed in a memory-efficient manner, without storing all the results in an array by default, but the toArray() method can do this for you. Behind the scenes,it actually converts the resulting itératif to an array using the appropriate PHP function (iterator_to_array).
By the way, there used to be some inconsistency between the iterator and the toArray() function call, but that has been fixed in recent versions.
Cheers!
Laravel 4: In the context of consume-your-own-api, my XyzController uses my custom InternalAPiDispatcher class to create a Request object, push it onto a stack (per this consideration), then dispatch the Route:
class InternalApiDispatcher {
// ...
public function dispatch($resource, $method)
{
$this->request = \Request::create($this->apiBaseUrl . '/' . $resource, $method);
$this->addRequestToStack($this->request);
return \Route::dispatch($this->request);
}
To start with, I'm working on a basic GET for a collection, and would like the Response content to be in the format of an Eloquent model, or whatever is ready to be passed to a View (perhaps a repository thingy later on when I get more advanced). It seems inefficient to have the framework create a json response and then I decode it back into something else to display it in a view. What is a simple/efficient/elegant way to direct the Request to return the Response in the format I desire wherever I am in my code?
Also, I've looked at this post a lot, and although I'm handling query string stuff in the BaseContorller (thanks to this answer to my previous question) it all seems to be getting far too convoluted and I feel I'm getting lost in the trees.
EDIT: could the following be relevant (from laravel.com/docs/templates)?
"By specifying the layout property on the controller, the view specified will be created for you and will be the assumed response that should be returned from actions."
Feel free to mark this as OT if you like, but I'm going to suggest that you might want to reconsider your problem in a different light.
If you are "consuming your own API", which is delivered over HTTP, then you should stick to that method of consumption.
For all that it might seem weird, the upside is that you could actually replace that part of your application with some other server altogether. You could run different parts of your app on different boxes, you could rewrite the HTTP part completely, etc, etc. All the benefits of "web scale".
The route you're going down is coupling the publisher and the subscriber. Now, since they are both you, or more accurately your single app, this is not necessarily a bad thing. But if you want the benefits of being able to access your own "stuff" without resorting to HTTP (or at least "HTTP-like") requests, then I wouldn't bother with faking it. You'd be better off defining a different internal non-web Service API, and calling that.
This Service could be the basis of your "web api", and in fact the whole HTTP part could probably be a fairly thin controller layer on top of the core service.
It's not a million miles away from where you are now, but instead of taking something that is meant to output HTTP requests and mangling it, make something that can output objects, and wrap that for HTTP.
Here is how I solved the problem so that there is no json encoding or decoding on an internal request to my API. This solution also demonstrates use of route model binding on the API layer, and use of a repository by the API layer as well. This is all working nicely for me.
Routes:
Route::get('user/{id}/thing', array(
'uses' => 'path\to\Namespace\UserController#thing',
'as' => 'user.thing'));
//...
Route::group(['prefix' => 'api/v1'], function()
{
Route::model('thing', 'Namespace\Thing');
Route::model('user', 'Namespace\User');
Route::get('user/{user}/thing', [
'uses' => 'path\to\api\Namespace\UserController#thing',
'as' => 'api.user.thing']);
//...
Controllers:
UI: UserController#thing
public function thing()
{
$data = $this->dispatcher->dispatch('GET', “api/v1/user/1/thing”)
->getOriginalContent(); // dispatcher also sets config flag...
// use $data in a view;
}
API: UserController#thing
public function thing($user)
{
$rspns = $this->repo->thing($user);
if ($this->isInternalCall()) { // refs config flag
return $rspns;
}
return Response::json([
'error' => false,
'thing' => $rspns->toArray()
], 200);
Repo:
public function thing($user)
{
return $user->thing;
}
Here is how I achieved it in Laravel 5.1. It requires some fundamental changes to the controllers to work.
Instead of outputting response with return response()->make($data), do return $data.
This allows the controller methods to be called from other controllers with App::make('apicontroller')->methodname(). The return will be object/array and not a JSON.
To do processing for the external API, your existing routing stays the same. You probably need a middleware to do some massaging to the response. Here is a basic example that camel cases key names for the JSON.
<?php
namespace App\Http\Middleware;
use Closure;
class ResponseFormer
{
public function handle($request, Closure $next)
{
$response = $next($request);
if($response->headers->get('content-type') == 'application/json')
{
if (is_array($response->original)) {
$response->setContent(camelCaseKeys($response->original));
}
else if (is_object($response->original)) {
//laravel orm returns objects, it is a huge time saver to handle the case here
$response->setContent(camelCaseKeys($response->original->toArray()));
}
}
return $response;
}
}
The problem, essentially, is that I can't get my sitemap config to support multiple sitemaps. It's always looking for "default" even when I name my instances and request another. Now for the background.
I've been pouring over the docs for the new implementation of MVCSiteMapProvider. They are now using Dependency Injection to configure the SiteMapProvider. We have an existing StructureMap DI implementation, so I followed the instructions and added, in our case
ObjectFactory.Configure(x =>
{
...
x.AddRegistry<MvcSiteMapProviderRegistry>();
...
});
Then I started tweaking the MvcSiteMapProviderRegistry.cs file to implement my multiple sitemap scenario. I have multiple site map files, either will work as long as it's called "default". If I remove the "default" item then it breaks and complains that "default" is missing. Which I assume is because it can't find my instance. Here's how I have them defined. I suspect the problem is somewhere in here... the loader which it says I have to configure in the Global.asax is looking for ISiteMapLoader but I'm adding my multiple configuration to SiteMapBuilderSet... anyway here's the code.
// Register the sitemap builder
string absoluteFileName = HostingEnvironment.MapPath("~/Main.sitemap");
string absoluteFileName2 = HostingEnvironment.MapPath("~/Test.sitemap");
var xmlSource = this.For<IXmlSource>().Use<FileXmlSource>()
.Ctor<string>("fileName").Is(absoluteFileName);
var reservedAttributeNameProvider = this.For<ISiteMapXmlReservedAttributeNameProvider>()
.Use<SiteMapXmlReservedAttributeNameProvider>()
.Ctor<IEnumerable<string>>("attributesToIgnore").Is(new string[0]);
var builder = this.For<ISiteMapBuilder>().Use<CompositeSiteMapBuilder>()
.EnumerableOf<ISiteMapBuilder>().Contains(y =>
{
y.Type<XmlSiteMapBuilder>()
.Ctor<ISiteMapXmlReservedAttributeNameProvider>().Is(reservedAttributeNameProvider)
.Ctor<IXmlSource>().Is(xmlSource);
y.Type<ReflectionSiteMapBuilder>()
.Ctor<IEnumerable<string>>("includeAssemblies").Is(includeAssembliesForScan)
.Ctor<IEnumerable<string>>("excludeAssemblies").Is(new string[0]);
y.Type<VisitingSiteMapBuilder>();
});
var xmlSource2 = this.For<IXmlSource>().Use<FileXmlSource>()
.Ctor<string>("fileName").Is(absoluteFileName2);
var builder2 = this.For<ISiteMapBuilder>().Use<CompositeSiteMapBuilder>()
.EnumerableOf<ISiteMapBuilder>().Contains(y =>
{
y.Type<XmlSiteMapBuilder>()
.Ctor<ISiteMapXmlReservedAttributeNameProvider>().Is(reservedAttributeNameProvider)
.Ctor<IXmlSource>().Is(xmlSource2);
y.Type<ReflectionSiteMapBuilder>()
.Ctor<IEnumerable<string>>("includeAssemblies").Is(includeAssembliesForScan)
.Ctor<IEnumerable<string>>("excludeAssemblies").Is(new string[0]);
y.Type<VisitingSiteMapBuilder>();
});
// Configure the builder sets
this.For<ISiteMapBuilderSetStrategy>().Use<SiteMapBuilderSetStrategy>()
.EnumerableOf<ISiteMapBuilderSet>().Contains(x =>
{
/* x.Type<SiteMapBuilderSet>()
.Ctor<string>("instanceName").Is("default")
.Ctor<bool>("securityTrimmingEnabled").Is(securityTrimmingEnabled)
.Ctor<bool>("enableLocalization").Is(enableLocalization)
.Ctor<ISiteMapBuilder>().Is(builder)
.Ctor<ICacheDetails>().Is(cacheDetails);*/
/*
x.Type<SiteMapBuilderSet>()
.Ctor<string>("instanceName").Is("MainSiteMapProvider")
.Ctor<bool>("securityTrimmingEnabled").Is(securityTrimmingEnabled)
.Ctor<bool>("enableLocalization").Is(enableLocalization)
.Ctor<ISiteMapBuilder>().Is(builder)
.Ctor<ICacheDetails>().Is(cacheDetails);*/
x.Type<SiteMapBuilderSet>()
.Ctor<string>("instanceName").Is("TestSiteMapProvider")
.Ctor<bool>("securityTrimmingEnabled").Is(securityTrimmingEnabled)
.Ctor<bool>("enableLocalization").Is(enableLocalization)
.Ctor<ISiteMapBuilder>().Is(builder2)
.Ctor<ICacheDetails>().Is(cacheDetails);
});
In my global.asax.cs I added
MvcSiteMapProvider.SiteMaps.Loader = Resolver.Get<ISiteMapLoader>();
and to reference in my view I have
#Html.MvcSiteMap("TestSiteMapProvider").Menu(false, true, true)
but it must not be able to find "TestSiteMapProvider" because it always displays "default" or complains if it doesn't exist.
I also thought it might have something to do with the Cache, as I see the filename referenced there, but I don't know how to add multiple instances to the cache, so I just disabled it. I'm really not doing anything fancy with my sitemaps anyway, and this whole thing is really feeling like massive overkill just to get some flippin automatic breadcrumbs!
Apparently there was another help doc that I wasn't aware of. I had completed all of the steps thus far properly, but I also needed to implement ISiteMapCacheKeyGenerator.
See this doc (which wasn't named this when I started.)
https://github.com/maartenba/MvcSiteMapProvider/wiki/Multiple-Sitemaps-in-One-Application
We use BIRT since version 2 (currently using 4.2.2) and have always been plagued by the PDF (itext?) fonts register time.
org.eclipse.birt.report.engine.layout.pdf.font.FontMappingManagerFactory$2 run
INFO: register fonts in c:/windows/fonts cost:17803ms
This process only occurs the first time the render is used. Subsequent renders are not problematic.
The problem seems to be the time wasted when accessing ALL the system connected DRIVES.
Editing the fontsConfig.xml in org.eclipse.birt.report.engine.fonts plugin, reducing the search paths does not resolve the issue. ALL connected drives are accessed by BIRT.
<font-paths>
<path path="/windows/fonts" />
</font-paths>
Is there a simple solution for this without having to render a "dummy" report to initialize BIRT in the background??
This isn't necessarily a solution, but potentially another option as a workaround that I came up with after we noticed the same issue and did some investigating. It's also (IMO) better than generating a dummy report.
On startup (or at some other point depending on your needs), make the following call:
FontMappingManagerFactory.getInstance().getFontMappingManager(format, Locale.getDefault());
Why?
BIRT uses com.lowagie.text.FontFactory (iText) to register the fonts. Calls to that class are made from
org.eclipse.birt.report.engine.layout.pdf.font.FontMappingManagerFactory which also spits out the log entries given in the question.
Within FontMappingManagerFactory we can see where the log entries are coming from:
private static void registerFontPath( final String fontPath )
{
AccessController.doPrivileged( new PrivilegedAction<Object>( ) {
public Object run( )
{
long start = System.currentTimeMillis( );
File file = new File( fontPath );
if ( file.exists( ) )
{
if ( file.isDirectory( ) )
{
FontFactory.registerDirectory( fontPath );
}
else
{
FontFactory.register( fontPath );
}
}
long end = System.currentTimeMillis( );
logger.info( "register fonts in " + fontPath + " cost:"
+ ( end - start ) + "ms" ); // <-- Here!
return null;
}
} );
}
Working backwards, we see that registerFontPath(String) is called by loadFontMappingConfig(URL), etc etc resulting in the following call hierarchy:
getFontMappingManager(String, Locale)
`-- createFontMappingManager(String, Locale)
`-- loadFontMappingConfig(String)
`-- loadFontMappingConfig(URL)
`-- registerFontPath(String)
And getFontMappingManager(String, Locale) is the public method we can call. More importantly, however, is that the method also caches the FontMappingManager that gets created:
public synchronized FontMappingManager getFontMappingManager(
String format, Locale locale )
{
HashMap managers = (HashMap) cachedManagers.get( format );
if ( managers == null )
{
managers = new HashMap( );
cachedManagers.put( format, managers );
}
FontMappingManager manager = (FontMappingManager) managers.get( locale );
if ( manager == null )
{
manager = createFontMappingManager( format, locale );
managers.put( locale, manager );
}
return manager;
}
As a result, when you're ready to go generate your PDF, it will already be in the cache and BIRT won't have to go call down to the FontFactory and re-register the fonts.
But what about the format String?
This bit is some speculation, but I think the valid options are the OUTPUT_FORMAT_XXX Strings in IRenderOption. For our purposes I debugged to see that we want the String to be pdf. Considering that's also conveniently the desired output format, I assume IRenderOption.OUTPUT_FORMAT_PDF is the route to go.
If you're ultimately creating both PDFs and HTML files, it appears that you could make the call twice (once with IRenderOption.OUTPUT_FORMAT_PDF and once with IRenderOption.OUTPUT_FORMAT_HTML) and only the font config files which are different will be considered (ie. you won't be reading from c:/windows/fonts twice).
All that said, take this with a grain of salt. I believe this is completely safe, since the purpose of getFontMappingManager(String, Locale) is to get an object for accessing available fonts, etc., and it conveniently caches the result. However, if that were to change in the future you may end up with a tricky-to-find bug on your hands.
I would suggest that you can modify the fontsConfig.xml and remove the fonts that you no longer need. Also remove the drives that you dont want birt to check for fonts.