Use Spring Cloud Spring Service Connector with RabbitMQ and start publisher config function - spring-rabbit

I connect RabbitMQ with sprin cloud config:
#Bean
public ConnectionFactory rabbitConnectionFactory() {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("publisherConfirms", true);
RabbitConnectionFactoryConfig rabbitConfig = new RabbitConnectionFactoryConfig(properties);
return connectionFactory().rabbitConnectionFactory(rabbitConfig);
}
2.Set rabbitTemplate.setMandatory(true) and setConfirmCallback():
#Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(connectionFactory);
template.setMandatory(true);
template.setMessageConverter(new Jackson2JsonMessageConverter());
template.setConfirmCallback((correlationData, ack, cause) -> {
if (!ack) {
System.out.println("send message failed: " + cause + correlationData.toString());
} else {
System.out.println("Publisher Confirm" + correlationData.toString());
}
});
return template;
}
3.Send message to queue to invoke the publisherConfirm and print log.
#Component
public class TestSender {
#Autowired
private RabbitTemplate rabbitTemplate;
#Scheduled(cron = "0/5 * * * * ? ")
public void send() {
this.rabbitTemplate.convertAndSend(EXCHANGE, "routingkey", "hello world",
(Message m) -> {
m.getMessageProperties().setHeader("tenant", "aaaaa");
return m;
}, new CorrelationData(UUID.randomUUID().toString()));
Date date = new Date();
System.out.println("Sender Msg Successfully - " + date);
}
}
But publisherConfirm have not worked.The log have not been printed. Howerver true or false, log shouldn't been absent.

