Why is Sling ignoring the "adaptable" parameter of HTL's data-sly-use? - sling

In 2017 functionality was added to Apache Sling and HTL to allow use of the Use API with a different adaptable (such as a resource resolver, or a child resource) rather than the default backing resource or request.
Feike Visser provided an example of how to use such flexible adapters with a simple list:
<div data-sly-list.child="${resource.listChildren}">
<sly data-sly-use.c="${'com.adobe.examples.htl.core.models.HelloWorldModel' # adaptable=child}" />
${c.resourceType}
</div>
However, the example does not appear to work (in this case, using non-AEM Sling 11). While the HelloWorldModel (a Sling Model) is instantiated, the backing resource is always the original page, not the adaptable specified. It's as it the adaptable=child part is ignored.
What might be preventing this useful feature from working?
EDIT: The HelloWorldModel is based on Visser's example:
#Model(adaptables=Resource.class)
public class HelloWorldModel {
private static Logger log = LoggerFactory.getLogger(HelloWorldModel.class);
#OSGiService
private SlingSettingsService settings;
#SlingObject
private ResourceResolver resourceResolver;
#ValueMapValue(name = "sling:resourceType", injectionStrategy=InjectionStrategy.OPTIONAL) #Default(values="No resourceType")
private String resourceType;
private String message;
#PostConstruct
public void init() {
log.info("Reached init of HelloWorldModel");
message = "\tHello World!\n";
message += "\tResource type is: " + resourceType + "\n";
message += "\tUser id is " + resourceResolver.getUserID() + "\n";
}
public String getResourceType() {
return resourceType;
}
public String getMessage() {
log.info("Inside getMessage() method");
return message;
}
}
The output is always the resource type of the page resource, never the resource type of the list children.
EDIT: Could this be because the SlingModelsUseProvider is used before the JavaUseProvider, meaning that the JavaUseProvider - which provides the flexible adaptation - is never reached?

Related

How to make ASP.NET Core MVC routes generation relative?

