Netflix Ribbon and Polling for Server List - load-balancing

I'm currently trying out the Netflix Ribbon library and I'm trying to dynamically update a list of available endpoints to load balance.
I've successfully created a httpResourceGroup that uses a configuration based server list, e.g.:
httpResourceGroup = Ribbon.createHttpResourceGroup("searchServiceClient",
ClientOptions.create()
.withMaxAutoRetriesNextServer(3)
.withLoadBalancerEnabled(true)
.withConfigurationBasedServerList(serverList))
However, I'd like to be able to use a DynamicServerList in the httpResourceGroup. I've managed to build a load balancer as follows:
LoadBalancerBuilder.<Server>newBuilder()
.withDynamicServerList(servicesList)
.buildDynamicServerListLoadBalancer();
but I can't find a way to swap out the load balancer configured by the httpResourceGroup ClientOptions.
Anyone know how I can do this?

The solution is to not specify withConfigurationBasedServerList() when constructing an HttpResourceGroup since this I believe this is meant for a fixed list though I am not sure. There are many ways to initialize a dynamic load balancer (typically you would never swap it out, but reuse the same load balancer and swap out new Servers as they become available or go away. The most straightforward way to do this might be via Archaius-based configuration.
Option 1
Create a config.properties file on the classpath containing the following
ribbon.NIWSServerListClassName=com.example.MyServerList
ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RoundRobinRule
Option 2
System.setProperty("ribbon.NIWSServerListClassName", "com.example.MyServerList");
System.setProperty("ribbon.NFLoadBalancerRuleClassName", "com.netflix.loadbalancer.RoundRobinRule");
Create a ServerList implementation
import java.util.Arrays;
import java.util.List;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
public class MyServerList implements ServerList<Server> {
#Override
public final List<Server> getUpdatedListOfServers() {
// TODO do some fancy stuff here
return Arrays.asList(new Server("1.2.3.4", 8888), new Server("5.6.7.8", 9999));
}
#Override
public final List<Server> getInitialListOfServers() {
return Arrays.asList(new Server("1.2.3.4", 8888), new Server("5.6.7.8", 9999));
}
}
Run the code
HttpResourceGroup httpResourceGroup = Ribbon.createHttpResourceGroup("searchServiceClient",
ClientOptions.create()
.withMaxAutoRetriesNextServer(3);
HttpRequestTemplate<ByteBuf> recommendationsByUserIdTemplate = httpResourceGroup.newTemplateBuilder("recommendationsByUserId", ByteBuf.class)
.withMethod("GET")
.withUriTemplate("/users/{userId}/recommendations")
.withFallbackProvider(new RecommendationServiceFallbackHandler())
.withResponseValidator(new RecommendationServiceResponseValidator())
.build();
Observable<ByteBuf> result = recommendationsByUserIdTemplate.requestBuilder()
.withRequestProperty("userId", “user1")
.build()
.observe();
It sounds like you already have a ServerList implementation which is where you would do any event driven updates to your server list, but keep the load balancer the same.

Related

Modify remote driver URL at runtime

I have a project which is based on the serenity-bdd/serenity-cucumber-starter project. I'm using test-containers to start a couple of Docker containers as well as a Selenium Grid container to run the test against.
new GenericContainer<>(SELENIUM_IMAGE)
...
.withExposedPorts(SELENIUM_CONTAINER_PORT, SELENIUM_CONTAINER_NOVNC_PORT)
...
);
When the tests start, test-containers will ramp up the containers and bind random host ports to all exposed ports of the containers.
Because of that, I cannot define a fixed value in serenity.conf for the url of the remote driver
webdriver.remote.url = "http://localhost:????/wd/hub"
Thus I need a way to set webdriver.remote.url programmatically.
One option would be to use the FixedHostPortGenericContainer, which allows you define the host port on which the container exposed port will be bound to.
I'd rather would like to use a different approach though, as the developers state that
While this works, we strongly advise against using fixed ports, since this will automatically lead to integrated tests (which are an anti pattern).
So the question is: How can I modify the value of webdriver.remote.url at runtime? Is there any option provided by serenity-bdd to reload the net.thucydides.core.util.SystemEnvironmentVariables at runtime?
Faced recently the same issue, but was lucky enough to find a solution:
import java.net.MalformedURLException;
import java.net.URL;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.RemoteWebDriver;
import net.serenitybdd.core.webdriver.driverproviders.FirefoxDriverCapabilities;
import net.thucydides.core.guice.Injectors;
import net.thucydides.core.util.EnvironmentVariables;
import net.thucydides.core.webdriver.DriverSource;
public class CustomWebDriverFactory implements DriverSource {
#Override
public WebDriver newDriver() {
try {
String ip = "your_dynamic_ip";
return new RemoteWebDriver(
new URL("http://" + ip + ":4444/wd/hub"),
new FirefoxDriverCapabilities(Injectors.getInjector().getProvider(EnvironmentVariables.class).get()).getCapabilities());
}
catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
#Override
public boolean takesScreenshots() {
return true;
}
}
So you have to add such factory implementation and define in serenity.properties:
webdriver.driver = provided
webdriver.provided.type = mydriver
webdriver.provided.mydriver = <your_factory_package>.CustomWebDriverFactory
thucydides.driver.capabilities = mydriver

How to enable module in Orchard migration?

I want to enable a specific module in a migration but the module is not enabled immediately.
The issue here seems to be the state of the module, it is set to Rising in table Orchard_Settings_ShellFeatureStateRecord. In this case I cannot enable the module manually in Admin anymore, I need to restart the web server after the migration has been executed to get the module to state Up.
The migration code looks like
public class Migration: Orchard.Data.Migration.DataMigrationImpl
{
// public
public Migration(Orchard.Environment.Features.IFeatureManager aFeatureManager)
{
mFeatureManager = aFeatureManager;
}
...
public int UpdateFrom1()
{
System.Collections.Generic.Dictionary<string, Orchard.Environment.Extensions.Models.FeatureDescriptor> lFeatures =
mFeatureManager.GetAvailableFeatures().ToDictionary(m => m.Id, m => m);
if (lFeatures.ContainsKey("Orchard.Taxonomies"))
mFeatureManager.EnableFeatures(new string[] {"Orchard.Taxonomies"}, true);
...
}
// private
private readonly Orchard.Environment.Features.IFeatureManager mFeatureManager;
}
I also tried using IModuleManager, did not work. Then I tried enabling another simple feature like Orchard.Alias.UI, did not work either.
Is this intended behavior or what might be wrong in the code?
If your feature has a dependency on taxonomies, enabling it will also enable taxonomies. You don't need to do anything else. That is, unless the dependency is something new that you're adding with the new version. In that case, I'd probably display a warning asking the user to enable it, and I'd make the code resilient to taxonomies not being enabled (which is a good idea no matter what)
Change this line:
mFeatureManager.EnableFeatures(new string[] {"Orchard.Taxonomies"},true);
to this one:
mFeatureManager.EnableFeatures(new string[] {"Orchard.Taxonomies"});

Change application.conf runtime?

I want to set the database connection at run time for my Play project. I know that I can set a property run time with the following code:
#OnApplicationStart public class Bootstrap extends Job
{
#Override public void doJob()
{
// now set the values in the properties file
Play.configuration.setProperty("db.driver", dbDriver);
Play.configuration.setProperty("db.url", dbUrl);
Play.configuration.setProperty("db.user", dbUsername);
Play.configuration.setProperty("db.pass", dbPassword);
}
}
But when executing the code above the file is not actually changed, I think just in memory.
How can I set the database properties and force Play! to use this properties in order to connect to the right database onApplicationStart?
Thanks!
UPDATE 2012-01-29
Solution is possible via a plugin. In this plugin I have to override onConfigurationRead() and apply the properties to the configuration file at that moment. I will try to post some code as soon as I have time for this.
By the time you change the properties, the DB plugin is already initialized. You need to write a plugin and overwrite the onConfigurationRead() method, then put your new settings there. Paly's dbplugin will init later on.
I faced with the necessity of programmatically obtaining values from aws secret manager in runtime before using that values in play framework configuration. You can override initial default values from application.conf and add new.
Work for play framework v2.7.3
import com.typesafe.config.ConfigValueFactory;
import play.api.Configuration;
import play.api.inject.guice.GuiceApplicationBuilder;
import play.api.inject.guice.GuiceApplicationLoader;
public class ExtendedGuiceApplicationLoader extends GuiceApplicationLoader {
#Override
public GuiceApplicationBuilder builder(Context context) {
Configuration configuration = new Configuration(
context.initialConfiguration().underlying()
.withValue("db.default.username",
ConfigValueFactory.fromAnyRef("aws.secret.db.username"))
.withValue("db.default.password",
ConfigValueFactory.fromAnyRef("aws.secret.db.password"))
);
return super.builder(
new Context(context.environment(),
configuration,
context.lifecycle(),
context.devContext())
);
}
}
Don´t forget add this string to application.conf
play.application.loader="youpackage.ExtendedGuiceApplicationLoader"
Are you sure this is what you really intend to do?
Play offers the possibility to add different configurations in your application.conf
for example you could have:
db.url=mydefaulturl
%uat.db.url=uaturl
%prod.db.url=produrl
%prod1.db.url=prod1url
And then start the app with play start --%uat or play start --%prod

How do you set a custom session when unit testing with wicket?

I'm trying to run some unit tests on a wicket page that only allows access after you've logged in. In my JUnit test I cannot start the page or render it without setting the session.
How do you set the session? I'm having problems finding any documentation on how to do this.
WicketTester tester = new WicketTester(new MyApp());
((MyCustomSession)tester.getWicketSession()).setItem(MyFactory.getItem("abc"));
//Fails to start below, no session seems to be set
tester.startPage(General.class);
tester.assertRenderedPage(General.class);
What I frequently do is to provide a fake WebApplication with overrides for things that I want to mock or stub.
Among the things I override is the method
public abstract Session newSession(Request request, Response response);
which allows you to return a fake session setup with anything you want.
This is in Wicket 1.3 - if you're using 1.4, some of this may have changed, and as noted in another response, it may be related to a wicket bug.
But assuming the interface hasn't changed too much, overriding this method may also be another way of working around the issue in WICKET-1215.
You may be running into WICKET-1215. Otherwise what you're doing looks fine. For example, I have a Junit4 setup method that looks like:
#Before
public void createTester() {
tester = new WicketTester( new MyApp() );
// see http://issues.apache.org/jira/browse/WICKET-1215
tester.setupRequestAndResponse();
MyAppSession session = (MyAppSession) tester.getWicketSession();
session.setLocale(Locale.CANADA);
session.setUser(...);
}
Using Wicket 1.4, I use my normal WebApplication and WebSession implementations, called NewtEditor and NewtSession in my app. I override newSession, where I do the same than in the regular app code, except that I sign in right away. I also override newSessionStore for performance reasons, I copied this trick from WicketTesters code.
tester = new WicketTester(new NewtEditor()
{
#Override
public Session newSession(Request request, Response response)
{
NewtSession session = new NewtSession(request);
session.signIn(getTestDao());
return session;
}
#Override
protected ISessionStore newSessionStore()
{
// Copied from WicketTester: Don't use a filestore, or we spawn lots of threads,
// which makes things slow.
return new HttpSessionStore(this);
}
});

Eclipse: Within a plug-in, how to access another plug-ins preference store?

I have an Eclipse plug-in with a checkbox in the plug-in's preference page.
This checkbox is used for enabling and disabling an editor, which is being launched from this plug-in.
However, the problem is, I would also like to be able to enable and disable this 'editor-launch' from another plug-in, by having actions which change the value of the checkbox in the above mentioned preference page.
Here's the problem, how do I access that local preference store from another plug-in?
I've tried things like..
View myView = (View) PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().findView("ViewID");
But this 'myView' always seems to be null.. And also, what would I do with the view since it's the Plug-in I want.
Platform.getBundle('bundleName')...
Same here, want the Plugin, not the bundle corresponding to is.
No matter what I try nothing seems to work.
Does anyone have any ideas?
There are two ways of doing this:
Please refer to http://www.vogella.com/tutorials/EclipsePreferences/article.html#preferences_pluginaccess
Using .getPluginPreferences(). For example, there is a plugin class "com.xxx.TestPlugin" which extends org.eclipse.ui.plugin.AbstractUIPlugin.Plugin, in order to get access to the preferences of TestPlugin. The plugin code could be below:
public class TestPlugin extends AbstractUIPlugin {
private static TestPlugin plugin;
public static final String PREF_TEST = "test_preference";
/**
* The constructor.
*/
public TestPlugin() {
plugin = this;
}
/**
* This method is called upon plug-in activation
*/
public void start(BundleContext context) throws Exception {
super.start(context);
}
/**
* This method is called when the plug-in is stopped
*/
public void stop(BundleContext context) throws Exception {
super.stop(context);
plugin = null;
}
/**
* Returns the shared instance.
*/
public static TestPlugin getDefault() {
return plugin;
}
}
To access the preference of TestPlugin, the code could be:
TestPlugin.getDefault().getPluginPreferences().getDefaultBoolean(TestPlugin.PREF_TEST);
Or have a look at this answer: Writing Eclipse plugin to modify Editor Preferences
This thread recommend the use of a Service tracker:
ServiceTracker tracker = new ServiceTracker(ToolkitPlugin.getDefault().getBundle().getBundleContext(),
IProxyService.class.getName(), null);
tracker.open();
proxyService = (IProxyService) tracker.getService();
proxyService.addProxyChangeListener(this);
This may work.
Prefs stores are found per plugin. This is one way to get a prefs store for the plugin whose activator class is ActivatorA.
IPreferenceStore store = ActivatorA.getDefault().getPreferenceStore();
If you want another plugin to refer to the same store, perhaps you could expose some api on ActivatorA for it to get there, e.g.
public IPreferenceStore getSharedPrefs() {
return ActivatorA.getDefault().getPreferenceStore();
}
The second plugin would find the shared store by doing this
IPreferenceStore sharedPrefs = ActivatorA.getSharedPrefs();
Good luck.