Mandatory is not needed for confirms, only returns.
Some things to try:
Turn on DEBUG logging to see it it helps; there are some logs generated regarding confirms.
Add some code
.
template.execute(channel -> {
system.out.println(channel.getClass());
return null;
}
If you don't see PublisherCallbackChannelImpl then it means the configuration didn't work for some reason. Again DEBUG logging should help with the configuration debugging.
If you still can't figure it out, strip your application to the bare minimum that exhibits the behavior and post the complete application.

Related

WebSocket Slow - Java and JavaScript

I am dealing with encoding of a minecraft plugin.
But now I have the following problem, my websocket server respond very very slow.
Here is my WebSocketClass (For the plugin)
// socket server class
package me.mickerd.pcoc;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.Collection;
import org.bukkit.Bukkit;
import org.java_websocket.WebSocket;
import org.java_websocket.WebSocketImpl;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;
public class WebsocketServer extends WebSocketServer {
public static WebsocketServer s;
public WebsocketServer(int port) throws UnknownHostException {
super(new InetSocketAddress(port));
}
public WebsocketServer(InetSocketAddress address) {
super(address);
}
#Override
public void onOpen(WebSocket conn, ClientHandshake handshake) {
WebsocketSessionManager.getSessionManager().openSession(conn.getRemoteSocketAddress().getAddress().getHostAddress());
Bukkit.getLogger().info(conn.getRemoteSocketAddress().getAddress().getHostName() + " has connected to the Websocket server!");
}
#Override
public void onClose(WebSocket conn, int code, String reason, boolean remote) {
WebsocketSessionManager.getSessionManager().endSession(conn.getRemoteSocketAddress().getAddress().getHostAddress());
Bukkit.getLogger().info(conn + " has disconnected form the Websocket server");
}
#Override
public void onMessage(WebSocket conn, String message) {
Bukkit.getLogger().info("Recieve Websocket packet - " + conn + ":" + message);
if (message.split(":")[0].equalsIgnoreCase("name")) {
WebsocketSessionManager.getSessionManager().addSessionUsername(conn.getRemoteSocketAddress().getAddress().getHostAddress(), message.split(":")[1]);
}
}
public static void runServer() throws InterruptedException, IOException {
WebSocketImpl.DEBUG = true;
int port = 8887;
s = new WebsocketServer(port);
s.start();
Bukkit.getLogger().info("Websocket server started on port: " + s.getPort());
}
#Override
public void onError(WebSocket conn, Exception ex) {
ex.printStackTrace();
if (conn != null) {
// some errors like port binding failed may not be assignable to a specific websocket
}
}
public void sendToAll(String data) {
Collection<WebSocket> con = connections();
synchronized (con) {
for (WebSocket c : con) {
c.send(data);
}
}
}
public void sendData(WebsocketSession session, String data) {
Collection<WebSocket> con = connections();
synchronized (con) {
for (WebSocket c : con) {
if (c.getRemoteSocketAddress().getAddress().getHostAddress().equalsIgnoreCase(session.getHost())) {
Bukkit.getLogger().info("Send data packet: " + data);
c.send(data);
}
}
}
}
}
and this is my receiver in Javascript:
var sound = null;
var name = window.location
document.session.name.value = name
var text = document.session.name.value
var ws = new WebSocket("ws://" + window.location.hostname + ":8887/");
ws.onopen = function () {
ws.send("name:" + delineate(text));
document.getElementById("title").innerHTML = "Welcome on the music server. Please hold this window open!";
};
ws.onmessage = function (evt) {
function loadScript(url, callback)
{
// Adding the script tag to the head as suggested before
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
// Then bind the event to the callback function.
// There are several events for cross browser compatibility.
script.onreadystatechange = callback;
script.onload = callback;
// Fire the loading
head.appendChild(script);
}
if(evt.data == "stop"){
sound.fadeOut(0, 3700);
} else {
sound = new Howl({
urls: ['music/' + evt.data + '.ogg']
}).play();
console.log("playing music");
};
}
ws.onclose = function () {
alert("Closed!");
};
ws.onerror = function (err) {
alert("Error: " + err);
};
function delineate(str) {
theleft = str.indexOf("=") + 1;
theright = str.lastIndexOf("&");
return (str.substring(theleft, theright));
}
The reacts very slow, but other things on the server are incredible fast!
Can someone help?
The websocket library you are using sends data blocking, this means that a call to c.send( will block until the frame is send.
There are different ways to solve this, for example:
Using a separate async thread for every message:
public void sendToAll(String data) {
// Ferrybig - added bukkit async task
Bukkit.getSchedular().runTaskAsynchronously(plugin, new Runnable(){
#Override public void run(){
Collection<WebSocket> con = connections();
synchronized (con) {
for (WebSocket c : con) {
c.send(data);
}
}
// Ferrybig - added bukkit async task
}});
}
While this solves you problem quickly, it isn't a clean solution since a large amount of send messages means that there are a large number of threads created for the purpose of message sending, ie don't send to often, or see next solution:
Using a dedicated thread for the message sending:
Using a dedicated thread for the sending of messages is the better solution, but it comes with its large code.
For this solution, we need to do the following:
Use a variable to store the messages that need to be send to every client
private final BlockingQueue<Pair<WebSocket,String>> messageQueue
= new LinkedBlockingDeque<>();
We use a Blocking queue that holds Pair objects containing the web socket and the message to be send. While we also could used the Map.Entry classes from Map, I choose to use a Pair since we can later change the code a little to make it automatically resort message based on priority. The Pair class I used for this answer can be found at What is the equivalent of the C++ Pair in Java?.
Using a dedicated thread to send the messages
We have list of incoming messages now, we now to process the messages as they come in. This can be done by making a task that blocks on messageQueue.take(). The following is a quick implementation of this:
public class MessageProcessor extends BukkitRunnable {
private BlockingQueue<Pair<WebSocket,String>> messageQueue;
public MessageProcessor (BlockingQueue<Pair<WebSocket,String>> messageQueue) {
this.messageQueue = messageQueue;
}
#Override
public void run() {
try {
Pair<WebSocket,String> next;
while(true) {
next = messageQueue.take();
if(next.getFirst() == null) {
// Special condition, will be explained later
return; // Quit run method
}
// System.out.println("Send message to " + next.getFirst()); // DEBUG
next.getFirst().send(next.getSecond());
}
} catch(InterruptedException e) {
Thread.currentThread().interrupt();
// Someone wanted to quit our thread, so we are quiting
} finally {
messageQueue.clear();
}
}
}
The above class has 2 special conditions, next.getFirst() == null and catch(InterruptedException e), these will be used when we disable the plugin to quit the task.
Start our dedicated task when bukkit is started
We need to start our task when bukkit and our Websocket server is started, so it can start processing messages and sending data. This is easy to do in our onEnable() with the following code:
new MessageProcessor (messageQueue).runTaskAsynchronously(this);
Stopping the dedicated task
We need to make sure the dedicated task is stopped when our plugin is disabled to prevent bukkit from spamming the error "This plugin is not properly shutting down its async tasks when it is being reloaded.". This is really easy to do since we have made a special condition for this above.
To do this, we place the following code in our onDisable():
messageQueue.add(new Pair<>(null,null));
Rewriting our methods to use the messageQueue
Our last step in the process is to rewrite the sendToAll method to use our queue. This is really easy to do any only requires us to replace 1 line.
public void sendToAll(String data) {
Collection<WebSocket> con = connections();
synchronized (con) {
for (WebSocket c : con) {
messageQueue.add(new Pair<>(c,data)); // Ferrybig: Use messageQueue
}
}
}
The same small modification can also be done for sendData method, but isn't done by me as an exercise for the reader.
Sidenotes
A BlockingQueue is designed with concurrent actions in mind, and doesn't require external synchronization.
You may choose to use BlockingQueue.offer() instead of BlockingQueue.add() because the fact the latter throws an exception when the list is full, but the first returns false.
The default size for a LinkedBlockingDeque is Integer.MAX_VALUE and can be changed with its constructor.

Cucumber Guice / Injector seems not to be thread-safe (Parallel execution / ExecutorService)

[long description warning]
I'm running some cucumber tests which have to be executed intercalated a defined server - for instance:
a.feature -> JBoss Server 1 | b.feature -> JBoss Serv. 2 | c.feature -> JB1 | etc.
For that, I created a hypothetical ExecutorService like this:
final ExecutorService executorService = Executors.newFixedThreadPool(2); //numberOfServers
for (Runnable task : tasks) {
executorService.execute(task);
}
executorService.shutdown();
try {
executorService.awaitTermination(1000, TimeUnit.SECONDS);
} catch (InterruptedException e) {
//doX();
}
The way that I manage about how will be the server chosen as liable to execute is:
inside of my Runnable class created for the executorService, I pass as a parameter a instanceId to a TestNG (XmlTest class) as below:
#Override
public void run() {
setupTest().run();
}
private TestNG setupTest() {
TestNG testNG = new TestNG();
XmlSuite xmlSuite = new XmlSuite();
XmlTest xmlTest = new XmlTest(xmlSuite);
xmlTest.setName(//irrelevant);
xmlTest.addParameter("instanceId", String.valueOf(instanceId));
xmlTest.setXmlClasses(..........);
testNG.setXmlSuites(..........);
return testNG;
}
Then, I get this just fine in a class that extends TestNgCucumberAdaptor:
#BeforeTest
#Parameters({"instanceId"})
public void setInstanceId(#Optional("") String instanceId) {
if (!StringUtils.isEmpty(instanceId)) {
this.instanceId = Integer.valueOf(instanceId);
}
}
And inside a #BeforeClass I'm populating a Pojo with this instanceId and setting the Pojo in a threadLocal attribute of another class. So far, so good.
public class CurrentPojoContext {
private static final ThreadLocal<PojoContext> TEST_CONTEXT = new ThreadLocal<PojoContext>();
...
public static PojoContext getContext(){
TEST_CONTEXT.get();
}
Now the problem really starts - I'm using Guice (Cucumber guice as well) in a 3rd class, injecting this pojo object that contains the instanceId. The example follows:
public class Environment {
protected final PojoContext pojoContext;
#Inject
public Environment() {
this.pojoContext = CurrentPojoContext.getContext();
}
public void foo() {
print(pojoContext.instanceId); // output: 1
Another.doSomething(pojoContext);
}
class Another{
public String doSomething(PojoContext p){
print(p.instanceId); // output: 2
}
}
}
Here it is not every time like this the outputs (1 and 2) but from time to time, I realized that the execution of different threads is messing with the attribute pojoContext. I know that is a little confusing, but my guess is that the Guice Injector is not thread-safe for this scenario - it might be a long shot, but I'd appreciate if someone else takes a guess.
Regards
Well, just in order to provide a solution for someone else, my solution was the following:
Create a class that maintains a Map with an identifier (unique and thread-safe one) as the key and a Guice Injector as value;
Inside my instantiation of Guice injector, I created my own module
Guice.createInjector(Stage.PRODUCTION, MyOwnModules.SCENARIO, new RandomModule());
and for this module:
public class MyOwnModules {
public static final Module SCENARIO = new ScenarioModule(MyOwnCucumberScopes.SCENARIO);
}
the scope defined here provides the following:
public class MyOwnCucumberScopes {
public static final ScenarioScope SCENARIO = new ParallelScenarioScope();
}
To sum up, the thread-safe will be in the ParallelScenarioScope:
public class ParallelScenarioScope implements ScenarioScope {
private static final Logger LOGGER = Logger.getLogger(ParallelScenarioScope.class);
private final ThreadLocal<Map<Key<?>, Object>> threadLocalMap = new ThreadLocal<Map<Key<?>, Object>>();
#Override
public <T> Provider<T> scope(final Key<T> key, final Provider<T> unscoped) {
return new Provider<T>() {
public T get() {
Map<Key<?>, Object> scopedObjects = getScopedObjectMap(key);
#SuppressWarnings("unchecked")
T current = (T) scopedObjects.get(key);
if (current == null && !scopedObjects.containsKey(key)) {
current = unscoped.get();
scopedObjects.put(key, current);
}
return current;
}
};
}
protected <T> Map<Key<?>, Object> getScopedObjectMap(Key<T> key) {
Map<Key<?>, Object> map = threadLocalMap.get();
if (map == null) {
throw new OutOfScopeException("Cannot access " + key + " outside of a scoping block");
}
return map;
}
#Override
public void enterScope() {
checkState(threadLocalMap.get() == null, "A scoping block is already in progress");
threadLocalMap.set(new ConcurrentHashMap<Key<?>, Object>());
}
#Override
public void exitScope() {
checkState(threadLocalMap.get() != null, "No scoping block in progress");
threadLocalMap.remove();
}
private void checkState(boolean expression, String errorMessage) {
if (!expression) {
LOGGER.info("M=checkState, Will throw exception: " + errorMessage);
throw new IllegalStateException(errorMessage);
}
}
}
Now the gotcha is just to be careful regarding the #ScenarioScoped and the code will work as expected.

Get property of person Alfresco in JAVA

I'm using Alfresco 5.1 Community, and i'm trying to get a property value of a current person logged for example, in the user I have:
"{http://www.someco.org/model/people/1.0}customProperty"
How can I obtain this in java?
Is a custom property, so, in http://localhost:8080/alfresco/service/api/people it does not appear. How can I do this?
I try this to obtain at least nodeRef:
protected ServiceRegistry getServiceRegistry() {
ProcessEngineConfigurationImpl config = Context.getProcessEngineConfiguration();
if (config != null) {
// Fetch the registry that is injected in the activiti spring-configuration
ServiceRegistry registry = (ServiceRegistry) config.getBeans().get(ActivitiConstants.SERVICE_REGISTRY_BEAN_KEY);
if (registry == null) {
throw new RuntimeException("Service-registry not present in ProcessEngineConfiguration beans, expected ServiceRegistry with key" + ActivitiConstants.SERVICE_REGISTRY_BEAN_KEY);
}
return registry;
}
throw new IllegalStateException("No ProcessEngineConfiguration found in active context");
}
public void writeToCatalina() {
PersonService personService = getServiceRegistry().getPersonService();
System.out.println("test");
String name = AuthenticationUtil.getFullyAuthenticatedUser();
System.out.println(name);
NodeRef personRef = personService.getPerson(name);
System.out.println(personRef);
}
But I got:
No ProcessEngineConfiguration found in active context
Help me !
You can query Alfresco using CMIS and call the API:
GET /alfresco/service/api/people/{userName}.
For first you can define the method to create the session CmisSession:
public Session getCmisSession() {
logger.debug("Starting: getCmisSession()");
// default factory implementation
SessionFactory factory = SessionFactoryImpl.newInstance();
Map<String, String> parameter = new HashMap<String, String>();
// connection settings
parameter.put(SessionParameter.ATOMPUB_URL, url + ATOMPUB_URL);
parameter.put(SessionParameter.BINDING_TYPE, BindingType.ATOMPUB.value());
parameter.put(SessionParameter.AUTH_HTTP_BASIC, "true");
parameter.put(SessionParameter.USER, username);
parameter.put(SessionParameter.PASSWORD, password);
parameter.put(SessionParameter.OBJECT_FACTORY_CLASS, "org.alfresco.cmis.client.impl.AlfrescoObjectFactoryImpl");
List<Repository> repositories = factory.getRepositories(parameter);
return repositories.get(0).createSession();
}
Then execute the query (this method returns more than one result, you probably need to change it):
public void doQuery(String cql, int maxItems) {
Session cmisSession = getCmisSession();
OperationContext oc = new OperationContextImpl();
oc.setMaxItemsPerPage(maxItems);
ItemIterable<QueryResult> results = cmisSession.query(cql, false, oc);
for (QueryResult result : results) {
for (PropertyData<?> prop : result.getProperties()) {
logger.debug(prop.getQueryName() + ": " + prop.getFirstValue());
}
}
}
If you need to get the token, use this:
public String getAuthenticationTicket() {
try {
logger.info("ALFRESCO: Starting connection...");
RestTemplate restTemplate = new RestTemplate();
Map<String, String> params = new HashMap<String, String>();
params.put("user", username);
params.put("password", password);
Source result = restTemplate.getForObject(url + AFMConstants.URL_LOGIN_PARAM, Source.class, params);
logger.info("ALFRESCO: CONNECTED!");
XPathOperations xpath = new Jaxp13XPathTemplate();
return xpath.evaluateAsString("//ticket", result);
}
catch (RestClientException ex) {
logger.error("FATAL ERROR - Alfresco Authentication failed - getAuthenticationTicket() - " + ex );
return null;
}
catch (Exception ex) {
logger.error("FATAL ERROR - Alfresco Authentication failed - getAuthenticationTicket() - " + ex );
return null;
}
}
You need to obtain your user noderef using this API then access its properties this way!
Edit : You need to inject service registry on your bean!
String name = AuthenticationUtil.getFullyAuthenticatedUser()
You can use this. Let me know if it works.
This will give you current logged in user name and detail.
String name = AuthenticationUtil.getFullyAuthenticatedUser();
System.out.println("Current user:"+name);
PersonService personService=serviceRegistry.getPersonService();
NodeRef node=personService.getPerson(name);
NodeService nodeService=serviceRegistry.getNodeService();
Map<QName, Serializable> props=nodeService.getProperties(node);
for (Entry<QName, Serializable> entry : props.entrySet())
{
System.out.println(entry.getKey() + "/" + entry.getValue());
}

WCF service hosed in application can't be accessed from javascript?

I have an console application which starts a WCF service, and I want to access it in an html file using javascript.
Don't want to use web.config because it seems too complicated. and I want to host the service in an addon of an application later. (but if web.config meets my requirement, it is ok to use it too).
Following is the service code:
class Program
{
static void Main(string[] args)
{
Uri baseAddress = new Uri("http://localhost:8080");
using (ServiceHost host = new ServiceHost(typeof(HelloWorldService), baseAddress))
{
host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true });
host.AddServiceEndpoint(typeof(IHelloWorldService), new BasicHttpBinding(), "bh");
host.AddServiceEndpoint(typeof(IHelloWorldService), new WebHttpBinding(WebHttpSecurityMode.None), "wb");
host.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
host.Open();
Console.WriteLine("The service is ready at {0}", baseAddress);
Console.WriteLine("Press <Enter> to stop the service.");
Console.ReadLine();
host.Close();
}
}
}
[ServiceContract]
public interface IHelloWorldService
{
[OperationContract]
[WebGet(UriTemplate = "/SayHello?name={name}", ResponseFormat = WebMessageFormat.Json)]
string SayHello(string name);
}
public class HelloWorldService : IHelloWorldService
{
public string SayHello(string name)
{
Console.WriteLine("called SayHello");
return string.Format("Hello, {0}", name);
}
}
And I want to access the service using javascript from a single html file, e.g. index.html like this:
jQuery.post("http://localhost:8080/HelloWorldService.svc/wb/SayHello", {name:"kii"}, function(ret){alert(ret);}});
Or like this:
jQuery.get("http://localhost:8080/HelloWorldService.svc/wb/SayHello?name=kii", function(ret){alert(ret);}});
But they don't work.
"POST" method got "404 Not Found"
and
"GET" method got "405 Method Not Allowed"
Any suggestion?
thanks very much~~
here is the modified program for your reference.
class Program {
static void Main(string[] args) {
Uri baseAddress = new Uri("http://localhost:8080");
using (ServiceHost host = new ServiceHost(
typeof(HelloWorldService), baseAddress)) {
host.Description.Behaviors.Add(
new ServiceMetadataBehavior { HttpGetEnabled = true });
host.AddServiceEndpoint(
typeof(IHelloWorldService), new BasicHttpBinding(), "bh");
var webEndPoint = host.AddServiceEndpoint(
typeof(IHelloWorldService),
new WebHttpBinding(WebHttpSecurityMode.None), "wb");
webEndPoint.Behaviors.Add(new WebHttpBehavior());
host.AddServiceEndpoint(
ServiceMetadataBehavior.MexContractName,
MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
host.Open();
var n = 0;
foreach (var endPoint in host.Description.Endpoints) {
Console.WriteLine("endpoint " + n);
Console.WriteLine(" address: " + endPoint.Address);
Console.WriteLine(" absolute path: " + endPoint.ListenUri.AbsolutePath);
Console.WriteLine(" absolute uri: " + endPoint.ListenUri.AbsoluteUri);
n++;
}
Console.WriteLine();
Console.WriteLine("The service is ready at {0}", baseAddress);
Console.WriteLine("Press <Enter> to stop the service.");
Console.ReadLine();
}
}
}
The only actual difference is to add WebHttpBehavior into the web endpoint.
running this program, and open brower to test address localhost:8080/wb/sayhello?name=abc
if the brower returns "abc", that means the web end point is working.
if calling this address by jQuery is still not working, then trouble-shooting on jQuery side.

Get WinForm “Cross-thread operation not valid” error for WCF host application ONLY while running in Visual Studio

I have a WCF service that I am self-hosting in a WinForm application. In the application I have a ListBox control to display diagnostic information. I pass a reference to the ListBox control to the WCF service. In that service there is a callback that uses it.
For some reason if I run the application outside of Visual Studio, I am able to use the ListBox to display information in the main hosting application, all methods in the WCF service, and the single callback in the WCF service. But, running in Visual Studio, the same application fails when trying to write info while in the callback.
Am I doing something that I fundamentally cannot do and getting away with it OR is there something incorrect going on while running in Visual Studio?
Here is part of my WinForm host
public WCFServiceHostGUI()
{
InitializeComponent();
// OutputBox is a ListBox control reference. Pass to WCF service to display diagnostics
WCFCallbacks.UsbBrokerService.initialize(OutputBox);
startService();
}
private void startService()
{
usbBrokerServiceHost = new ServiceHost(typeof(WCFCallbacks.UsbBrokerService));
ServiceDescription serviceDesciption = usbBrokerServiceHost.Description;
foreach (ServiceEndpoint endpoint in serviceDesciption.Endpoints)
{
OutputBox.Items.Add("Endpoint - address: " + endpoint.Address);
OutputBox.Items.Add(" - binding name: " + endpoint.Binding.Name);
OutputBox.Items.Add(" - contract name: " + endpoint.Contract.Name);
}
usbBrokerServiceHost.Open();
/*
ChannelFactory<WCFCallbacks.IUsbBroker> channelFactory = new ChannelFactory<WCFCallbacks.IUsbBroker>(BINDING, ADDRESS);
WCFCallbacks.IUsbBroker clientProxy = channelFactory.CreateChannel();
clientProxy.initialize(OutputBox);
*/
}
In the constructor a reference to the ListBox is passed to the WCF service. One can see the usual creation of the ServiceHost in the startService() method.
Here is the WCF service that gets the ListBox reference
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class UsbBrokerService : IUsbBroker
{
private static readonly List<IUsbBrokerCallback> subscribers = new List<IUsbBrokerCallback>();
private static ListBox OutputBox = null;
public UsbBrokerService()
{
Console.WriteLine("Service started");
UsbDevicePluginIndicator.DeviceNotify += new UsbDevicePluginIndicator.DeviceNotifyDelegate(UsbDevicePluginIndicator_DeviceNotify);
UsbDevicePluginIndicator.Start();
}
public static void initialize(ListBox outputBox)
{
OutputBox = outputBox;
}
public void AddMessage(string message)
{
// Use the following to see which endpoint is accessed
OperationContext oc = OperationContext.Current;
if (oc != null)
{
Console.WriteLine("A request was made on endpoint " + oc.Channel.LocalAddress.ToString());
if (OutputBox != null)
{
OutputBox.Items.Add("A request was made on endpoint " + oc.Channel.LocalAddress.ToString());
}
}
}
public bool RegisterDevices(UsbDevice[] usbDevices)
{
try
{
IUsbBrokerCallback callback = OperationContext.Current.GetCallbackChannel<IUsbBrokerCallback>();
if (!subscribers.Contains(callback))
{
subscribers.Add(callback);
}
return true;
}
catch
{
return false;
}
}
public bool UnRegisterDevices(UsbDevice[] usbDevices)
{
try
{
IUsbBrokerCallback callback = OperationContext.Current.GetCallbackChannel<IUsbBrokerCallback>();
if (subscribers.Contains(callback))
{
subscribers.Remove(callback);
}
return true;
}
catch
{
return false;
}
}
private void UsbDevicePluginIndicator_DeviceNotify(System.Windows.Forms.Message msg)
{
BroadcastHeader lBroadcastHeader;
Console.WriteLine("Wcf WM_DEVICECHANGE signaled");
if (OutputBox != null)
{
OutputBox.Items.Add("Wcf WM_DEVICECHANGE signaled");
}
}
}
In the callback routine UsbDevicePluginIndicator_DeviceNotify the attempt to write to OutputBox fails with "Cross-thread operation not valid: Control 'OutputBox' accessed from a thread other than the thread it was created on." but ONLY while running in Visual Studio.
So am I doing something fundamentally wrong?

Resources