Context - the application
I'm developing an ASP.NET Core application using netcoreapp2.2 (dotnet core 2.2). This application is distributed as a Docker image and it's working well. It's an Add-On for HASS.IO, an automated environment for Home Assistant based on docker. Everything works well.
The missing feature in my app: HASS.IO's ingress
But... I want to make use of a HASS.IO feature called Ingress: https://developers.home-assistant.io/docs/en/next/hassio_addon_presentation.html#ingress
The goal of this feature is to allow Home Assistant to route the http traffic to the add-on without having to manage the authentication part and without requiring the system owner to setup a port mapping on its firewall for the communication. So it's a very nice feature.
MVC routing paths are absolute
To use HASS.IO ingress, the application needs to provide relative paths for navigation. By example, when the user is loading the url https://my.hass.io/a0a0a0a0_myaddon/, the add-on container will receive a / http request. It means all navigation in the app must be relative.
By example, while on the root page (https://my.hass.io/a0a0a0a0_myaddon/ translated to a HTTP GET / for the container), we add the following razor code:
<a asp-action="myAction" asp-route-id="123">this is a link</a>
We'll get a resulting html like this, which is wrong in this case:
this is a link <!-- THIS IS A WRONG LINK! -->
It's wrong because it's getting translated to https://my.hass.io/Home/myAction/123 by the browser while the correct address would be https://my.hass.io/a0a0a0a0_myaddon/Home/myAction/123.
To fix this, I need the resulting html to be like that:
<!-- THIS WOULD BE THE RIGHT LINK [option A] -->
this is a link
<!-- THIS WOULD BE GOOD TOO [option B] -->
this is a link
The problem to solve
[option A]
Is there a way to setup the MVC's routing engine to output relative paths instead of absolute ones? That would solve my problem.
It also means when you're on https://my.hass.io/a0a0a0a0_myaddon/Home/myAction/123 and you want to go home, the result should be
Return home
---OR---
[option B]
Another approach would be to find a way to discover the actual absolute path and find a way to prepend it in the MVC's routing mechanism.
I found the solution to my own question. I don't know if it's the best way to do it, but it worked!
1. Create a wrapper for the existing IUrlHelper
This one converts absolute paths to relative ones...
private class RelativeUrlHelper : IUrlHelper
{
private readonly IUrlHelper _inner;
private readonly HttpContext _contextHttpContext;
public RelativeUrlHelper(IUrlHelper inner, HttpContext contextHttpContext)
{
_inner = inner;
_contextHttpContext = contextHttpContext;
}
private string MakeUrlRelative(string url)
{
if (url.Length == 0 || url[0] != '/')
{
return url; // that's an url going elsewhere: no need to be relative
}
if (url.Length > 2 && url[1] == '/')
{
return url; // That's a "//" url, means it's like an absolute one using the same scheme
}
// This is not a well-optimized algorithm, but it works!
// You're welcome to improve it.
var deepness = _contextHttpContext.Request.Path.Value.Split('/').Length - 2;
if (deepness == 0)
{
return url.Substring(1);
}
else
{
for (var i = 0; i < deepness; i++)
{
url = i == 0 ? ".." + url : "../" + url;
}
}
return url;
}
public string Action(UrlActionContext actionContext)
{
return MakeUrlRelative(_inner.Action(actionContext));
}
public string Content(string contentPath)
{
return MakeUrlRelative(_inner.Content(contentPath));
}
public bool IsLocalUrl(string url)
{
if (url?.StartsWith("../") ?? false)
{
return true;
}
return _inner.IsLocalUrl(url);
}
public string RouteUrl(UrlRouteContext routeContext) => _inner.RouteUrl(routeContext);
public string Link(string routeName, object values) => _inner.Link(routeName, values);
public ActionContext ActionContext => _inner.ActionContext;
}
2. Create a wrapper for IUrlHelperFactory
public class RelativeUrlHelperFactory : IUrlHelperFactory
{
private readonly IUrlHelperFactory _previous;
public RelativeUrlHelperFactory(IUrlHelperFactory previous)
{
_previous = previous;
}
public IUrlHelper GetUrlHelper(ActionContext context)
{
var inner = _previous.GetUrlHelper(context);
return new RelativeUrlHelper(inner, context.HttpContext);
}
}
3. Wrap the IUrlHelper in DI/IoC
Put this in the ConfigureServices() of the Startup.cs file:
services.Decorate<IUrlHelperFactory>((previous, _) => new RelativeUrlHelperFactory(previous));
IMPORTANT: You need to install the nuget package Scrutor for that https://www.nuget.org/packages/Scrutor/.
Finally...
I posted my solution as a PR there: https://github.com/yllibed/Zigbee2MqttAssistant/pull/2

How to bind view model property with different name

Is there a way to make a reflection for a view model property as an element with different name and id values on the html side.
That is the main question of what I want to achieve. So the basic introduction for the question is like:
1- I have a view model (as an example) which created for a filter operation in view side.
public class FilterViewModel
{
public string FilterParameter { get; set; }
}
2- I have a controller action which is created for GETting form values(here it is filter)
public ActionResult Index(FilterViewModel filter)
{
return View();
}
3- I have a view that a user can filter on some data and sends parameters via querystring over form submit.
#using (Html.BeginForm("Index", "Demo", FormMethod.Get))
{
#Html.LabelFor(model => model.FilterParameter)
#Html.EditorFor(model => model.FilterParameter)
<input type="submit" value="Do Filter" />
}
4- And what I want to see in rendered view output is
<form action="/Demo" method="get">
<label for="fp">FilterParameter</label>
<input id="fp" name="fp" type="text" />
<input type="submit" value="Do Filter" />
</form>
5- And as a solution I want to modify my view model like this:
public class FilterViewModel
{
[BindParameter("fp")]
[BindParameter("filter")] // this one extra alias
[BindParameter("param")] //this one extra alias
public string FilterParameter { get; set; }
}
So the basic question is about BindAttribute but the usage of complex type properties. But also if there is a built in way of doing this is much better.
Built-in pros:
1- Usage with TextBoxFor, EditorFor, LabelFor and other strongly typed view model helpers can understand and communicate better with each other.
2- Url routing support
3- No framework by desing problems :
In general, we recommend folks don’t write custom model binders
because they’re difficult to get right and they’re rarely needed. The
issue I’m discussing in this post might be one of those cases where
it’s warranted.
Link of quote
And also after some research I found these useful works:
Binding model property with different name
One step upgrade of first link
Here some informative guide
Result: But none of them give me my problems exact solution. I am looking for a strongly typed solution for this problem. Of course if you know any other way to go, please share.
Update
The underlying reasons why I want to do this are basically:
1- Everytime I want to change the html control name then I have to change PropertyName at compile time. (There is a difference Changing a property name between changing a string in code)
2- I want to hide (camouflage) real property names from end users. Most of times View Model property names same as mapped Entity Objects property names. (For developer readability reasons)
3- I don't want to remove the readability for developer. Think about lots of properties with like 2-3 character long and with mo meanings.
4- There are lots of view models written. So changing their names are going to take more time than this solution.
5- This is going to be better solution (in my POV) than others which are described in other questions until now.
Actually, there is a way to do it.
In ASP.NET binding metadata gathered by TypeDescriptor, not by reflection directly. To be more precious, AssociatedMetadataTypeTypeDescriptionProvider is used, which, in turn, simply calls TypeDescriptor.GetProvider with our model type as parameter:
public AssociatedMetadataTypeTypeDescriptionProvider(Type type)
: base(TypeDescriptor.GetProvider(type))
{
}
So, everything we need is to set our custom TypeDescriptionProvider for our model.
Let's implement our custom provider. First of all, let's define attribute for custom property name:
[AttributeUsage(AttributeTargets.Property)]
public class CustomBindingNameAttribute : Attribute
{
public CustomBindingNameAttribute(string propertyName)
{
this.PropertyName = propertyName;
}
public string PropertyName { get; private set; }
}
If you already have attribute with desired name, you can reuse it. Attribute defined above is just an example. I prefer to use JsonPropertyAttribute because in most cases I work with json and Newtonsoft's library and want to define custom name only once.
The next step is to define custom type descriptor. We will not implement whole type descriptor logic and use default implementation. Only property accessing will be overridden:
public class MyTypeDescription : CustomTypeDescriptor
{
public MyTypeDescription(ICustomTypeDescriptor parent)
: base(parent)
{
}
public override PropertyDescriptorCollection GetProperties()
{
return Wrap(base.GetProperties());
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
return Wrap(base.GetProperties(attributes));
}
private static PropertyDescriptorCollection Wrap(PropertyDescriptorCollection src)
{
var wrapped = src.Cast<PropertyDescriptor>()
.Select(pd => (PropertyDescriptor)new MyPropertyDescriptor(pd))
.ToArray();
return new PropertyDescriptorCollection(wrapped);
}
}
Also custom property descriptor need to be implemented. Again, everything except property name will be handled by default descriptor. Note, NameHashCode for some reason is a separate property. As name changed, so it's hash code need to be changed too:
public class MyPropertyDescriptor : PropertyDescriptor
{
private readonly PropertyDescriptor _descr;
private readonly string _name;
public MyPropertyDescriptor(PropertyDescriptor descr)
: base(descr)
{
this._descr = descr;
var customBindingName = this._descr.Attributes[typeof(CustomBindingNameAttribute)] as CustomBindingNameAttribute;
this._name = customBindingName != null ? customBindingName.PropertyName : this._descr.Name;
}
public override string Name
{
get { return this._name; }
}
protected override int NameHashCode
{
get { return this.Name.GetHashCode(); }
}
public override bool CanResetValue(object component)
{
return this._descr.CanResetValue(component);
}
public override object GetValue(object component)
{
return this._descr.GetValue(component);
}
public override void ResetValue(object component)
{
this._descr.ResetValue(component);
}
public override void SetValue(object component, object value)
{
this._descr.SetValue(component, value);
}
public override bool ShouldSerializeValue(object component)
{
return this._descr.ShouldSerializeValue(component);
}
public override Type ComponentType
{
get { return this._descr.ComponentType; }
}
public override bool IsReadOnly
{
get { return this._descr.IsReadOnly; }
}
public override Type PropertyType
{
get { return this._descr.PropertyType; }
}
}
Finally, we need our custom TypeDescriptionProvider and way to bind it to our model type. By default, TypeDescriptionProviderAttribute is designed to perform that binding. But in this case we will not able to get default provider that we want to use internally. In most cases, default provider will be ReflectTypeDescriptionProvider. But this is not guaranteed and this provider is inaccessible due to it's protection level - it's internal. But we do still want to fallback to default provider.
TypeDescriptor also allow to explicitly add provider for our type via AddProvider method. That what we will use. But firstly, let's define our custom provider itself:
public class MyTypeDescriptionProvider : TypeDescriptionProvider
{
private readonly TypeDescriptionProvider _defaultProvider;
public MyTypeDescriptionProvider(TypeDescriptionProvider defaultProvider)
{
this._defaultProvider = defaultProvider;
}
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
{
return new MyTypeDescription(this._defaultProvider.GetTypeDescriptor(objectType, instance));
}
}
The last step is to bind our provider to our model types. We can implement it in any way we want. For example, let's define some simple class, such as:
public static class TypeDescriptorsConfig
{
public static void InitializeCustomTypeDescriptorProvider()
{
// Assume, this code and all models are in one assembly
var types = Assembly.GetExecutingAssembly().GetTypes()
.Where(t => t.GetProperties().Any(p => p.IsDefined(typeof(CustomBindingNameAttribute))));
foreach (var type in types)
{
var defaultProvider = TypeDescriptor.GetProvider(type);
TypeDescriptor.AddProvider(new MyTypeDescriptionProvider(defaultProvider), type);
}
}
}
And either invoke that code via web activation:
[assembly: PreApplicationStartMethod(typeof(TypeDescriptorsConfig), "InitializeCustomTypeDescriptorProvider")]
Or simply call it in Application_Start method:
public class MvcApplication : HttpApplication
{
protected void Application_Start()
{
TypeDescriptorsConfig.InitializeCustomTypeDescriptorProvider();
// rest of init code ...
}
}
But this is not the end of the story. :(
Consider following model:
public class TestModel
{
[CustomBindingName("actual_name")]
[DisplayName("Yay!")]
public string TestProperty { get; set; }
}
If we try to write in .cshtml view something like:
#model Some.Namespace.TestModel
#Html.DisplayNameFor(x => x.TestProperty) #* fail *#
We will get ArgumentException:
An exception of type 'System.ArgumentException' occurred in System.Web.Mvc.dll but was not handled in user code
Additional information: The property Some.Namespace.TestModel.TestProperty could not be found.
That because all helpers soon or later invoke ModelMetadata.FromLambdaExpression method. And this method take expression we provided (x => x.TestProperty) and takes member name directly from member info and have no clue about any of our attributes, metadata (who cares, huh?):
internal static ModelMetadata FromLambdaExpression<TParameter, TValue>(/* ... */)
{
// ...
case ExpressionType.MemberAccess:
MemberExpression memberExpression = (MemberExpression) expression.Body;
propertyName = memberExpression.Member is PropertyInfo ? memberExpression.Member.Name : (string) null;
// I want to cry here - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// ...
}
For x => x.TestProperty (where x is TestModel) this method will return TestProperty, not actual_name, but model metadata contains actual_name property, have no TestProperty. That is why the property could not be found error thrown.
This is a design failure.
However despite this little inconvenience there are several workarounds, such as:
The easiest way is to access our members by theirs redefined names:
#model Some.Namespace.TestModel
#Html.DisplayName("actual_name") #* this will render "Yay!" *#
This is not good. No intellisense at all and as our model change we will have no any compilation errors. On any change anything can be broken and there is no easy way to detect that.
Another way is a bit more complex - we can create our own version of that helpers and forbid anybody from calling default helpers or ModelMetadata.FromLambdaExpression for model classes with renamed properties.
Finally, combination of previous two would be preferred: write own analogue to get property name with redefinition support, then pass that into default helper. Something like this:
#model Some.Namespace.TestModel
#Html.DisplayName(Html.For(x => x.TestProperty))
Compilation-time and intellisense support and no need to spend a lot of time for complete set of helpers. Profit!
Also everything described above work like a charm for model binding. During model binding process default binder also use metadata, gathered by TypeDescriptor.
But I guess binding json data is the best use case. You know, lots of web software and standards use lowercase_separated_by_underscores naming convention. Unfortunately this is not usual convention for C#. Having classes with members named in different convention looks ugly and can end up in troubles. Especially when you have tools that whining every time about naming violation.
ASP.NET MVC default model binder does not bind json to model the same way as it happens when you call newtonsoft's JsonConverter.DeserializeObject method. Instead, json parsed into dictionary. For example:
{
complex: {
text: "blabla",
value: 12.34
},
num: 1
}
will be translated into following dictionary:
{ "complex.text", "blabla" }
{ "complex.value", "12.34" }
{ "num", "1" }
And later these values along with others values from query string, route data and so on, collected by different implementations of IValueProvider, will be used by default binder to bind a model with help of metadata, gathered by TypeDescriptor.
So we came full circle from creating model, rendering, binding it back and use it.
The short answer is NO and long answer still NO. There is no built-in helper, attribute, model binder, whatever is it (Nothing out of box).
But what I did in before answer (I deleted it) was an awful solution that I realized yesterday. I am going to put it in github for who still wants to see (maybe it solves somebody problem) (I don't suggest it also!)
Now I searched it for again and I couldn't find anything helpful. If you are using something like AutoMapper or ValueInjecter like tool for mapping your ViewModel objects to Business objects and if you want to obfuscate that View Model parameters also, probably you are in some trouble. Of course you can do it but strongly typed html helpers are not going to help you alot. I even not talking about the if other developers taking branch and working over common view models.
Luckily my project (4 people working on it, and its commercial use for) not that big for now, so I decided to change View Model property names! (It is still lot work to do. Hundreds of view models to obfuscate their properties!!!) Thank you Asp.Net MVC !
There some ways in the links which I gave in question. But also if you still want to use the BindAlias attribute, I can only suggest you to use the following extension methods. At least you dont have to write same alias string which you write in BindAlias attribute.
Here it is:
public static string AliasNameFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression)
{
var memberExpression = ExpressionHelpers.GetMemberExpression(expression);
if (memberExpression == null)
throw new InvalidOperationException("Expression must be a member expression");
var aliasAttr = memberExpression.Member.GetAttribute<BindAliasAttribute>();
if (aliasAttr != null)
{
return MvcHtmlString.Create(aliasAttr.Alias).ToHtmlString();
}
return htmlHelper.NameFor(expression).ToHtmlString();
}
public static string AliasIdFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression)
{
var memberExpression = ExpressionHelpers.GetMemberExpression(expression);
if (memberExpression == null)
throw new InvalidOperationException("Expression must be a member expression");
var aliasAttr = memberExpression.Member.GetAttribute<BindAliasAttribute>();
if (aliasAttr != null)
{
return MvcHtmlString.Create(TagBuilder.CreateSanitizedId(aliasAttr.Alias)).ToHtmlString();
}
return htmlHelper.IdFor(expression).ToHtmlString();
}
public static T GetAttribute<T>(this ICustomAttributeProvider provider)
where T : Attribute
{
var attributes = provider.GetCustomAttributes(typeof(T), true);
return attributes.Length > 0 ? attributes[0] as T : null;
}
public static MemberExpression GetMemberExpression<TModel, TProperty>(Expression<Func<TModel, TProperty>> expression)
{
MemberExpression memberExpression;
if (expression.Body is UnaryExpression)
{
var unaryExpression = (UnaryExpression)expression.Body;
memberExpression = (MemberExpression)unaryExpression.Operand;
}
else
{
memberExpression = (MemberExpression)expression.Body;
}
return memberExpression;
}
When you want to use it:
[ModelBinder(typeof(AliasModelBinder))]
public class FilterViewModel
{
[BindAlias("someText")]
public string FilterParameter { get; set; }
}
In html:
#* at least you dont write "someText" here again *#
#Html.Editor(Html.AliasNameFor(model => model.FilterParameter))
#Html.ValidationMessage(Html.AliasNameFor(model => model.FilterParameter))
So I am leaving this answer here like this. This is even not an answer (and there is no answer for MVC 5) but who searching in google for same problem might find useful this experience.
And here is the github repo: https://github.com/yusufuzun/so-view-model-bind-20869735

Monotouch/WCF: How to consume the wcf service without svcutil

Becuase monotouch compile to native code, so it has some limitation such as dynamic invoke is not allowed.
But I have a lot class in .net, that I use the ChannelFactory dynamic to invoke the wcf service: new ChannelFactory(myBinding, myEndpoint); Now in monotouch I should use the slsvcutil to generate the wcf proxy class, but the slsvcutil generate a lot of Unnecessary extra code (huge), and Makes consumers difficult to unit test, due to high coupling with the WCF infrastructure through the ClientBase class.
Is there a better solution except the ChannelFactory? I would rather write the code manually, have more control over how services are invoked such as the ChannelFactory.
==========
ChannelFactory<IMyContract> factory = new ChannelFactory<IMyContract>(binding, endpointAddress);
return factory.CreateChannel();
//==> It throw exception: MonoTouch does not support dynamic proxy code generation. Override this method or its caller to return specific client proxy instance
ChannelFactory<T> has a virtual method CreateChannel(). If this is not overridden, it uses dynamic code generation, which fails on MonoTouch.
The solution is to override it and provide your own compile-time implementation.
Below is an old service implementation of mine that at least used to work on MonoTouch. I split it up into 2 partial classes - the first one being linked in all builds, the 2nd only in the iOS builds (allowing the dynamic generation mechanism to still work on windows).
I've stripped it down to only contain 1 service call.
TransactionService.cs:
public partial class TransactionService : ClientBase<IConsumerService>, IConsumerService
{
public TransactionService()
{
}
public TransactionService(string endpointConfigurationName) :
base(endpointConfigurationName)
{
}
public TransactionService(string endpointConfigurationName, string remoteAddress) :
base(endpointConfigurationName, remoteAddress)
{
}
public TransactionService(string endpointConfigurationName, EndpointAddress remoteAddress) :
base(endpointConfigurationName, remoteAddress)
{
}
public TransactionService(Binding binding, EndpointAddress remoteAddress) :
base(binding, remoteAddress)
{
}
public AccountBalanceResponse GetAccountBalance( AccountBalanceQuery query )
{
return Channel.GetAccountBalance( query );
}
}
TransactionService.iOS.cs:
ConsumerServiceClientChannel which executes the calls via reflection)
public partial class TransactionService
{
protected override IConsumerService CreateChannel()
{
return new ConsumerServiceClientChannel(this);
}
private class ConsumerServiceClientChannel : ChannelBase<IConsumerService>, IConsumerService
{
public ConsumerServiceClientChannel(System.ServiceModel.ClientBase<IConsumerService> client) :
base(client)
{
}
// Sync version
public AccountBalanceResponse GetAccountBalance(AccountBalanceQuery query)
{
object[] _args = new object[1];
_args[0] = query;
return (AccountBalanceResponse)base.Invoke("GetAccountBalance", _args);
}
// Async version
public IAsyncResult BeginGetAccountBalance(AccountBalanceQuery query, AsyncCallback callback, object asyncState )
{
object[] _args = new object[1];
_args[0] = query;
return (IAsyncResult)base.BeginInvoke("GetAccountBalance", _args, callback, asyncState );
}
public AccountBalanceResponse EndGetAccountBalance(IAsyncResult asyncResult)
{
object[] _args = new object[0];
return (AccountBalanceResponse)base.EndInvoke("GetAccountBalance", _args, asyncResult);
}
}
}
EDIT: I just tested this with the latest MT (5.2) - it no longer needs all that extra boiler plate I had in there before, just the CreateChannel() override. I've cleaned up the sample code to match.
EDIT2: I added an async method implementation.
I think you might be confusing terms here - ChannelFactory is a generic type, not a dynamic.
According to MonoTouch documentation, although there's limitations to the Generics support in MonoTouch, ChannelFactory should be okay here.
Have you tried using ChannelFactory?

