As far as I know the DependencyResolver is Thread-Safe, however, running the following code throws a null reference exception in a background thread.
public interface ITest {
}
public class Test : ITest {
}
//this works fine
var service = DependencyResolver.Current.GetService<ITest>();
var t1 = Task.Run(() => {
//This throws a Null Reference exception.
// note that DependencyResolver.Current is NOT null.
// The exception occurs in GetService
var s1 = DependencyResolver.Current.GetService<ITest>();
});
Task.WaitAll(t1);
Here's the stack trace:
at Unity.Mvc4.UnityDependencyResolver.get_ChildContainer()
at Unity.Mvc4.UnityDependencyResolver.IsRegistered(Type typeToCheck)
at Unity.Mvc4.UnityDependencyResolver.GetService(Type serviceType)
at System.Web.Mvc.DependencyResolverExtensions.GetService[TService](IDependencyResolver resolver)
at System.Threading.Tasks.Task.InnerInvoke()
at System.Threading.Tasks.Task.Execute()
I'm aware that the "Service Locator" pattern is an anti-pattern. At this point I'm just trying to understand why this doesn't work.
Any insights would be appreciated.
Thanks!
From the stack trace it is clear that you are using the the Unity.Mvc4 NuGet package, which is some unofficial package and is not published by Microsoft. This package contains a bug. Its UnityDependencyResolver.ChildContainer property calls HttpContext.Current.Items without checking whether HttpContext.Current is null and it causes a NullReferenceException when instances are resolved outside the context of a web request.
So instead of using that unofficial NuGet package, I think you're better off using the official NuGet package.
Related
I use Async with a method that call a remote service with Feign and I need to append an oauth2 token to the request, for that I use a RequestInterceptor.
#Bean
public RequestInterceptor requestTokenBearerInterceptor() {
return requestTemplate -> {
Object principal = SecurityContextHolder
.getContext()
.getAuthentication()
.getPrincipal();
if (!principal.equals("anonymousUser")) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)
SecurityContextHolder.getContext().getAuthentication().getDetails();
requestTemplate.header("Authorization", "bearer " + details.getTokenValue());
}
};
}
But when the requestInterceptor is used in another thread, I don't have acces to the same security context so getAuhentication return null.
I try to fix it in the executor configuration, I create a DelegatingSecurityContextExecutor wrapping the executor and the security context. But it seems that the bean is created in the 'main' thread and the security context is not the same used then, when a RestController method is executed, so the getAuthentication() still return null.
#Bean(name = "asyncExecutor")
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(3);
executor.setMaxPoolSize(3);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("AsynchThread-");
executor.initialize();
Executor wrappedExecutor = new DelegatingSecurityContextExecutor(executor, SecurityContextHolder.getContext());
return wrappedExecutor;
}
How can I configure the executor the right way ?
I finally found the solution, it is possible to propagate the security context automaticly to the other threads.
Just add this line of code in the static main method of your spring boot application :
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
The solution is well explained here : https://www.baeldung.com/spring-security-async-principal-propagation?fbclid=IwAR1zeGKvRvBb7GG8SmxO4x8-NlKkG39Q29WoLKxZ8NRzyKEcnDWx4Q6EUk0
!! WARNING !! : I noticed an unexpected behaviour with that solution, at least on my local dev environment. I'm connected to my app with two different accounts using sessionbox tool of chrome (same with different browsers), and it seems that when I 'm connected with user A SecurityContextHolder.getContext().getAuthentication().getPrincipal() return the security context of user B ... So Huge security problem ! I'm looking for a solution at the moment.
Reading this post : How to set up Spring Security SecurityContextHolder strategy? the solution seems to be here Spring Security and #Async (Authenticated Users mixed up)
It seems to me you can not use RequestInterceptor here. As far as I know when you use #Async you lose request context in the method which you want to execute in asynchronous way. To do so you have to explicitly pass access token to asynchronous method and provide it as request header:
#FeignClient(name = "userClient", url ="${userService.hostname}")
public interface MyFeignClient {
String AUTH_TOKEN = “Authorization”;
#GetMapping(“/users”)
List<User> findUsers(#RequestHeader(AUTH_TOKEN) String bearerToken);
}
I am trying to follow the instructions in Insights Preview where I can create custom telemetry. I followed the instructions exactly. But maybe I've got it configured wrong.
I have the APPINSIGHTS_INSTRUMENTATIONKEY set in the local.settings.json file and it seems to work fine. But when I add a new TelemetryClient I start getting those duplicate errors (below). It happens when the function gets invoked.
I really would like the telemetry data from AF to go to the same AI instrumentation key so I can see it together.
I also pulled the Microsoft.Extensions.Logging out as I wanted to use AI only, if that makes any difference.
Anyone have any suggestions?
See below...
TIA
ERROR: Exception in Command Processing for EventSource Microsoft-ApplicationInsights-Core: An instance of EventSource with Guid 74af9f20-af6a-5582-9382-f21f674fb271 already exists.
ERROR: Exception in Command Processing for EventSource Microsoft-ApplicationInsights-Core: An instance of EventSource with Guid 74af9f20-af6a-5582-9382-f21f674fb271 already exists.
Microsoft.WindowsAzure.ServiceRuntime Critical: 102 : Unexpcted Exception During Runtime Startup:
System.TypeInitializationException: The type initializer for '<Module>' threw an exception. ---> <CrtImplementationDetails>.ModuleLoadException: The C++ module failed to load while attempting to initialize the default appdomain.
---> System.Runtime.InteropServices.COMException: Invalid operation. (Exception from HRESULT: 0x80131022)
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
at <CrtImplementationDetails>.GetDefaultDomain()
at <CrtImplementationDetails>.DoCallBackInDefaultDomain(IntPtr function, Void* cookie)
at <CrtImplementationDetails>.LanguageSupport.InitializeDefaultAppDomain(LanguageSupport* )
at <CrtImplementationDetails>.LanguageSupport._Initialize(LanguageSupport* )
at <CrtImplementationDetails>.LanguageSupport.Initialize(LanguageSupport* )
--- End of inner exception stack trace ---
at <CrtImplementationDetails>.LanguageSupport.Initialize(LanguageSupport* )
at .cctor()
--- End of inner exception stack trace ---
at Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.InitializeEnvironment()
at Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment..cctor()
ERROR: Exception in Command Processing for EventSource Microsoft-ApplicationInsights-Data: An instance of EventSource with Guid a62adddb-6b4b-519d-7ba1-f983d81623e0 already exists.
I started over with a fresh AF project (and a glass of wine) to keep it simple.
The following code works:
private static TelemetryConfiguration config = new TelemetryConfiguration { InstrumentationKey = System.Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY", EnvironmentVariableTarget.Process)};
private static TelemetryClient telemetryClient = new TelemetryClient(config);
This code (directly from the Preview post) does not:
private static TelemetryClient telemetryClient = new TelemetryClient();
private static string key = TelemetryConfiguration.Active.InstrumentationKey = System.Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY", EnvironmentVariableTarget.Process);
An unfortunate side effect is that the telemetry doesn't show up in te VS2017 Application Insights window automatically. You have to use the settings gear to select the AI repository you want and then you can see it. Minutes later, but better than nothing.
After looking through the documentation and trying to find other examples of developers getting this error, I am a bit stuck. We are working with NServiceBus 6 and are occasionally getting a System.MethodAccessException in our message handlers on the call to return Task.CompletedTask. It seems to only occur when the handler is deployed in an Azure Worker Role (as opposed to running in the emulator). We are using the Azure Service Bus transport.
public Task Handle(UpdatePatientAccommodationCode message, IMessageHandlerContext context)
{
Console.WriteLine($"Handling [{message.GetType()}]");
var patientVisit = LoadByExternalPatientId(message.ClientId, message.ExternalPatientId);
var mappedEvent = patientVisit.HandleCommand(message);
if (patientVisit.IsEventAdded)
PatientVisitEventStore.Save(patientVisit);
return mappedEvent == null ? Task.CompletedTask : context.Publish(mappedEvent);
}
The actual exception looks like this:
System.MethodAccessException: Attempt by method 'XXX.Handlers.PatientVisitHandler.Handle(XXX.UpdatePatientAccommodationCode, NServiceBus.IMessageHandlerContext)' to access method 'System.Threading.Tasks.Task.get_CompletedTask()' failed.
at XXX.Handlers.PatientVisitHandler.Handle(UpdatePatientAccomm odationCode message, IMessageHandlerContext context) in PatientVisitHandler.cs: line 314
at NServiceBus.InvokeHandlerTerminator.Terminate(IInvokeHandlerContext context) in C:\BuildAgent\work\3206e2123f54fce4\src\NServiceBus.Core\Pipeline\Incoming\Invok eHandlerTerminator.cs: line 24
at NServiceBus.LoadHandlersConnector.<Invoke>d__1.MoveNext() in C:\BuildAgent\work\3206e2123f54fce4\src\NServiceBus.Core\Pipeline\Incoming\LoadH andlersConnector.cs: line 40
I suspect your code locally has .NET framework 4.6.x which supports Task.CompletedTask. When you deploy to CS and use OS family less than version 5 won't have support for 4.6.x You either will need to use a startup task to install 4.6.x or migrate to OS Family 5 (Server 2016).
That is strange. Judging by the reference sources of Task.CompletedTask I can't come up with a scenario where this could happen. The task that is statically cached is initialized with RAN_TO_COMPLETION and DO_NOT_DISPOSE. Based on that I would suggest you determine whether you are using the .NET Framework version 4.6 or higher. If you do and you still see the exception try to replace Task.CompletedTask with
static class TaskEx
{
public static readonly Task CompletedTask = Task.FromResult(0);
}
I have taken the following example code from the Unity documentation, and yet a basic controller can't be instantiated with a service implementation as a required constructor dependency.
public static void RegisterTypes(IUnityContainer container)
{
container.RegisterTypes(
AllClasses.FromLoadedAssemblies(),
WithMappings.FromMatchingInterface,
WithName.TypeName,
WithLifetime.ContainerControlled);
}
This gives the error Exception information:
Exception type: ResolutionFailedException Exception message:
Resolution of the dependency failed, type =
"[XXX].Controllers.HomeController", name = "(none)". Exception
occurred while: Calling constructor
Microsoft.Practices.Unity.InterceptionExtension.PolicyInjectionBehavior(Microsoft.Practices.Unity.InterceptionExtension.CurrentInterceptionRequest
interceptionRequest,
Microsoft.Practices.Unity.InterceptionExtension.InjectionPolicy[]
policies, Microsoft.Practices.Unity.IUnityContainer container).
Exception is: ArgumentException - Type passed must be an interface
This is an MVC5 application and i have the latest Nuget packages for Unity and the bootstrapper
in order to get this working we switched the WithName parameter to use Default as the passed value.
We have some solution that we built against a MOSS farm one of which includes a timer job. This job has been working just fine for months. Recently the administrator enlisted another server into the farm, and our timer job automatically started running on this new machine. As soon as this switch happened our timer job started yielding the error below (found this in the SP logs).
At first I thought it was a rights issue, but the timer service on the machine where it worked before and the new one are running under the same domain account. It seems to be failing while looping the site list in a site collection, on just one of the sites/webs (code snippet below). I know that this domain account has access to this because it works on the other box under same account. Does anyone have any ideas on why this cryptic error is occurring? Or if any special procedure needs to be done on this new machine to ensure it has proper ACL's for all databases in the MOSS farm?
Code:
public static void Main(string[] args)
{
SPSecurity.RunWithElevatedPrivileges(delegate() { setInputParameters(); });
}
private static void setInputParameters()
{
SPFarm farm = SPFarm.Local;
SPWebService service = farm.Services.GetValue<SPWebService>("");
foreach (SPWebApplication webApp in service.WebApplications)
{
foreach (SPSite siteCollection in webApp.Sites)
{
using(siteCollection)
{
siteCollection.CatchAccessDeniedException = false;
try
{
/* Here is the line that it fails on */
foreach (SPWeb web in siteCollection.AllWebs)
Exception:
The Execute method of job definition LMSDataImport (ID 4b37b285-ef8a-407c-8652-391639449790) threw an exception.
More information is included below.
The type initializer for 'Microsoft.SharePoint.Administration.SPPersistedObjectCollection`1' threw an exception.
Exception stack trace:
at Microsoft.SharePoint.Administration.SPPersistedObjectCollection`1.get_BackingList()
at Microsoft.SharePoint.Administration.SPPersistedObjectCollection`1.GetEnumerator()
at Microsoft.SharePoint.Administration.SPAlternateUrlCollectionManager.LookupAlternateUrl(Uri canonicalRequestUri)
at Microsoft.SharePoint.Administration.SPAlternateUrl.LookupCore(Uri uri, SPFarm farm)
at Microsoft.SharePoint.Administration.SPWebApplication.Lookup(SPFarm farm, Uri requestUri, Boolean fallbackToHttpContext, SPAlternateUrl& alternateUrl, SiteMapInfo& hostHeaderSiteInfo, Boolean& lookupRequiredContext)
at Microsoft.SharePoint.SPSite..ctor(SPFarm farm, Uri requestUri, Boolean contextSite, SPUserToken userToken)
at Microsoft.SharePoint.SPSite..ctor(SPFarm farm, Uri requestUri, Boolean contextSite)
at Microsoft.SharePoint.Administration.SPSiteCollection.get_Item(String strSiteName)
at Microsoft.SharePoint.Administration.SPSiteCollection.get_Item(Int32 index)
at Microsoft.SharePoint.Administration.SPSiteCollection.ItemAtIndex(Int32 iIndex)
at Microsoft.SharePoint.SPBaseCollection.SPEnumerator.System.Collections.IEnumerator.get_Current()
at LMSDataImporter.setInputParameters()
at Microsoft.SharePoint.SPSecurity.CodeToRunElevatedWrapper(Object state)
at Microsoft.SharePoint.SPSecurity.<>c__DisplayClass4.<RunWithElevatedPrivileges>b__2()
at Microsoft.SharePoint.Utilities.SecurityContext.RunAsProcess(CodeToRunElevated secureCode)
at Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(WaitCallback secureCode, Object param)
at Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(CodeToRunElevated secureCode)
at Axian.AxianCalendar.LMSDataImporter.Main(String[] args)
at Microsoft.SharePoint.Administration.SPTimerJobInvoke.Invoke(TimerJobExecuteData& data, Int32& result)
Check the DLLs for SharePoint, do all exists and all are the same version? Try putting a catch for the TypeInitializationException, and see what is wrong inside that exception.
It's not a solution, but as a workaround in the interim, I think my suggestion to one of your other questions here:
How do you instruct a SharePoint Farm to run a Timer Job on a specific server?
will keep you running while you investigate further.
Check the NLB (Network Load Balancing) configuration. Most of the time SharePoint and applications integrated to it fails when NLB changes its state. There is a patch available to solve this problem. Just a suggestion. Not sure if this is the reason. But I have faced a similar issue and the root cause was an NLB bug
A Type init exception just means an exception occurred in the .ctor of the class (as you probably know). The real exception should be in the InnerException property - can you get your hands on this? Likely it's stemming from the database alright, from Microsoft.SharePoint.Administration.SPPersistedChildCollection InitializeFromDatabse method.
Can you look into the sharepoint logs (on that errant server) for information about the database error, it will be there. Reading logs are a pain, but not if you install the ULS Log Viewer feature from http://www.codeplex.com/features
Since the stacktrace has SPAlternateUrl tinkering furhter up the stack, perhaps your zones are misconfigured (and do not include a mapping for this new server's machine name) - granted, it shouldn't fail this bad, but what can you do.
You can filter the ULS logs by source.
-Oisin