Has anybody ever had an issue where the SessionModSvcContractClient Logout function throws an Exception: Additional information:
Object reference not set to an instance of an object.
When I tried LogoutAsync and Close methods they worked fine.
Can anybody help me figure out why that's happening or the difference between the 3.
I'm basically trying to use create the test from the WCF guide
static void Main(string[] args)
{
//use a self-signed certificate in IIS, be sure to include the following code. This code speeds up calls to the services and prevents the method from trying to validate the certificate with the known authorities.
ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, errors) => { return true; };
//You can toggle the value assigned to this variable to test the two bindings: SOAPHttp or BasicHttp
EndpointBindingType bindingType = EndpointBindingType.SOAPHttp;
//Epicor credentials:
string epicorUserID = "XXX";
string epiorUserPassword = "XXX";
string scheme = "http";
if (bindingType == EndpointBindingType.BasicHttp)
{
scheme = "https";
}
UriBuilder builder = new UriBuilder(scheme, "localhost");
string webServicesLink = "XXX/";
builder.Path = webServicesLink + "Ice/Lib/SessionMod.svc";
SessionModSvcContractClient sessionModClient = GetClient < SessionModSvcContractClient, SessionModSvcContract > (builder.Uri.ToString(), epicorUserID, epiorUserPassword, bindingType);
builder.Path = webServicesLink + "Erp/BO/AbcCode.svc";
ABCCodeSvcContractClient abcCodeClient = GetClient<ABCCodeSvcContractClient, ABCCodeSvcContract>(builder.Uri.ToString(), epicorUserID, epiorUserPassword, bindingType);
Guid sessionId = Guid.Empty;
sessionId = sessionModClient.Login();
//Create a new instance of the SessionModSvc. Do this because when you call any method on the service
//client class, you cannot modify its Endpointbehaviors.
builder.Path = webServicesLink + "Ice/Lib/SessionMod.svc";
sessionModClient = GetClient < SessionModSvcContractClient, SessionModSvcContract > (builder.Uri.ToString(),epicorUserID,epiorUserPassword,bindingType);
sessionModClient.Endpoint.EndpointBehaviors.Add(new HookServiceBehavior(sessionId, epicorUserID));
abcCodeClient.Endpoint.EndpointBehaviors.Add(new HookServiceBehavior(sessionId, epicorUserID));
var ts = new ABCCodeTableset();
abcCodeClient.GetNewABCCode(ref ts);
var newRow = ts.ABCCode.Where(n => n.RowMod.Equals("A", StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
if (newRow != null)
{
newRow.ABCCode = "G";
newRow.CountFreq = 30;
newRow.StockValPcnt = 100;
abcCodeClient.Update(ref ts);
ts = null;
ts = abcCodeClient.GetByID("G");
if (ts != null && ts.ABCCode.Any())
{
ABCCodeRow backupRow = new ABCCodeRow();
var fields = backupRow.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var field in fields)
{
if (field.PropertyType == typeof(System.Runtime.Serialization.ExtensionDataObject))
{
continue;
}
var fieldValue = field.GetValue(ts.ABCCode[0]);
field.SetValue(backupRow, fieldValue);
}
ts.ABCCode.Add(backupRow);
ts.ABCCode[0].CountFreq = 45;
ts.ABCCode[0].RowMod = "U";
abcCodeClient.Update(ref ts);
ts = null;
ts = abcCodeClient.GetByID("G");
if (ts != null && ts.ABCCode.Any())
{
Console.WriteLine("CountFreq = {0}", ts.ABCCode[0].CountFreq);
ts.ABCCode[0].RowMod = "D";
abcCodeClient.Update(ref ts);
try
{
ts = abcCodeClient.GetByID("G");
}
catch (FaultException<Epicor.AbcCodeSvc.EpicorFaultDetail> ex)
{
if (ex.Detail.ExceptionKindValue.Equals("RecordNotFound", StringComparison.InvariantCultureIgnoreCase))
{
Console.WriteLine("Record deleted.");
}
else
{
Console.WriteLine(ex.Message);
}
}
}
}
}
if (sessionId != Guid.Empty)
{
sessionModClient.Logout();
}
Console.ReadLine();
}
Your code worked fine for me after I changed the config to suit my environment.
It looks like you followed the step 7 on page 15 of the Epicor10_techrefWCFServices_101400.pdf guide and correctly created a new instance of the SessionModSvc after Login(). However, if you copied the full code for the Main method from page 18 then this is missing and I can replicate your issue.
Check the code that you are compiling has created a new instance of the SessionModSvc after the call to .Login().
See my other answer, but as an alternative you may want to consider this method of accessing the services.
If you have access to the client DLLs then this code might be easier:
static void Main(string[] args)
{
// Hard-coded LogOn method
// Reference: Ice.Core.Session.dll
Ice.Core.Session session = new Ice.Core.Session("manager", "manager", "net.tcp://AppServer/MyCustomerAppserver-99999-10.0.700.2");
// References: Epicor.ServiceModel.dll, Erp.Contracts.BO.ABCCode.dll
var abcCodeBO = Ice.Lib.Framework.WCFServiceSupport.CreateImpl<Erp.Proxy.BO.ABCCodeImpl>(session, Erp.Proxy.BO.ABCCodeImpl.UriPath);
// Call the BO methods
var ds = abcCodeBO.GetByID("A");
var row = ds.ABCCode[0];
System.Console.WriteLine("CountFreq is {0}", row.CountFreq);
System.Console.ReadKey();
}
Just add references to:
Ice.Core.Session.dll
Epicor.ServiceModel.dll
Erp.Contracts.BO.ABCCode.dll
Related
I want to get Bitcoin value for corresponding USD value and store it in table or variable. I got this URL from which I can get a Bitcoin value for USK amount. I searched on blockchain and I found this URL.
For example:
500usd = 0.76105818 btc
I tried:
https://blockchain.info/tobtc?currency=USD&value=500
at the end, its USD value which we want to convert in Bitcoin.
I want to get the result in the variable in C# (backend).
How can I accomplish this?
You need to just make call to server and parse the response.
var uri = String.Format("https://blockchain.info/tobtc?currency=USD&value={0}", 500);
WebClient client = new WebClient();
client.UseDefaultCredentials = true;
var data = client.DownloadString(uri);
var result = Convert.ToDouble(data);
Install-Package CoinMarketCapClient
using CoinMarketCap;
public static async Task<double> GetBitcoinInUsd(double usd){
//https://api.coinmarketcap.com/v1/ticker/bitcoin/
CoinMarketCapClient client = CoinMarketCapClient.GetInstance();
var entity = await client.GetTickerAsync("bitcoin");
return entity.PriceUsd * usd;
}
var uri = String.Format(#"https://blockchain.info/tobtc?currency=USD&value={0}",1);
WebClient client = new WebClient();
client.UseDefaultCredentials = true;
var data = client.DownloadString(uri);
var result = 1/Convert.ToDouble(data.Replace('.',',')); //you will receive 1 btc = result;
There are several APIs out there that will allow you to request the prices for a list of crypto currencies, and/or fiat currencies. The problem is that all the APIs do it in a disparate way. The follow on from that is that any one could be down at any given time, so you need to have some failure tolerance built in. I.e. the code should attempt to use one API, and if that fails, move to the next. Of course, this is further complicated by the fact that price is subjective and localised to a given country, exchange and so on. So, getting an accurate value is very difficult.
Here is example Crypto Compare client from CryptoCurrency.Net (https://github.com/MelbourneDeveloper/CryptoCurrency.Net/blob/master/src/CryptoCurrency.Net/APIClients/PriceEstimationClients/CryptoCompareClient.cs):
public class CryptoCompareClient : PriceEstimationClientBase, IPriceEstimationClient
{
public CryptoCompareClient(IRestClientFactory restClientFactory) : base(restClientFactory)
{
RESTClient = restClientFactory.CreateRESTClient(new Uri("https://min-api.cryptocompare.com"));
}
protected override Func<GetPricesArgs, Task<EstimatedPricesModel>> GetPricesFunc { get; } = async a =>
{
var retVal = new EstimatedPricesModel();
if (a.Currencies.ToList().Count == 0)
{
return retVal;
}
retVal.LastUpdate = DateTime.Now;
var symbolsPart = string.Join(",", a.Currencies.Select(c => c.Name));
var priceJson = await a.RESTClient.GetAsync<string>($"data/pricemultifull?fsyms={symbolsPart}&tsyms={a.FiatCurrency}");
var jObject = (JObject)JsonConvert.DeserializeObject(priceJson);
var rawNode = (JObject)jObject.First.First;
foreach (JProperty coinNode in rawNode.Children())
{
var fiatNode = (JProperty)coinNode.First().First;
var allProperties = fiatNode.First.Children().Cast<JProperty>().ToList();
var change24HourProperty = allProperties.FirstOrDefault(p => string.Compare(p.Name, "CHANGEPCT24HOUR", true) == 0);
var priceProperty = allProperties.FirstOrDefault(p => string.Compare(p.Name, "PRICE", true) == 0);
var price = (decimal)priceProperty.Value;
var change24Hour = (decimal)change24HourProperty.Value;
retVal.Result.Add(new CoinEstimate { CurrencySymbol = new CurrencySymbol(coinNode.Name), ChangePercentage24Hour = change24Hour, FiatEstimate = price, LastUpdate = DateTime.Now });
}
//Extreme hack. It's better to show zero than nothing at all and get the coins stuck
foreach (var currency in a.Currencies)
{
if (retVal.Result.FirstOrDefault(ce => ce.CurrencySymbol.Equals(currency)) == null)
{
retVal.Result.Add(new CoinEstimate { ChangePercentage24Hour = 0, CurrencySymbol = currency, FiatEstimate = 0, LastUpdate = DateTime.Now });
}
}
return retVal;
};
}
The PriceEstimationManager will flick through APIs until it finds one that works (https://github.com/MelbourneDeveloper/CryptoCurrency.Net/blob/master/src/CryptoCurrency.Net/APIClients/PriceEstimationClients/PriceEstimationManager.cs):
public class PriceEstimationManager
{
#region Fields
private readonly Collection<IPriceEstimationClient> _Clients = new Collection<IPriceEstimationClient>();
#endregion
#region Constructor
public PriceEstimationManager(IRestClientFactory restClientFactory)
{
foreach (var typeInfo in typeof(PriceEstimationManager).GetTypeInfo().Assembly.DefinedTypes)
{
var type = typeInfo.AsType();
if (typeInfo.ImplementedInterfaces.Contains(typeof(IPriceEstimationClient)))
{
_Clients.Add((IPriceEstimationClient)Activator.CreateInstance(type, restClientFactory));
}
}
}
#endregion
#region Public Methods
/// <summary>
/// TODO: This needs to be averaged. The two current clients give wildly different values. Need to include some Australian exchanges etc.
/// </summary>
public async Task<EstimatedPricesModel> GetPrices(IEnumerable<CurrencySymbol> currencySymbols, string fiatCurrency)
{
//Lets try a client that hasn't been used before if there is one
var client = _Clients.FirstOrDefault(c => c.AverageCallTimespan.TotalMilliseconds == 0);
var currencies = currencySymbols.ToList();
if (client != null)
{
try
{
return await client.GetPrices(currencies, fiatCurrency);
}
catch
{
//Do nothing
}
}
foreach (var client2 in _Clients.OrderBy(c => c.SuccessRate).ThenBy(c => c.AverageCallTimespan).ToList())
{
try
{
return await client2.GetPrices(currencies, fiatCurrency);
}
catch (Exception ex)
{
Logger.Log("Error Getting Prices", ex, nameof(PriceEstimationManager));
}
}
throw new Exception("Can't get prices");
}
#endregion
}
At a higher level, you can use the code like this (https://github.com/MelbourneDeveloper/CryptoCurrency.Net/blob/master/src/CryptoCurrency.Net.UnitTests/PricingTests.cs):
public async Task GetUSDBitcoinPrice()
{
var priceEstimationManager = new PriceEstimationManager(new RESTClientFactory());
var estimatedPrice = await priceEstimationManager.GetPrices(new List<CurrencySymbol> { CurrencySymbol.Bitcoin }, "USD");
Console.WriteLine($"Estimate: {estimatedPrice.Result.First().FiatEstimate}");
}
As more pricing clients are added, it will get more and more reliable.
I downloaded CEF (chromuim embedded framework) binary distributation that comes with (cefclient & cefsimple) c++ examples, And Realized that cefclient can change proxy settings on run-time.
And the key to do that is to Grab the RequestContext and call the function SetPreference.
on CefClient all works just nice.
but on CefSharp calling SetPreference always returns false, and also HasPreference returns false for the preference name "proxy".
thanks to amaitland the proper way to actively inforce changing the request-context prefrences, is to run the code on CEF UIThread as following:
Cef.UIThreadTaskFactory.StartNew(delegate {
var rc = this.browser.GetBrowser().GetHost().RequestContext;
var v = new Dictionary<string, object>();
v["mode"] = "fixed_servers";
v["server"] = "scheme://host:port";
string error;
bool success = rc.SetPreference("proxy", v, out error);
//success=true,error=""
});
if anyone need any other soulition i found this solution.
Cef.UIThreadTaskFactory.StartNew(delegate
{
string ip = "ip or adress";
string port = "port";
var rc = this.browser.GetBrowser().GetHost().RequestContext;
var dict = new Dictionary<string, object>();
dict.Add("mode", "fixed_servers");
dict.Add("server", "" + ip + ":" + port + "");
string error;
bool success = rc.SetPreference("proxy", dict, out error);
});
I downloaded CefSharp.WinForms 65.0.0 and made class, that can help to start working with proxy:
public class ChromeTest
{
public static ChromiumWebBrowser Create(WebProxy proxy = null, Action<ChromiumWebBrowser> onInited = null)
{
var result = default(ChromiumWebBrowser);
var settings = new CefSettings();
result = new ChromiumWebBrowser("about:blank");
if (proxy != null)
result.RequestHandler = new _requestHandler(proxy?.Credentials as NetworkCredential);
result.IsBrowserInitializedChanged += (s, e) =>
{
if (!e.IsBrowserInitialized)
return;
var br = (ChromiumWebBrowser)s;
if (proxy != null)
{
var v = new Dictionary<string, object>
{
["mode"] = "fixed_servers",
["server"] = $"{proxy.Address.Scheme}://{proxy.Address.Host}:{proxy.Address.Port}"
};
if (!br.GetBrowser().GetHost().RequestContext.SetPreference("proxy", v, out string error))
MessageBox.Show(error);
}
onInited?.Invoke(br);
};
return result;
}
private class _requestHandler : DefaultRequestHandler
{
private NetworkCredential _credential;
public _requestHandler(NetworkCredential credential = null) : base()
{
_credential = credential;
}
public override bool GetAuthCredentials(IWebBrowser browserControl, IBrowser browser, IFrame frame, bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback)
{
if (isProxy == true)
{
if (_credential == null)
throw new NullReferenceException("credential is null");
callback.Continue(_credential.UserName, _credential.Password);
return true;
}
return false;
}
}
}
Using:
var p = new WebProxy("Scheme://Host:Port", true, new[] { "" }, new NetworkCredential("login", "pass"));
var p1 = new WebProxy("Scheme://Host:Port", true, new[] { "" }, new NetworkCredential("login", "pass"));
var p2 = new WebProxy("Scheme://Host:Port", true, new[] { "" }, new NetworkCredential("login", "pass"));
wb1 = ChromeTest.Create(p1, b => b.Load("http://speed-tester.info/check_ip.php"));
groupBox1.Controls.Add(wb1);
wb1.Dock = DockStyle.Fill;
wb2 = ChromeTest.Create(p2, b => b.Load("http://speed-tester.info/check_ip.php"));
groupBox2.Controls.Add(wb2);
wb2.Dock = DockStyle.Fill;
wb3 = ChromeTest.Create(p, b => b.Load("http://speed-tester.info/check_ip.php"));
groupBox3.Controls.Add(wb3);
wb3.Dock = DockStyle.Fill;
If you want dynamic proxy resolver (proxy hanlder), which allow you to use different proxy for different host - you should:
1) Prepare javascript
var proxy1Str = "PROXY 1.2.3.4:5678";
var proxy2Str = "PROXY 2.3.4.5:6789";
var ProxyPacScript =
$"var proxy1 = \"{(proxy1Str.IsNullOrEmpty() ? "DIRECT" : proxy1Str)}\";" +
$"var proxy2 = \"{(proxy2Str.IsNullOrEmpty() ? "DIRECT" : proxy2Str)}\";" +
#"function FindProxyForURL(url, host) {
if (shExpMatch(host, ""*example.com"")) {
return proxy1;
}
return proxy2;
}";
var bytes = Encoding.UTF8.GetBytes(ProxyPacScript);
var base64 = Convert.ToBase64String(bytes);
2) Set it correctly
var v = new Dictionary<string, object>();
v["mode"] = "pac_script";
v["pac_url"] = "data:application/x-ns-proxy-autoconfig;base64," + base64;
3) Call SetPreference as in accepted answer https://stackoverflow.com/a/36106994/9252162
As result all request to *example.com will flow throught proxy1, all others through proxy2.
To do it I spent all day but with help of source (https://cs.chromium.org/) I found solution. Hope it helps someone.
Main problems:
1) In new version (72 or 74 as I remember) there is no ability to use "file://..." as pac_url.
2) We can't use https://developer.chrome.com/extensions/proxy in cef.. or i can't find how to do it.
p.s. How to use another types of proxy (https, socks) - https://chromium.googlesource.com/chromium/src/+/master/net/docs/proxy.md#evaluating-proxy-lists-proxy-fallback
With the new version of CefSharp, it's quite simple to set proxy:
browser = new ChromiumWebBrowser();
panel1.Controls.Add(browser);
browser.Dock = DockStyle.Fill;
await browser.LoadUrlAsync("about:blank");
await Cef.UIThreadTaskFactory.StartNew(() =>
{
browser.GetBrowser().GetHost().RequestContext.SetProxy("127.0.0.1", 1088, out string _);
});
Dears
Please help me with restoring delayed (and persisted) workflows.
I'm trying to check on self-hosted workflow store is there any instance that was delayed and can be resumed. For testing purposes I've created dummy activity that is delayed and it persists on delay.
generally resume process looks like:
get WF definition
configure sql instance store
call WaitForEvents
is there event with HasRunnableWorkflowEvent.Value name and if it is
create WorkflowApplication object and execute LoadRunnableInstance method
it works fine if store is created|initialized, WaitForEvents is called, store is closed. In such case store reads all available workflows from persisted DB and throws timeout exception if there is no workflows available to resume.
The problem happens if store is created and loop is started only for WaitForEvents (the same thing happens with BeginWaitForEvents). In such case it reads all available workflows from DB (with proper IDs) but then instead of timeout exception it is going to read one more instance (I know exactly how many workflows is there ready to be resumed because using separate test database). But fails to read and throws InstanceNotReadyException. In catch I'm checking workflowApplication.Id, but it was not saved with my test before.
I've tried to run on new (empty) persistent database and result is the same :(
This code fails:
using (var storeWrapper = new StoreWrapper(wf, connStr))
for (int q = 0; q < 5; q++)
{
var id = Resume(storeWrapper); // InstanceNotReadyException here when all activities is resumed
But this one works as expected:
for (int q = 0; q < 5; q++)
using (var storeWrapper = new StoreWrapper(wf, connStr))
{
var id = Resume(storeWrapper); // timeout exception here or beginWaitForEvents continues to wait
What is a best solution in such case? Add empty catch for InstanceNotReadyException and ignore it?
Here are my tests
const int delayTime = 15;
string connStr = "Server=db;Database=AppFabricDb_Test;Integrated Security=True;";
[TestMethod]
public void PersistOneOnIdleAndResume()
{
var wf = GetDelayActivity();
using (var storeWrapper = new StoreWrapper(wf, connStr))
{
var id = CreateAndRun(storeWrapper);
Trace.WriteLine(string.Format("done {0}", id));
}
using (var storeWrapper = new StoreWrapper(wf, connStr))
for (int q = 0; q < 5; q++)
{
var id = Resume(storeWrapper);
Trace.WriteLine(string.Format("resumed {0}", id));
}
}
Activity GetDelayActivity(string addName = "")
{
var name = new Variable<string>(string.Format("incr{0}", addName));
Activity wf = new Sequence
{
DisplayName = "testDelayActivity",
Variables = { name, new Variable<string>("CustomDataContext") },
Activities =
{
new WriteLine
{
Text = string.Format("before delay {0}", delayTime)
},
new Delay
{
Duration = new InArgument<TimeSpan>(new TimeSpan(0, 0, delayTime))
},
new WriteLine
{
Text = "after delay"
}
}
};
return wf;
}
Guid CreateAndRun(StoreWrapper sw)
{
var idleEvent = new AutoResetEvent(false);
var wfApp = sw.GetApplication();
wfApp.Idle = e => idleEvent.Set();
wfApp.Aborted = e => idleEvent.Set();
wfApp.Completed = e => idleEvent.Set();
wfApp.Run();
idleEvent.WaitOne(40 * 1000);
var res = wfApp.Id;
wfApp.Unload();
return res;
}
Guid Resume(StoreWrapper sw)
{
var res = Guid.Empty;
var events = sw.GetStore().WaitForEvents(sw.Handle, new TimeSpan(0, 0, delayTime));
if (events.Any(e => e.Equals(HasRunnableWorkflowEvent.Value)))
{
var idleEvent = new AutoResetEvent(false);
var obj = sw.GetApplication();
try
{
obj.LoadRunnableInstance(); //instancenotready here if the same store has read all instances from DB and no delayed left
obj.Idle = e => idleEvent.Set();
obj.Completed = e => idleEvent.Set();
obj.Run();
idleEvent.WaitOne(40 * 1000);
res = obj.Id;
obj.Unload();
}
catch (InstanceNotReadyException)
{
Trace.TraceError("failed to resume {0} {1} {2}", obj.Id
, obj.DefinitionIdentity == null ? null : obj.DefinitionIdentity.Name
, obj.DefinitionIdentity == null ? null : obj.DefinitionIdentity.Version);
foreach (var e in events)
{
Trace.TraceWarning("event {0}", e.Name);
}
throw;
}
}
return res;
}
Here is store wrapper definition I'm using for test:
public class StoreWrapper : IDisposable
{
Activity WfDefinition { get; set; }
public static readonly XName WorkflowHostTypePropertyName = XNamespace.Get("urn:schemas-microsoft-com:System.Activities/4.0/properties").GetName("WorkflowHostType");
public StoreWrapper(Activity wfDefinition, string connectionStr)
{
_store = new SqlWorkflowInstanceStore(connectionStr);
HostTypeName = XName.Get(wfDefinition.DisplayName, "ttt.workflow");
WfDefinition = wfDefinition;
}
SqlWorkflowInstanceStore _store;
public SqlWorkflowInstanceStore GetStore()
{
if (Handle == null)
{
InitStore(_store, WfDefinition);
Handle = _store.CreateInstanceHandle();
var view = _store.Execute(Handle, new CreateWorkflowOwnerCommand
{
InstanceOwnerMetadata = { { WorkflowHostTypePropertyName, new InstanceValue(HostTypeName) } }
}, TimeSpan.FromSeconds(30));
_store.DefaultInstanceOwner = view.InstanceOwner;
//Trace.WriteLine(string.Format("{0} owns {1}", view.InstanceOwner.InstanceOwnerId, HostTypeName));
}
return _store;
}
protected virtual void InitStore(SqlWorkflowInstanceStore store, Activity wfDefinition)
{
}
public InstanceHandle Handle { get; protected set; }
XName HostTypeName { get; set; }
public void Dispose()
{
if (Handle != null)
{
var deleteOwner = new DeleteWorkflowOwnerCommand();
//Trace.WriteLine(string.Format("{0} frees {1}", Store.DefaultInstanceOwner.InstanceOwnerId, HostTypeName));
_store.Execute(Handle, deleteOwner, TimeSpan.FromSeconds(30));
Handle.Free();
Handle = null;
_store = null;
}
}
public WorkflowApplication GetApplication()
{
var wfApp = new WorkflowApplication(WfDefinition);
wfApp.InstanceStore = GetStore();
wfApp.PersistableIdle = e => PersistableIdleAction.Persist;
Dictionary<XName, object> wfScope = new Dictionary<XName, object> { { WorkflowHostTypePropertyName, HostTypeName } };
wfApp.AddInitialInstanceValues(wfScope);
return wfApp;
}
}
I'm not workflow foundation expert so my answer is based on the official examples from Microsoft. The first one is WF4 host resumes delayed workflow (CSWF4LongRunningHost) and the second is Microsoft.Samples.AbsoluteDelay. In both samples you will find a code similar to yours i.e.:
try
{
wfApp.LoadRunnableInstance();
...
}
catch (InstanceNotReadyException)
{
//Some logging
}
Taking this into account the answer is that you are right and the empty catch for InstanceNotReadyException is a good solution.
I use DotNetOpenAuth.
So.. I am getting looking good response which has state Authenticated.
That is fine.
Now I want to get user profile info but always getting NULL.
Here is the code.
private ServiceProviderDescription GetServiceDescription()
{
string ValidateTokenEndPoint = ConfigurationManager.AppSettings["identityOAuthValidateTokenEndPointUrl"];
string ValidateAuthorizationHeaderEndPoint = ConfigurationManager.AppSettings["identityOAuthValidateAuthorizationHeaderEndPointUrl"];
string AccessTokenEndPoint = ConfigurationManager.AppSettings["identityOAuthAccessTokenURL"];
bool UseVersion10A = Convert.ToBoolean(ConfigurationManager.AppSettings["identityOAuthUseVersion10a"]);
string RequestTokenStr = ConfigurationManager.AppSettings["identityOAuthRequestTokenURL"];
string UserAuthStr = ConfigurationManager.AppSettings["identityOAuthAuthorizeUserURL"];
string AccessTokenStr = ConfigurationManager.AppSettings["identityOAuthAccessTokenURL"];
string InvalidateTokenStr = ConfigurationManager.AppSettings["identityOAuthRequestInvalidateTokenURL"];
return new ServiceProviderDescription
{
AccessTokenEndpoint = new MessageReceivingEndpoint(AccessTokenStr, HttpDeliveryMethods.PostRequest),
RequestTokenEndpoint = new MessageReceivingEndpoint(RequestTokenStr, HttpDeliveryMethods.PostRequest),
UserAuthorizationEndpoint = new MessageReceivingEndpoint(UserAuthStr, HttpDeliveryMethods.PostRequest),
TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },
ProtocolVersion = DotNetOpenAuth.OAuth.ProtocolVersion.V10a
};
}
void GetUserProfile()
{
var tokenManager = TokenManagerFactory.GetTokenManager(TokenManagerType.InMemoryTokenManager);
tokenManager.ConsumerKey = ConfigurationManager.AppSettings["identityOAuthConsumerKey"];
tokenManager.ConsumerSecret = ConfigurationManager.AppSettings["identityOAuthConsumerSecret"];
var serviceDescription = GetServiceDescription();
var consumer = new WebConsumer(serviceDescription, tokenManager);
var result = consumer.ProcessUserAuthorization(response);
if (result != null) // It is always null
{
}
Well I checked 10 times and I am pretty sure that all URLs to create ServiceProviderDescription are correct.
Any clue?
Well
finally check your web.config app keys
add key="identityOAuthConsumerKey" value="put here correct data!!!"
add key="identityOAuthConsumerSecret" value="put here correct data!!!"
and if you use hosts file you have to put correct sitename as well
127.0.0.1 site1.host1.com
EDIT: I've posted the solution below.
I know you don't like these type of questions, but i've been struggling with this issue for half a day now.
I've written a C# code that fetches user attributes from our Active Directory using LDAP, the code works well.
The code is as follows:
DirectoryEntry dirEnt = new DirectoryEntry("LDAP://dc=dom,dc=int");
DirectorySearcher adSearch = new DirectorySearcher(dirEnt);
adSearch.SearchScope = SearchScope.Subtree;
adSearch.PageSize = 10000;
adSearch.Filter = "(&(objectClass=user))";
SearchResultCollection sColl = adSearch.FindAll();
foreach (SearchResult sResult in sColl)
{
string sConn = sResult.Properties["distinguishedName"][0].ToString();
DirectoryEntry dirEnt2 = new DirectoryEntry("LDAP://" + sConn);
...
// dirEnt2 contains ALL attributes for the user
}
I'm trying to port this code to Java, but it seems like that the technique I used in C# does not work too well in Java.
Using the following code
DirContext context;
ArrayList<String> nList = new ArrayList<String>();
Hashtable env = new Hashtable();
String username = ...;
String password = ...;
try {
env.put(Context.SECURITY_PRINCIPAL, username);
env.put(Context.SECURITY_CREDENTIALS, password);
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, ldapUri);
try {
context = new InitialDirContext(env);
} catch (NamingException e) {
throw new RuntimeException(e);
}
SearchControls ctrl = new SearchControls();
ctrl.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration enumeration = context.search("", "(objectClass=user)",
ctrl);
while (enumeration.hasMore()) {
SearchResult result = (SearchResult) enumeration.next();
Attributes attribs = result.getAttributes();
NamingEnumeration values = ((BasicAttribute)
attribs.get("distinguishedName")).getAll();
while (values.hasMore()) {
nList.add(values.next().toString());
}
}
} catch (NamingException e) {
e.printStackTrace();
}
for (String sVar : nList ){
Hashtable env2 = new Hashtable();
env2.put(Context.SECURITY_PRINCIPAL, username);
env2.put(Context.SECURITY_CREDENTIALS, password);
env2.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env2.put(Context.PROVIDER_URL, "ldap://DOM/" + sVar);
Attributes attrs = null;
try {
context = new InitialDirContext(env2);
attrs = context.getAttributes(sVar);
} catch (NamingException e) {
System.out.println(e.toString());
continue;
}
System.out.println(attrs.toString());
}
Yields that attrs only contains BASIC attributes regarding the user (such as samaccountname, displayname, etc)
and no 'email', 'telephone' or any other similar attributes.
Any help on the issue is blessed!
Here's the solution, sorry for the messy code/formatting
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.*;
import javax.naming.ldap.*;
public class UserFetch {
public static void main(String[] args) {
try{
// Activate paged results
byte[] cookie = null;
int count=0;
int total;
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.REFERRAL, "follow");
env.put(Context.SECURITY_AUTHENTICATION, "Simple");
env.put(Context.SECURITY_PRINCIPAL, "USERNAME#DOM.COM");
env.put(Context.SECURITY_CREDENTIALS, "PASSWORD");
env.put(Context.PROVIDER_URL, "ldap://DOM.COM:389");
LdapContext ctx = new InitialLdapContext(env, null);
ctx.setRequestControls(new Control[]{
new PagedResultsControl(10000, Control.CRITICAL) });
do {
// Perform the search
NamingEnumeration results =
ctx.search("dc=DOM,dc=COM", "(&(objectclass=user)(employeeNumber=*))", getSimpleSearchControls());
// Iterate over a batch of search results
while (results != null && results.hasMore()) {
// Display an entry
SearchResult entry = (SearchResult)results.next();
Attributes attrs = entry.getAttributes ();
System.out.println(attrs.get("SAMAccountName")); // Username
System.out.println("Firstname: " +
attrs.get("givenname")); // firstname
System.out.println("Lastname: " + attrs.get("sn")); // lastname
System.out.println("EmployeeID " + attrs.get("employeeID"));
System.out.println("EmployeeNumber: " +
attrs.get("employeeNumber"));
// Handle the entry's response controls (if any)
}
// Examine the paged results control response
Control[] controls = ctx.getResponseControls();
if (controls != null) {
for (int i = 0; i < controls.length; i++) {
if (controls[i] instanceof PagedResultsResponseControl) {
PagedResultsResponseControl prrc =
(PagedResultsResponseControl)controls[i];
total = prrc.getResultSize();
cookie = prrc.getCookie();
} else {
// Handle other response controls (if any)
}
}
}
// Re-activate paged results
ctx.setRequestControls(new Control[]{
new PagedResultsControl(10000, cookie, Control.CRITICAL) });
} while (cookie != null);
} catch (Exception e) {
e.printStackTrace();
}
}
public static SearchControls getSimpleSearchControls() {
SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
searchControls.setTimeLimit(30000);
String[] attrIDs =
{ "SAMAccountName", "sn", "givenname", "employeeID",
"employeeNumber" };
searchControls.setReturningAttributes(attrIDs);
return searchControls;
}
}
Try setting the returned attributes on your SearchControls
ctrl.setReturningAttributes(new String[] {"email", "telephone"});