Struts2 - Implementing ModelDriven - Create an interceptor that accesses the model properties

I'm relatively new to Struts2.
I've started using ModelDriven to reduce overhead in development.
I wanted to write an interface to modify a property before it gets to the action but I don't see how you can access the properties of a class that implements ModelDriven for the Model.
I can see how things like validate() can work as they are in the actual action class.
I changed the design to encapsulate the logic behind the service anyways but still would like to know if this is possible.
We're doing everything by ajax/json so I find modeldriven helps quite a lot - not sure if there is a better alternative though!
Edit - code example:
Trying to replace a message with a message in a template to use in an email body.
public class EmailActionImpl implements EmailAction {
private Email email=new Email();
private EmailService emailService;
public Email getModel(){
return email;
}
[... getters and setters ...]
public String execute(){
logger.info("Email action is sendind an email...");
try{
emailService.sendNewMail(email);
}catch(Exception e){
logger.error("Email not sent: " + e.getMessage());
return "failure";
}
return "success";
}
}
Email model something like this
#Entity
#Table(name="email")
public class Email {
private Long id;
private String from;
private String to;
private String message;
private String templateType;
[...]
}
I would like an interceptor preprocessor to replace email.message.
Should look something like this but action.getMessage/setMessage aren't available.
public class SimpleInterceptor extends AbstractInterceptor {
public String intercept(ActionInvocation invocation) throws Exception {
EmailAction action = (EmailAction)invocation.getAction();
action.setMessage(MessageTemplateFactoryImpl(action.getMessage(), action.getTemplateType());
return invocation.invoke();
}
}
If you still want to implement an interceptor to work on a particular set of models then you will check if the Action implements ModelDriven. Via reflection (or Apache bean utils) you can derive the particular model in question, to determine if your interceptor applies and then act on it accordingly.

What is the best way of using NLog with MEF?

I am wondering what is the best way to use NLog with Managed Extensibility Framework (MEF)?
I have an application that support plugins using MEF architecture (Import and Exports etc)
I want to add logging capability to my application.
As a logging component I want to use NLog.
What would you recommend?
1. Create a wrapper for NLog, i.e. additional plugin that configures NLog and exports functions like void Log(string level, string message) that other plugins importing
2. Every plugin should have it is own instance of NLog configured and used. (They all would write to the same file actually).
This is an interesting approach, however, it seems to suffer from the drawback that all loggers that are injected (or the one singleton that is injected) will be the same instance (or will have the same name, the name being the name of the NLogLoggingService class. That means that you cannot very easily control the granularity of logging (i.e. turn logging to "Info" level in one class and "Warn" in another class). Also, if you opt to use the call site formatting tokens, you will always get the call site of the call the the NLog logger rather than the call site in your application code.
Here is an abbreviated version of the logger that was linked:
[Export(Services.Logging.LoggingService, typeof(ILoggingService))]
class NLogLoggingService : ILoggingService
{
Logger log; public NLogLoggingService()
{
log = LogManager.GetCurrentClassLogger();
}
public void Debug(object message)
{
log.Debug(message);
}
public void DebugWithFormat(string format, params object[] args)
{
if (args.Length == 0)
{
log.Debug(format);
}
else
{
Debug(string.Format(format, args));
}
}
public bool IsDebugEnabled
{
get
{
return log.IsDebugEnabled;
}
}
}
In the constructor LogManager.GetCurrentClassLogger() is used to get the NLog logger. GetCurrentClassLogger will return a NLog logger that is "named" based on the "current" type, which, in this case, is NLogLoggingService. So, to configure NLog in the app.config file, you will configure based on the that the logger is named "SoapBox.Core.NLogLoggingService". Commonly, in code that uses NLog (or log4net) directly, each class gets its own uniquely named logger like this:
namespace MyNamespace
{
public class MyClass1
{
private static readonly Logger logger LogManager.GetCurrentClassLogger();
public void DoSomeWork()
{
logger.Info("Logging from inside MyClass1.DoSomeWork");
}
}
public class MyClass2
{
private static readonly Logger logger LogManager.GetCurrentClassLogger();
public void DoSomeWork()
{
logger.Info("Logging from inside MyClass2.DoSomeWork");
}
}
}
Now the logging for MyClass1 and MyClass2 is individually controllable. You can configure different levels for each class, send them to different targets, or turn one or both off altogether. Alternatively, due to the concept of logger hierarchies in both log4net and NLog, you could control the logging in both class simultaneously by configuring a "logger" for the namespace (MyNamespace in this case), or any "ancestor" namespace. If there is not a logger configured for the fully qualified typename, then the logging framework essentially moves up the hierarchy by considering the name a dot delimited string and removing the last chunk and checking to see if that logger is configured. So, in this case, we are requesting loggers for MyNamespace.MyClass1 and MyNamespace.MyClass2. I could configure the app.config file to have MyNamespace log at the "info" and write to a file target (appender in log4net-speak). If I did that, then both loggers that I requested via their fully qualified names would inherit the MyNamespace configuration.
With the suggested way of injecting NLog via MEF, you will only have one logger instance, so you cannot configure each class to log differently. Also, as I mentioned earlier, if you opt to log call site information, you will always get "SoapBox.Core.NLogLoggingService" for the class and "Debug" (or DebugWithFormat, or Info, or InfoWithFormat, etc) for the method.
This seems to be an issue with successfully injecting loggers from log4net and NLog. You can see the question that I asked about this very issue a couple of months ago.
Ultimately I was able to figure out how some dependency injection frameworks can successfully inject log4net and NLog loggers that are specific to the class being created (i.e. if the DI framework is instantiating MyClass, which in turn depends on an ILogger interface, then MyClass will get a logger that is essentially equivalent to what would have happened if MyClass requested the logger itself via the LogManager.GetCurrentClassLogger api). Generally "resolvers" in DI/IoC frameworks are given the current context (containing, among other information, the type of the object currently being created). With that type available, it becomes a simple matter of having a logging framework-specific resolver receive that type and pass it along to the logging framework to create a logger appropriate for that type.
In order to get the most out of NLog's (and log4net's) capabilities you would really like to be able to tell MEF that your class is dependendent on "ILogger", but also that the instance of "ILogger" that gets injected into your class should depend on the Type of your class.
I don't know how easy it will be to achieve that with MEF. Alternatively, you could wrap NLog's static LogManager in a ILogManager and inject that. That would deviate from the normal "inject ILogger" paradigm.
To summarize: If you inject NLog via MEF this way, you will indeed be able to log with NLog, but you will only ever have one named logger (SoapBox.Core.NLogLoggingService). This means that you will not be able control with any degree of granularity - either for levels/on/off or for output (NLog Target/log4net Appender)
I don't have a good answer for what to do as far as injecting NLog via MEF AND keeping the granularity/flexibility that "raw" NLog gives you.
I can say that we have decided to use Common.Logging for .NET to abstract the logging framework but we decided NOT to inject logging. Instead, we will just use a static LogManager (as provided by Common.Logging) to hand out loggers.
I think Option 1 is better.
You can take a look at how the open source framework SoapBox Core imports a reference to an ILoggingService using MEF. It also provides a default implementation of the logging service based on NLog, but you could easily swap it out for log4Net, for example.
For reference:
the ILoggingService interface
the Logging Service that wraps NLog and exports itself using MEF
SoapBox Core is LGPL'd, so you might be able to use (this part) in your application.
I have been fighting with this problem a while now.
Really improtant was the Callsite (FullyQualified Namespace) within the logfiles.
First, i tryed to get the right logger out of the Stacktrace:
[MethodImpl(MethodImplOptions.NoInlining)]
private static NLog.Logger GetLogger()
{
var stackTrace = new StackTrace(false);
StackFrame[] frames = stackTrace.GetFrames();
if (null == frames) throw new ArgumentException("Stack frame array is null.");
StackFrame stackFrame;
switch (frames.Length)
{
case 0:
throw new ArgumentException("Length of stack frames is 0.");
case 1:
case 2:
stackFrame = frames[frames.Length - 1];
break;
default:
stackFrame = stackTrace.GetFrame(2);
break;
}
Type declaringType = stackFrame.GetMethod()
.DeclaringType;
return declaringType == null ? LogManager.GetCurrentClassLogger() : LogManager.GetLogger(declaringType.FullName);
}
But sadly, the Stacktrace with MEF is very long and i cannot clearly identify the correct caller for the Requester of the ILogger.
So, instead of injecting the ILogger Interface via Constructor Injection, i have created a ILogFactory Interface, that can get injected via Constructor Injection and call then the Create Method on the Factory
public interface ILogFactory
{
#region Public Methods and Operators
/// <summary>
/// Creates a logger with the Callsite of the given Type
/// </summary>
/// <example>
/// factory.Create(GetType());
/// </example>
/// <param name="type">The type.</param>
/// <returns></returns>
ILogger Create(Type type);
#endregion
}
And implemented it:
using System;
using System.ComponentModel.Composition;
[Export(typeof(ILogFactory))]
[PartCreationPolicy(CreationPolicy.Shared)]
public class LogFactory : ILogFactory
{
#region Public Methods and Operators
public ILogger Create(Type type)
{
var logger = new Logger().CreateLogger(type);
return logger;
}
#endregion
}
With the ILogger:
public interface ILogger
{
#region Public Properties
bool IsDebugEnabled { get; }
bool IsErrorEnabled { get; }
bool IsFatalEnabled { get; }
bool IsInfoEnabled { get; }
bool IsTraceEnabled { get; }
bool IsWarnEnabled { get; }
#endregion
#region Public Methods and Operators
void Debug(Exception exception);
void Debug(string format, params object[] args);
void Debug(Exception exception, string format, params object[] args);
void Error(Exception exception);
void Error(string format, params object[] args);
void Error(Exception exception, string format, params object[] args);
void Fatal(Exception exception);
void Fatal(string format, params object[] args);
void Fatal(Exception exception, string format, params object[] args);
void Info(Exception exception);
void Info(string format, params object[] args);
void Info(Exception exception, string format, params object[] args);
void Trace(Exception exception);
void Trace(string format, params object[] args);
void Trace(Exception exception, string format, params object[] args);
void Warn(Exception exception);
void Warn(string format, params object[] args);
void Warn(Exception exception, string format, params object[] args);
#endregion
}
and Implementation of:
using System;
using NLog;
using NLog.Config;
/// <summary>
/// The logging service.
/// </summary>
public class Logger : NLog.Logger, ILogger
{
#region Fields
private string _loggerName;
#endregion
#region Public Methods and Operators
/// <summary>
/// The get logging service.
/// </summary>
/// <returns>
/// The <see cref="ILogger" />.
/// </returns>
public ILogger CreateLogger(Type type)
{
if (type == null) throw new ArgumentNullException("type");
_loggerName = type.FullName;
var logger = (ILogger)LogManager.GetLogger(_loggerName, typeof(Logger));
return logger;
}
To use it... just inject the ILogFactory and calle the Create Method in a Mefed Importing Constructor:
[ImportingConstructor]
public MyConstructor(
ILogFactory logFactory)
{
_logger = logFactory.Create(GetType());
}
hope this helps
If you create a new ExportProvider and cast the ImportDefinition being passed in to a ICompositionElement. You can get the type that the logger is being injected into.
Here is the ExportProvider
public class LoggerExportProvider : ExportProvider
{
private readonly ExportDefinition _loggerExportDefinition;
private readonly Func<string, ILogger> _loggerFactory;
/// <summary>
/// Initializes a new instance of the <see cref="LoggerExportProvider"/> class.
/// </summary>
/// <param name="loggerFactory">The logger factory function.</param>
public LoggerExportProvider(Func<string, ILogger> loggerFactory)
{
_loggerFactory = loggerFactory;
_loggerExportDefinition = new ExportDefinition(typeof (ILogger).FullName, new Dictionary<string, object> {{"ExportTypeIdentity", typeof (ILogger).FullName}});
}
protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition)
{
IList<Export> exports = new List<Export>();
var compositionElement = definition as ICompositionElement;
if (compositionElement == null || compositionElement.Origin == null)
return exports;
var constraint = definition.Constraint.Compile();
if (constraint(_loggerExportDefinition))
exports.Add(new Export(_loggerExportDefinition, () => _loggerFactory(compositionElement.Origin.DisplayName)));
return exports;
}
}
This is setup in such a way that it will work with any logging framework as you need to pass in a function that will return an ILogger (the Ilogger is our own, you'll have to create your own interface or just make it specific to Nlog). The string being passed to the function is the full class name that the type is being injected too. (compositionElement.Origin.DisplayName)
An example of bootstrapping MEF with this would look like this:
public class Example
{
[Import]
public ILogger Logger { get; set;}
public Example()
{
var aggregatecatalogue = new AggregateCatalog();
aggregatecatalogue.Catalogs.Add(new AssemblyCatalog(typeof (ILogger).Assembly));
aggregatecatalogue.Catalogs.Add(new AssemblyCatalog(GetType().Assembly));
var container = new CompositionContainer(aggregatecatalogue, new LoggerExportProvider(s => new MockLogger(s)));
container.ComposeParts(this);
}
}
The code above was copied from a unit test, so I'm just add specific assemblies instead of parsing a directory. The MockLogger is an implementation of the ILogger interface that takes the logging class name (or injecting type) as a parameter to it's constructor.
This doesn't require parsing any stack traces and pulls the information that is otherwise sitting there directly out of MEF.

Resources