LaunchDarkly: Flushing data from client in offline mode - java

I'm working on a POC using LaunchDarkly's Java + Redis SDK and one of my requirements is initializing a 2nd LaunchDarkly client in "offline" mode. Due to my existing architecture one application will connect to LaunchDarkly and hydrate a Redis instance. The 2nd application will connect to the same data store, but the client will initialize as "offline" -- is there currently a way for me to read stored events from the offline client and flush them to the LaunchDarkly servers?
In the code snippet below I am initializing the first client + redis store, then initializing a 2nd client in a background thread that connects to the same local redis instance. I can confirm that when I run this snippet I do not see events populate in the LaunchDarkly UI.
NOTE: this is POC to determine whether LaunchDarkly will work for my use case. It is not a Production-grade implementation.
public static void main(String[] args) throws IOException {
LDConfig config = new LDConfig.Builder().dataStore(Components
.persistentDataStore(
Redis.dataStore().uri(URI.create("redis://127.0.0.1:6379")).prefix("my-key-prefix"))
.cacheSeconds(30)).build();
LDClient ldClient = new LDClient("SDK-KEY", config);
Runnable r = new Runnable() {
#Override
public void run() {
LDConfig offlineConfig = new LDConfig.Builder().dataStore(Components
.persistentDataStore(
Redis.dataStore().uri(URI.create("redis://127.0.0.1:6379")).prefix("my-key-prefix"))
.cacheSeconds(30)).offline(true).build();
LDClient offlineClient = new LDClient("SDK-KEY", offlineConfig);
String uniqueId = "abcde";
LDUser user = new LDUser.Builder(uniqueId).custom("customField", "customValue").build();
boolean showFeature = offlineClient.boolVariation("test-feature-flag", user, false);
if (showFeature) {
System.out.println("Showing your feature");
} else {
System.out.println("Not showing your feature");
}
try {
offlineClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
};
ExecutorService executor = Executors.newCachedThreadPool();
executor.submit(r);
executor.shutdown();
ldClient.close();
}

Related

Junit and Spring 5 : All my classes should be multithreading tests expect one class, how can I achieve this? SSL handshake with concurrent

I'm working on a Spring 5 project and have some very special expectations with junit. Spring 5 now support junit multithreading and that definitely works very well, I'm now running my hundreds of tests into method parrallel multithreading. But I just setup recently my whole automatic mailing system which works like a charm but that's where it start to be problematic : I run a class that send all my mails to test them, and so they are being sent concurently. But as I just tried right now to test it with not only one email at a time but several, I get a strange SSL handshake error which I related to the fact that concurrent mail sending is not supported by most mail clients.
That's where goes my interrogation: how can I run all my test classes with parallel methods execution except for that email batch sending class?
Maybe I should think about a mail queue to avoid this kind of problem in live? Anyone has an idea?
By the way, in case you wonder I'm yet using gmail client to send mail as I didn't configured it yet for our live mail sending but it will be achieved using dedicated 1and1.fr smtp email client.
Thanks for your patience!
For those who feels interested about the solution, here is how I solved it:
I created a new Singleton class which would handle the queue :
public class EmailQueueHandler {
/** private Constructor */
private EmailQueueHandler() {}
/** Holder */
private static class EmailQueueHandlerHolder
{
/** unique instance non preinitialized */
private final static EmailQueueHandler INSTANCE = new EmailQueueHandler();
}
/** access point for unique instanciation of the singleton **/
public static EmailQueueHandler getInstance()
{
return EmailQueueHandlerHolder.INSTANCE;
}
private List<EmailPreparator> queue = new ArrayList<>();
public void queue(EmailPreparator email) {
waitForQueueHandlerToBeAvailable();
queue.add(email);
}
public List<EmailPreparator> getQueue()
{
waitForQueueHandlerToBeAvailable();
List<EmailPreparator> preparators = queue;
queue = new ArrayList<>();
return preparators;
}
// This method is used to make this handler thread safe
private synchronized void waitForQueueHandlerToBeAvailable(){}
}
I then created a CRON task using #Schedule annotation in my Scheduler bean in which I would correctly handle any mail sending fail.
#Scheduled(fixedRate = 30 * SECOND)
public void sendMailsInQueue()
{
List<EmailPreparator> queue = emailQueueHandler.getQueue();
int mailsSent = queue.size();
int mailsFailed = 0;
for(EmailPreparator preparator : queue)
{
try {
// And finally send the mail
emailSenderService.sendMail(preparator);
}
// If mail sending is not activated, mail sending function will throw an exception,
// Therefore we have to catch it and only throw it back if the email was really supposed to be sent
catch(Exception e)
{
mailsSent --;
// If we are not in test Env
if(!SpringConfiguration.isTestEnv())
{
mailsFailed ++;
preparator.getEmail().setTriggeredExceptionName(e.getMessage()).update();
// This will log the error into the database and eventually
// print it to the console if in LOCAL env
new Error()
.setTriggeredException(e)
.setErrorMessage(e.getClass().getName());
}
else if(SpringConfiguration.SEND_MAIL_ANYWAY_IN_TEST_ENV || preparator.isForceSend())
{
mailsFailed ++;
throw new EmailException(e);
}
}
}
log.info("CRON Task - " + mailsSent + " were successfuly sent ");
if(mailsFailed > 0)
log.warn("CRON Task - But " + mailsFailed + " could not be sent");
}
And then I called this mail queue emptyer methods at the end of each unit test in my #After annotated method to make sure it's called before I unit test the mail resulted. This way I'm aware of any mail sending fail even if it appear in PROD env and I'm also aware of any mail content creation failure when testing.
#After
public void downUp() throws Exception
{
proceedMailQueueManuallyIfNotAlreadySent();
logger.debug("After Test");
RequestHolder requestHolder = securityContextBuilder.getSecurityContextHolder().getRequestHolder();
// We check mails sending if some were sent
if(requestHolder.isExtResultsSent())
{
for(ExtResults results : requestHolder.getExtResults())
{
ExtSenderVerificationResolver resolver =
new ExtSenderVerificationResolver(
results,
notificationParserService
);
resolver.assertExtSending();
}
}
// Some code
}
protected void proceedMailQueueManuallyIfNotAlreadySent()
{
if(!mailQueueProceeded)
{
mailQueueProceeded = true;
scheduler.sendMailsInQueue();
}
}

Vertx http server Thread has been blocked for xxxx ms, time limit is 2000

i have written a large scale http server using , but im getting this error when number of concurrent requests increases
WARNING: Thread Thread[vert.x-eventloop-thread-1,5,main] has been blocked for 8458 ms, time limit is 1000
io.vertx.core.VertxException: Thread blocked
here is my full code :
public class MyVertxServer {
public Vertx vertx = Vertx.vertx(new VertxOptions().setWorkerPoolSize(100));
private HttpServer server = vertx.createHttpServer();
private Router router = Router.router(vertx);
public void bind(int port){
server.requestHandler(router::accept).listen(port);
}
public void createContext(String path,MyHttpHandler handler){
if(!path.endsWith("/")){
path += "/";
}
path+="*";
router.route(path).handler(new Handler<RoutingContext>() {
#Override
public void handle(RoutingContext ctx) {
String[] handlerID = ctx.request().uri().split(ctx.currentRoute().getPath());
String suffix = handlerID.length > 1 ? handlerID[1] : null;
handler.Handle(ctx, new VertxUtils(), suffix);
}
});
}
}
and how i call it :
ver.createContext("/getRegisterManager",new ProfilesManager.RegisterHandler());
ver.createContext("/getLoginManager", new ProfilesManager.LoginHandler());
ver.createContext("/getMapcomCreator",new ItemsManager.MapcomCreator());
ver.createContext("/getImagesManager", new ItemsManager.ImagesHandler());
ver.bind(PORT);
how ever i dont find eventbus() useful for http servers that process send/receive files , because u need to send the RoutingContext in the message with is not possible.
could you please point me to the right direction? thanks
added a little bit of handler's code:
class ProfileGetter implements MyHttpHandler{
#Override
public void Handle(RoutingContext ctx, VertxUtils utils, String suffix) {
String username = utils.Decode(ctx.request().headers().get("username"));
String lang = utils.Decode(ctx.request().headers().get("lang"));
display("profile requested : "+username);
Profile profile = ProfileManager.FindProfile(username,lang);
if(profile == null){
ctx.request().response().putHeader("available","false");
utils.sendResponseAndEnd(ctx.response(),400);
return;
}else{
ctx.request().response().putHeader("available","true");
utils.writeStringAndEnd(ctx, new Gson().toJson(profile));
}
}
}
here ProfileManager.FindProfile(username,lang) does a long running database job on the same thread
...
basically all of my processes are happening on the main thread , because if i use executor i will get strange exceptions and nullpointers in Vertx , making me feel like the request proccessors in Vertx are parallel
Given the small amount of code in the question lets agree that the problem is on the line:
Profile profile = ProfileManager.FindProfile(username,lang);
Assuming that this is internally doing some blocking JDBC call which is a anti-pattern in Vert.x you can solve this in several ways.
Say that you can totally refactor the ProfileManager class which IMO is the best then you can update it to be reactive, so your code would be like:
ProfileManager.FindProfile(username,lang, res -> {
if (res.failed()) {
// handle error, sent 500 back, etc...
} else {
Profile profile = res.result();
if(profile == null){
ctx.request().response().putHeader("available","false");
utils.sendResponseAndEnd(ctx.response(),400);
return;
}else{
ctx.request().response().putHeader("available","true");
utils.writeStringAndEnd(ctx, new Gson().toJson(profile));
}
}
});
Now what would be hapening behind the scenes is that your JDBC call would not block (which is tricky because JDBC is blocking by nature). So to fix this and you're lucky enough to use MySQL or Postgres then you could code your JDBC against the async-client if you're stuck with other RDBMS servers then you need to use the jdbc-client which in turn will use a thread pool to offload the work from the event loop thread.
Now say that you cannot change the ProfileManager code then you can still off load it to the thread pool by wrapping the code in a executeBlocking block:
vertx.executeBlocking(future -> {
Profile profile = ProfileManager.FindProfile(username,lang);
future.complete(profile);
}, false, res -> {
if (res.failed()) {
// handle error, sent 500 back, etc...
} else {
Profile profile = res.result();
if(profile == null){
ctx.request().response().putHeader("available","false");
utils.sendResponseAndEnd(ctx.response(),400);
return;
}else{
ctx.request().response().putHeader("available","true");
utils.writeStringAndEnd(ctx, new Gson().toJson(profile));
}
}
});

cassandra single node connection error

i am trying to use cassandra as database for an app i am working on. The app is a Netbeans platform app.
In order to start the cassandra server on my localhost i issue Runtime.getRuntime().exec(command)
where command is the string to start the cassandra server and then i connect to the cassandra sever with the datastax driver. However i get the error:
com.datastax.driver.core.exceptions.NoHostAvailableException: All host(s) tried for query failed (tried: /127.0.0.1:9042 (com.datastax.driver.core.TransportException: [/127.0.0.1:9042] Cannot connect))
at com.datastax.driver.core.ControlConnection.reconnectInternal(ControlConnection.java:199)
at com.datastax.driver.core.ControlConnection.connect(ControlConnection.java:80)
at com.datastax.driver.core.Cluster$Manager.init(Cluster.java:1154)
at com.datastax.driver.core.Cluster.getMetadata(Cluster.java:318)
at org.dhviz.boot.DatabaseClient.connect(DatabaseClient.java:43)
at org.dhviz.boot.Installer.restored(Installer.java:67)
....
i figure it out that the server requires some time to start so i have added the line Thread.sleep(MAX_DELAY_SERVER) which seem to resolve the problem.
Is there any more elegant way to sort this issue?
Thanks.
Code is below.
public class Installer extends ModuleInstall {
private final int MAX_DELAY_SERVER = 12000;
//private static final String pathSrc = "/org/dhviz/resources";
#Override
public void restored() {
/*
-*-*-*-*-*DESCRIPTION*-*-*-*-*-*
IMPLEMENT THE CASSANDRA DATABASE
*********************************
*/
DatabaseClient d = new DatabaseClient();
// launch an instance of the cassandra server
d.loadDatabaseServer();
/*wait for MAX_DELAY_SERVER milliseconds before launching the other instructions.
*/
try {
Thread.sleep(MAX_DELAY_SERVER);
Logger.getLogger(Installer.class.getName()).log(Level.INFO, "wait for MAX_DELAY_SERVER milliseconds before the connect database");
} catch (InterruptedException ex) {
Exceptions.printStackTrace(ex);
Logger.getLogger(Installer.class.getName()).log(Level.INFO, "exeption in thread sleep");
}
d.connect("127.0.0.1");
}
}
public class DatabaseClient {
private Cluster cluster;
private Session session;
private ShellCommand shellCommand;
private final String defaultKeyspace = "dhviz";
final private String LOAD_CASSANDRA = "launchctl load /usr/local/Cellar/cassandra/2.1.2/homebrew.mxcl.cassandra.plist";
final private String UNLOAD_CASSANDRA = "launchctl unload /usr/local/Cellar/cassandra/2.1.2/homebrew.mxcl.cassandra.plist";
public DatabaseClient() {
shellCommand = new ShellCommand();
}
public void connect(String node) {
//this connect to the cassandra database
cluster = Cluster.builder()
.addContactPoint(node).build();
// cluster.getConfiguration().getSocketOptions().setConnectTimeoutMillis(12000);
Metadata metadata = cluster.getMetadata();
System.out.printf("Connected to cluster: %s\n",
metadata.getClusterName());
for (Host host
: metadata.getAllHosts()) {
System.out.printf("Datatacenter: %s; Host: %s; Rack: %s\n",
host.getDatacenter(), host.getAddress(), host.getRack());
}
session = cluster.connect();
Logger.getLogger(DatabaseClient.class.getName()).log(Level.INFO, "connected to server");
}
public void loadDatabaseServer() {
if (shellCommand == null) {
shellCommand = new ShellCommand();
}
shellCommand.executeCommand(LOAD_CASSANDRA);
Logger.getLogger(DatabaseClient.class.getName()).log(Level.INFO, "database cassandra loaded");
}
public void unloadDatabaseServer() {
if (shellCommand == null) {
shellCommand = new ShellCommand();
}
shellCommand.executeCommand(UNLOAD_CASSANDRA);
Logger.getLogger(DatabaseClient.class.getName()).log(Level.INFO, "database cassandra unloaded");
}
}
If you are calling cassandra without any parameters in Runtime.getRuntime().exec(command) it's likely that this is spawning cassandra as a background process and returning before the cassandra node has fully started and is listening.
I'm not sure why you are attempting to embed cassandra in your app, but you may find using cassandra-unit useful for providing a mechanism to embed cassandra in your app. It's primarily used for running tests that require a cassandra instance, but it may also meet your use case.
The wiki provides a helpful example on how to start an embedded cassandra instance using cassandra-unit:
EmbeddedCassandraServerHelper.startEmbeddedCassandra();
In my experience cassandra-unit will wait until the server is up and listening before returning. You could also write a method that waits until a socket is in use, using logic opposite of this answer.
I have changed the code to the following taking inspiration from the answers below. Thanks for your help!
cluster = Cluster.builder()
.addContactPoint(node).build();
cluster.getConfiguration().getSocketOptions().setConnectTimeoutMillis(50000);
boolean serverConnected = false;
while (serverConnected == false) {
try {
try {
Thread.sleep(MAX_DELAY_SERVER);
} catch (InterruptedException ex) {
Exceptions.printStackTrace(ex);
}
cluster = Cluster.builder()
.addContactPoint(node).build();
cluster.getConfiguration().getSocketOptions().setConnectTimeoutMillis(50000);
session = cluster.connect();
serverConnected = true;
} catch (NoHostAvailableException ex) {
Logger.getLogger(DatabaseClient.class.getName()).log(Level.INFO, "trying connection to cassandra server...");
serverConnected = false;
}
}

Lock wait timeout exceeded with Hibernate and MySQL (using play framework)

In my web application I'm using Stateless sessions with Hibernate to have better performances on my inserts and updates.
It was working fine with H2 database (the one used in play framework in dev mode).
But when I test it with MySQL I get the following exception :
ERROR ~ Lock wait timeout exceeded; try restarting transaction
ERROR ~ HHH000315: Exception executing batch [Lock wait timeout exceeded; try restarting transaction]
Here is the code :
public static void update() {
Session session = (Session) JPA.em().getDelegate();
StatelessSession stateless = this.session.getSessionFactory().openStatelessSession();
try {
stateless.beginTransaction();
// Fetch all products
{
List<ProductType> list = ProductType.retrieveAllWithHistory();
for (ProductType pt : list) {
updatePrice(pt, stateless);
}
}
// Fetch all raw materials
{
List<RawMaterialType> list = RawMaterialType.retrieveAllWithHistory();
for (RawMaterialType rm : list) {
updatePrice(rm, stateless);
}
}
} catch (Exception ex) {
play.Logger.error(ex.getMessage());
ExceptionLog.log(ex, Thread.currentThread());
} finally {
stateless.getTransaction().commit();
stateless.close();
}
}
private static void updatePrice(ProductType pt, StatelessSession stateless) {
pt.priceDelta = computeDelta();
pt.unitPrice = computePrice();
stateless.update(pt);
PriceHistory ph = new PriceHistory(pt, price);
stateless.insert(ph);
}
private static void updatePrice(RawMaterialType rm, StatelessSession stateless) {
rm.priceDelta = computeDelta();
rm.unitPrice = computePrice();
stateless.update(rm);
PriceHistory ph = new GoodPriceHistory(rm, price);
stateless.insert(ph);
}
In this example I have 3 simple Entities (ProductType, RawMaterialType and PriceHistory).
computeDelta and computePrice are just algorithm functions with no DB stuff.
retrieveAllWithHistory functions are functions that fetch some data from the database using Play framework model functions.
So, this code retrieves some data, edit some, create new one and finally save everything.
Why have I a lock exception with MySQL and no exception with H2 ?
I'm not sure why you have a commit in a finally block. Give this structure a try:
try {
factory.getCurrentSession().beginTransaction();
factory.getCurrentSession().getTransaction().commit();
} catch (RuntimeException e) {
factory.getCurrentSession().getTransaction().rollback();
throw e; // or display error message
}
Also, it might be helpful for you to check this documentation.

How do I discover peers and send messages in JXTA-JXSE 2.6?

Using JXTA 2.6 from http://jxse.kenai.com/ I want to create application that can run multiple peers on one or more hosts. The peers should be able to find each other in a group and send direct messages as well as propagate messages.
What would a simple hello world type of application look like that meet these requirements?
I created this question with the intention of supplying a tutorial like answer, an answer I tried very hard to find two months ago when starting to look at JXTA for a uni project. Feel free to add your own answers or improve on mine. I will wait a few days and accept the best one.
Introduction to JXTA 2.6 Peer discovery and pipe messaging
The guide I wish I had 2 months ago =)
After spending a lot of time during a university course building
a JXTA p2p application I feel a lot of the frustrations and
confusion I went through could have been avoided with a good
starting point.
The jar files you will need can be found here:
https://oss.sonatype.org/content/repositories/comkenaijxse-057/com/kenai/jxse/jxse/2.6/jxse-2.6.jar
http://sourceforge.net/projects/practicaljxta/files/lib-dependencies-2.6.zip/download
Throw them into Yourproject/lib, open up eclipse, create a new project "Yourproject" and it should sort out
importing the libraries for you.
You will soon come to realize that almost any information on the web is out dated, very out dated.
You will also run into a lot of very confusing error messages and most of them can be avoided by
going through this check list.
Is your firewall turned off or at least open for the ports you use?
You can disable iptables using "sudo service iptables stop" under Fedora.
Check spelling! Many times when joining groups or trying to send messages spelling the group name wrong or not using the
exact same advertisement when looking for peers and services or opening pipes will cause very confusing messages.
Was trying to figure out why my pipe connections timed out when I spotted the group names being "Net info" and "Net_info".
Are you using a JXTA home directory? One per each peer you run on the same computer?
Do you really use a unique peer id? The seed provided to IDFactory need to be long enough or else you will get duplicates.
Turn off SELinux. I have had SELinux turned off during the development but can imagine it causing errors.
While it is common to group all fields together I introduce them as I go to show where they are needed.
Note: This will not work in 2.7. Some issue with PSE membership I think.
public class Hello implements DiscoveryListener, PipeMsgListener {
// When developing you should handle these exceptions, I don't to lessen the clutter of start()
public static void main(String[] args) throws PeerGroupException, IOException {
// JXTA logs a lot, you can configure it setting level here
Logger.getLogger("net.jxta").setLevel(Level.ALL);
// Randomize a port to use with a number over 1000 (for non root on unix)
// JXTA uses TCP for incoming connections which will conflict if more than
// one Hello runs at the same time on one computer.
int port = 9000 + new Random().nextInt(100);
Hello hello = new Hello(port);
hello.start();
hello.fetch_advertisements();
}
private String peer_name;
private PeerID peer_id;
private File conf;
private NetworkManager manager;
public Hello(int port) {
// Add a random number to make it easier to identify by name, will also make sure the ID is unique
peer_name = "Peer " + new Random().nextInt(1000000);
// This is what you will be looking for in Wireshark instead of an IP, hint: filter by "jxta"
peer_id = IDFactory.newPeerID(PeerGroupID.defaultNetPeerGroupID, peer_name.getBytes());
// Here the local peer cache will be saved, if you have multiple peers this must be unique
conf = new File("." + System.getProperty("file.separator") + peer_name);
// Most documentation you will find use a deprecated network manager setup, use this one instead
// ADHOC is usually a good starting point, other alternatives include Edge and Rendezvous
try {
manager = new NetworkManager(
NetworkManager.ConfigMode.ADHOC,
peer_name, conf.toURI());
}
catch (IOException e) {
// Will be thrown if you specify an invalid directory in conf
e.printStackTrace();
}
NetworkConfigurator configurator;
try {
// Settings Configuration
configurator = manager.getConfigurator();
configurator.setTcpPort(port);
configurator.setTcpEnabled(true);
configurator.setTcpIncoming(true);
configurator.setTcpOutgoing(true);
configurator.setUseMulticast(true);
configurator.setPeerID(peer_id);
}
catch (IOException e) {
// Never caught this one but let me know if you do =)
e.printStackTrace();
}
}
private static final String subgroup_name = "Make sure this is spelled the same everywhere";
private static final String subgroup_desc = "...";
private static final PeerGroupID subgroup_id = IDFactory.newPeerGroupID(PeerGroupID.defaultNetPeerGroupID, subgroup_name.getBytes());
private static final String unicast_name = "This must be spelled the same too";
private static final String multicast_name = "Or else you will get the wrong PipeID";
private static final String service_name = "And dont forget it like i did a million times";
private PeerGroup subgroup;
private PipeService pipe_service;
private PipeID unicast_id;
private PipeID multicast_id;
private PipeID service_id;
private DiscoveryService discovery;
private ModuleSpecAdvertisement mdadv;
public void start() throws PeerGroupException, IOException {
// Launch the missiles, if you have logging on and see no exceptions
// after this is ran, then you probably have at least the jars setup correctly.
PeerGroup net_group = manager.startNetwork();
// Connect to our subgroup (all groups are subgroups of Netgroup)
// If the group does not exist, it will be automatically created
// Note this is suggested deprecated, not sure what the better way is
ModuleImplAdvertisement mAdv = null;
try {
mAdv = net_group.getAllPurposePeerGroupImplAdvertisement();
} catch (Exception ex) {
System.err.println(ex.toString());
}
subgroup = net_group.newGroup(subgroup_id, mAdv, subgroup_name, subgroup_desc);
// A simple check to see if connecting to the group worked
if (Module.START_OK != subgroup.startApp(new String[0]))
System.err.println("Cannot start child peergroup");
// We will spice things up to a more interesting level by sending unicast and multicast messages
// In order to be able to do that we will create to listeners that will listen for
// unicast and multicast advertisements respectively. All messages will be handled by Hello in the
// pipeMsgEvent method.
unicast_id = IDFactory.newPipeID(subgroup.getPeerGroupID(), unicast_name.getBytes());
multicast_id = IDFactory.newPipeID(subgroup.getPeerGroupID(), multicast_name.getBytes());
pipe_service = subgroup.getPipeService();
pipe_service.createInputPipe(get_advertisement(unicast_id, false), this);
pipe_service.createInputPipe(get_advertisement(multicast_id, true), this);
// In order to for other peers to find this one (and say hello) we will
// advertise a Hello Service.
discovery = subgroup.getDiscoveryService();
discovery.addDiscoveryListener(this);
ModuleClassAdvertisement mcadv = (ModuleClassAdvertisement)
AdvertisementFactory.newAdvertisement(ModuleClassAdvertisement.getAdvertisementType());
mcadv.setName("STACK-OVERFLOW:HELLO");
mcadv.setDescription("Tutorial example to use JXTA module advertisement Framework");
ModuleClassID mcID = IDFactory.newModuleClassID();
mcadv.setModuleClassID(mcID);
// Let the group know of this service "module" / collection
discovery.publish(mcadv);
discovery.remotePublish(mcadv);
mdadv = (ModuleSpecAdvertisement)
AdvertisementFactory.newAdvertisement(ModuleSpecAdvertisement.getAdvertisementType());
mdadv.setName("STACK-OVERFLOW:HELLO");
mdadv.setVersion("Version 1.0");
mdadv.setCreator("sun.com");
mdadv.setModuleSpecID(IDFactory.newModuleSpecID(mcID));
mdadv.setSpecURI("http://www.jxta.org/Ex1");
service_id = IDFactory.newPipeID(subgroup.getPeerGroupID(), service_name.getBytes());
PipeAdvertisement pipeadv = get_advertisement(service_id, false);
mdadv.setPipeAdvertisement(pipeadv);
// Let the group know of the service
discovery.publish(mdadv);
discovery.remotePublish(mdadv);
// Start listening for discovery events, received by the discoveryEvent method
pipe_service.createInputPipe(pipeadv, this);
}
private static PipeAdvertisement get_advertisement(PipeID id, boolean is_multicast) {
PipeAdvertisement adv = (PipeAdvertisement )AdvertisementFactory.
newAdvertisement(PipeAdvertisement.getAdvertisementType());
adv.setPipeID(id);
if (is_multicast)
adv.setType(PipeService.PropagateType);
else
adv.setType(PipeService.UnicastType);
adv.setName("This however");
adv.setDescription("does not really matter");
return adv;
}
#Override public void discoveryEvent(DiscoveryEvent event) {
// Found another peer! Let's say hello shall we!
// Reformatting to create a real peer id string
String found_peer_id = "urn:jxta:" + event.getSource().toString().substring(7);
send_to_peer("Hello", found_peer_id);
}
private void send_to_peer(String message, String found_peer_id) {
// This is where having the same ID is important or else we wont be
// able to open a pipe and send messages
PipeAdvertisement adv = get_advertisement(unicast_id, false);
// Send message to all peers in "ps", just one in our case
Set<PeerID> ps = new HashSet<PeerID>();
try {
ps.add((PeerID)IDFactory.fromURI(new URI(found_peer_id)));
}
catch (URISyntaxException e) {
// The JXTA peer ids need to be formatted as proper urns
e.printStackTrace();
}
// A pipe we can use to send messages with
OutputPipe sender = null;
try {
sender = pipe_service.createOutputPipe(adv, ps, 10000);
}
catch (IOException e) {
// Thrown if there was an error opening the connection, check firewall settings
e.printStackTrace();
}
Message msg = new Message();
MessageElement fromElem = null;
MessageElement msgElem = null;
try {
fromElem = new ByteArrayMessageElement("From", null, peer_id.toString().getBytes("ISO-8859-1"), null);
msgElem = new ByteArrayMessageElement("Msg", null, message.getBytes("ISO-8859-1"), null);
} catch (UnsupportedEncodingException e) {
// Yepp, you want to spell ISO-8859-1 correctly
e.printStackTrace();
}
msg.addMessageElement(fromElem);
msg.addMessageElement(msgElem);
try {
sender.send(msg);
} catch (IOException e) {
// Check, firewall, settings.
e.printStackTrace();
}
}
#Override public void pipeMsgEvent(PipeMsgEvent event) {
// Someone is sending us a message!
try {
Message msg = event.getMessage();
byte[] msgBytes = msg.getMessageElement("Msg").getBytes(true);
byte[] fromBytes = msg.getMessageElement("From").getBytes(true);
String from = new String(fromBytes);
String message = new String(msgBytes);
System.out.println(message + " says " + from);
}
catch (Exception e) {
// You will notice that JXTA is not very specific with exceptions...
e.printStackTrace();
}
}
/**
* We will not find anyone if we are not regularly looking
*/
private void fetch_advertisements() {
new Thread("fetch advertisements thread") {
public void run() {
while(true) {
discovery.getRemoteAdvertisements(null, DiscoveryService.ADV, "Name", "STACK-OVERFLOW:HELLO", 1, null);
try {
sleep(10000);
}
catch(InterruptedException e) {}
}
}
}.start();
}
}

Categories

Resources