Using: Spring 3.2 portlet MVC with Liferay 5.2.3 and Tomcat 6.0.18
I'm trying to create a PropertyEditor to convert between Set<Type> and String, and back again. I have successfully got Set<Type> to String to work without problems. But I can't get property editor to be recognized for the reverse direction. I've done this successfully with Type -> String -> Type, but doing the conversion for a Set is eluding me.
I have determined that the SetAsText method is never invoked, and I get a runtime error that shows that the conversion wasn't done. Information about propertyEditors is very sparse, and with one or two exceptions the only vaguely related issues I could find are 4 or more years old.
Either I'm missing something so fundamental that I can't see it, or it's something deeper in the framework, but I would be grateful for any help or suggestions.
Here's the #InitBinder snippet from my controller:
#InitBinder("formBacking")
public void initBinder(WebDataBinder binder){
binder.registerCustomEditor(Set.class, "field", new SetEditor(listService, Set.class));
logger.info("FormBacking Binder initialization");
}
Here's my PropertyEditor:
public class SetEditor extends PropertyEditorSupport {
protected static Logger logger = Logger.getLogger("PropertyEditor");
private ListService listService;
public SetEditor(ListService listService, Class clazz) {
super(clazz);
this.listService = listService;
}
#Override
public String getAsText() {
Stack<String> returnString = new Stack<String>();
Set<Type> types = new HashSet<Type>();
try {
types = (Set<Type>)this.getValue();
for (Type type:types) {
returnString.push(type.getTypeId().toString());
}
} catch (NullPointerException e) {
logger.info("getAsText is \"\"");
return "";
} catch (Exception e) {
logger.info("getAsText Other Exception: " + e.getMessage());
return "";
}
return "[" + StringUtils.collectionToDelimitedString(returnString,",") + "]"; // a very useful Spring Util
}
#Override
public void setAsText(String text) throws IllegalArgumentException {
Type type = new Type();
Set<Type> result = new HashSet<Type>();
try {
String[] typeArray = text.split("[,]"); //this may not be correct, but I can't get here to debug it!!
for(String type:typeArray) {
if(!type.isEmpty()) {
type = listService.getType(Long.valueOf(text));
result.add(type);
}
}
}catch(NullPointerException e) {
logger.info("SetAsText is \"\" ");
setValue(null);
}
catch(Exception e) {
logger.info("setAsText Other Exception: " + e.getMessage());
}
setValue(result);
}
}
the Type class snippet is:
#Entity(name="Type")
#Table(name="type")
public class Type {
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "playListImages",
joinColumns={#JoinColumn(name="superTypeId")},
inverseJoinColumns={#JoinColumn(name="typeId")})
private Set<Type> types = new HashSet<Type>();
getters and setters...
Hopefully this will help any one else who has struggled to resolve this issue.
After a bit of further investigation (i.e. turning on Trace logging), it appears that Spring Annotations doesn't fully register PropertyEditors that are associated with Set (maybe Collections in general, although I haven't verified that). Trace showed the built-in CustomCollectionEditor being invoked (but only on String -> Type conversions), even though I had registered my own editor.
This, imho, is a Spring Annotations bug. The work around, which fully works as I expected, is to create your own property editor registrar and register the property editor in the xxx-portlet.xml configuration file.
For example: project-portlet.xml should include something along these lines:
<bean class="org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="webBindingInitializer">
<bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="propertyEditorRegistrars">
<list>
<ref bean="myPropertyEditorRegistrar" />
</list>
</property>
</bean>
</property>
</bean>
<bean id="myPropertyEditorRegistrar"
class="com.sbeko.slider.util.MyPropertyEditorRegistrar" />
Registration class:
public class MyPropertyEditorRegistrar implements PropertyEditorRegistrar {
public void registerCustomEditors(PropertyEditorRegistry registry)
{registry.registerCustomEditor(Set.class, new TypeEditor(Set.class));
}
Related
I am configuring logging for my application and for logging I am using log4net and castle windsor for DI.
I want logging framework to be wrap inside custom implementation so it can be changed in future.
public interface ICustomLogger
{
void Debug(object message, Exception ex = null);
void Info(object message, Exception ex = null);
void Warn(object message, Exception ex = null);
void Error(object message, Exception ex = null);
void Fatal(object message, Exception ex = null);
}
public class CustomLogger : ICustomLogger
{
private readonly log4net.ILog _log;
private readonly log4net.ILog _log1;
public CustomLogger()
{
//approach1
var stack = new StackTrace();
var frame = stack.GetFrame(1);
var method = frame.GetMethod();
Type type = method.DeclaringType;
_log = log4net.LogManager.GetLogger(type);
//approach2
var dtype = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType;
_log1 = log4net.LogManager.GetLogger(dtype);
}
public CustomLogger(string name)
{
_log = log4net.LogManager.GetLogger(name);
}
public CustomLogger(Type type)
{
_log = log4net.LogManager.GetLogger(type);
}
public void Debug(object message, Exception ex = null)
{
if (_log.IsDebugEnabled)
{
if (ex == null)
{
_log.Debug(message);
}
else
{
_log.Debug(message, ex);
}
}
}
public void Info(object message, Exception ex = null)
{
if (_log.IsInfoEnabled)
{
if (ex == null)
{
_log.Info(message);
}
else
{
_log.Info(message, ex);
}
}
}
public void Warn(object message, Exception ex = null)
{
if (_log.IsWarnEnabled)
{
if (ex == null)
{
_log.Warn(message);
}
else
{
_log.Warn(message, ex);
}
}
}
public void Error(object message, Exception ex = null)
{
if (_log.IsErrorEnabled)
{
if (ex == null)
{
_log.Error(message);
}
else
{
_log.Error(message, ex);
}
}
}
public void Fatal(object message, Exception ex = null)
{
if (_log.IsFatalEnabled)
{
if (ex == null)
{
_log.Fatal(message);
}
else
{
_log.Fatal(message, ex);
}
}
}
}
To register this custom implementation with DI...
container.Register(Component.For<ICustomLogger>()
.ImplementedBy<CustomLogger>()
.LifeStyle.Transient);
Problem comes when I ask DI to resolve logger, then it always return logger for Customlogger type not the class where I want to use it.
class ABC
{
ICustomLogger _logger;
public ABC(ICustomLogger logger)
{
_logger = logger; // type of this logger is CustomLogger not ABC
}
}
Both the approach are not working to resolve logger as ABC.
Can anyone help me to understand what's wrong here and how to fix the issue.
You can do this via a custom dependency resolver.
You first need to create an implementation of ISubDependencyResolver that can resolve dependencies of type ICustomLogger:
public class LoggerResolver : ISubDependencyResolver
{
public bool CanResolve(
CreationContext context,
ISubDependencyResolver contextHandlerResolver,
ComponentModel model,
DependencyModel dependency)
{
//We can only handle dependencies of type ICustomLogger
return dependency.TargetType == typeof (ICustomLogger);
}
public object Resolve(
CreationContext context,
ISubDependencyResolver contextHandlerResolver,
ComponentModel model,
DependencyModel dependency)
{
//We pass the requested type, e.g. ABC, to the constructor of CustomLogger
return new CustomLogger(context.RequestedType);
}
}
You then need to register this resolver with the container like this:
container.Kernel.Resolver.AddSubResolver(new LoggerResolver());
For your specific question - in both approaches you never really leave the "scope" of your class. With the first you are creating a new StackTrace and in the other the declaring type of a constructor is that class itself.
But you implemented a constructor that can receive a type so why not use it. Currently your CustomLogger is registered with your default constructor:
//There is no place here that you tell castle to resolve using the constructor
//that receives `ABS`
container.Register(Component.For<ICustomLogger>()
.ImplementedBy<CustomLogger>()
.LifeStyle.Transient);
See Castle Windsor passing constructor parameters to understand how to pass the parameters and that way invoke the constructor you want
In addition - Worth re-thinking:
Though it is a good idea to create such abstraction between your code and external source in this case I would not do it and I will explain why:
From my experience one doesn't really change the logging framework after the code is up and running. Especially since you are working with a mature and excellent framework - Log4Net. It has many built in abilities and is very adaptable for ones needs: From different formatting of the messaged to outputting the logs to different sources such as databases, files and if I'm not wrong there are also appenders for things like elastic search.
You are using Castle Windsor which has a good integration with Log4Net and has for you a ready made Logging Facility to Log4Net. See this question for how simple it is to add it.
Last point is that if you already write good SOLID code and pass your logger as ILogger to all the components (and not a specific implementation) all they will probably do is call the different Debug/Info/Warn/Error/Fatal methods - which any other mature logging framework will have. So on the day you will have to change (which I think won't happen) you can write an interface that looks like the Log4Net's interface and an implementation that will adapt that to your new logging framework.
I am just following the code examples of a Beginning SilverLight book and here is part of the code about user controls and Dependeny Property that I have typed from the book into my IDE:
public class CoolDownButtonControl: Control
{
public static readonly DependencyProperty CoolDownSecondsProperty =
DependencyProperty.Register(
"CoolDownSeconds",
typeof(int),
typeof(CoolDownButtonControl),
new PropertyMetadata(
new PropertyChangedCallback(
CoolDownButtonControl.OnCoolDownSecondsPropertyChanged
)
)
);
public int CoolDownSeconds
{
get
{
return (int)GetValue(CoolDownSecondsProperty);
}
set
{
SetValue(CoolDownSecondsProperty, value);
}
}
private static void OnCoolDownSecondsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CoolDownButtonControl cdBuutton = d as CoolDownButtonControl;
cdBuutton.OnCoolDownButtonChange(null);
}
}
The problem is that IDE highlights the line of cdBuutton.OnCoolDownButtonChange(null); complaining about
CoolDownButtonControl does not contain a definition for
OnCoolDownButtonChange
As I am new to this and hoping to learn it from this example I couldn't figure out what is wrong and how to fix it?
You should add that method too, something like this:
protected virtual void OnCoolDownButtonChange(RoutedEventArgs e)
{
}
My NetDataContractSerializer seems to be confused: The end of the XML appears twice:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns:i="http://www.w3.org/2001/XMLSchema-instance" z:Id="1"
[...]
<d2p1:anyType i:nil="true" />
</d2p1:_items>
<d2p1:_size>2</d2p1:_size>
<d2p1:_version>2</d2p1:_version>
</d2p1:items>
</ProjectParts>
<ProjectPath z:Id="31">D:\t10\</ProjectPath>
</Project>ze>
<d2p1:_version>3</d2p1:_version>
</d2p1:items>
<d2p1:_monitor xmlns:d7p1="http://schemas.datacontract.org/2004/07/System.Collections.ObjectModel" z:Id="33">
<d7p1:_busyCount>0</d7p1:_busyCount>
</d2p1:_monitor>
</Elements>
<Project z:Ref="1" i:nil="true" xmlns="http://schemas.datacontract.org/2004/07/Modules.WorkspaceManager.Types" />
</d2p1:anyType>
<d2p1:anyType i:nil="true" />
<d2p1:anyType i:nil="true" />
</d2p1:_items>
<d2p1:_size>2</d2p1:_size>
<d2p1:_version>2</d2p1:_version>
</d2p1:items>
</ProjectParts>
<ProjectPath z:Id="34">D:\t10\</ProjectPath>
</Project>
As you can see, there is some serious stammering going on. It happens occasionally and I can't reproduce the error. Any ideas? Could it be caused by the file being opened in VS while it's being written?
I serialize my object like this:
private void SerializeToFile(object objectToSerialize)
{
Stream stream = null;
try
{
stream = File.Open(_fileName, FileMode.OpenOrCreate, FileAccess.Write);
using (var writer = XmlWriter.Create(stream, new XmlWriterSettings { Indent = true }))
{
NetDataContractSerializer serializer = new NetDataContractSerializer();
serializer.WriteObject(writer, objectToSerialize);
}
}
finally
{
if (stream != null) stream.Close();
}
}
And the class serialized looks like this:
[DataContract(IsReference = true)]
public class Project : IProject
{
[DataMember] public string ProjectPath { get; set; }
[DataMember] public string ProjectName { get; set; }
[DataMember] public Collection<IProjectPart> ProjectParts { get; set; }
public T GetPart<T>() where T : IProjectPart
{
return ProjectParts.OfType<T>().First();
}
public void RegisterPart<T>(T part) where T : IProjectPart
{
if (ProjectParts.Any(p => p.GetType().IsInstanceOfType(part))) throw new InvalidOperationException("Part already registered.");
ProjectParts.Add(part);
part.Project = this;
}
public void Load()
{
foreach (var projectPart in ProjectParts)
{
projectPart.Load();
}
}
public void Unload()
{
foreach (var projectPart in ProjectParts)
{
projectPart.Unload();
}
}
public void Save()
{
foreach (var projectPart in ProjectParts)
{
projectPart.Save();
}
}
public Project()
{
ProjectParts = new Collection<IProjectPart>();
}
}
Thank you!
The issue is simple - when you serialize over and over your object, you do it with different size of IProjectPart collection. The File.Open method does not clear the file from previous content so assume following steps :
i) serialize object with two IProjectPart instaces - let's say it will take 10 lines of xml file
ii) serialize object again with one IProjectPart instance in the collection - this time it will take 8 lines of xml file
iii) lines 9 and 10 will be filled with old xml data since they are not cleared between serialization attempts - so there is some duplicated-trash-looking xml data.
Try it for yourself , you will see exactly how those multiple tags are generated.
NOTE : The 8 and 10 lines are approximate values for my implementation
NOTE 2 : I suggest using using statement for the stream inside serialization method(as for all IDisposable objects) :
private void SerializeToFile(object objectToSerialize)
{
using(var stream = File.Open(_fileName, FileMode.OpenOrCreate, FileAccess.Write))
{
using (var writer = XmlWriter.Create(stream, new XmlWriterSettings { Indent = true }))
{
NetDataContractSerializer serializer = new NetDataContractSerializer();
serializer.WriteObject(writer, objectToSerialize);
}
}
}
I have a service that needs to invoke a runnable class.
Here are the lines of code that are being used in my service.
#Autowired
private LinkBrc2MemberProfile brcTask;
// Background Task.
SimpleAsyncTaskExecutor sate = new SimpleAsyncTaskExecutor();
sate.createThread(new LinkBrc2MemberProfile(user));
Here is my Runnable class
#Service
public class LinkBrc2MemberProfile implements Runnable {
private final Logger log = LoggerFactory.getLogger(LinkBrc2MemberProfile.class);
#Autowired
private LoyaltyDao dao;
private Member member;
public LinkBrc2MemberProfile() {
super();
}
public LinkBrc2MemberProfile(Member member) {
this.member = member;
}
public void run() {
log.debug("*** Member User Name: " + member.getString("USER_NAME"));
String emailAddress = member.getString("USER_NAME");
Map<String, Object> map = dao.findBrcByEmailAddress( emailAddress );
log.debug("==========================================================");
if( ! map.isEmpty() ) {
try {
//a.CUSTOMER_ID, a.EMAIL_ADDRESS, b.card_no
String customerId = (String) map.get("CUSTOMER_ID");
String brcCardNumber = (String) map.get("CARD_NO");
log.debug("\ncustomerId: " + customerId + " brcCardNumber: " + brcCardNumber);
if(!brcCardNumber.equals("")) {
// Add the Be Rewarded Card.
HashMap<String, String> userAttributes = new HashMap<String, String>();
String brcNumber = member.getString("BREWARDED_CARD_NO");
if (brcNumber.equals("")) {
userAttributes.put("BREWARDED_CARD_NO", brcCardNumber);
try {
member.putAll(userAttributes);
} catch (Exception e) {
String errorMessage = "Unable to save user's BRC information due to: " + e.getMessage();
log.error("{}", errorMessage);
}
}
}
} catch (Exception e) {
log.error(e.getMessage());
e.printStackTrace();
}
}
}
I'm not seeing any errors in the log but at the same time it does not appear to be invoking the Runnable class. Am I missing an annotation somewhere? Are there any good examples that you can point me to, the only ones I have found use XML files to configure the runnable class I would like to use annotations. Thanks in Advance.
I've updated my service to do the following.
Please help, my DAO is NULL so it looks like my #Autowired in my Runnable class is not wiring it in.
I've added the following bean to my bean-config.xml file.
<bean id="brcType" class="com.ws.ocp.service.LinkBrc2MemberProfile" scope="prototype"/>
I removed my #Autowired annotation and added the following to my service class.
ClassPathResource rsrc = new ClassPathResource("bean-config.xml");
XmlBeanFactory factory = new XmlBeanFactory(rsrc);
LinkBrc2MemberProfile brcTask = (LinkBrc2MemberProfile) factory.getBean("brcType");
SimpleAsyncTaskExecutor sate = new SimpleAsyncTaskExecutor();
// Set Member attribute
brcTask.setMember(user);
// Executer
sate.execute(brcTask);
Why is my dao still null?
The runnable will throw a NullPointerException, since you create it yourself (using the new operator), instead of letting Spring create it. This obviously means that the autowired DAO attribute won't be autowired, which will lead to a NPE when calling dao.findBrcByEmailAddress(...).
You should get your Runnable instance from the bean factory (as a prototype), set its member attribute, and then submit it to the executor.
To answer your question of how to properly use a Prototype-Bean, this is my favorite way:
#Component
abstract class MyBean {
/* Factory method that will be installed by Spring */
#Lookup
protected abstract YourPrototypeBean createBean();
void someCode() {
YourPrototypeBean bean = createBean();
}
}
Since it's a factory method, you can create as many instances as you like.
Im having some problems with NServiceBus, I can get the pubsub example working fine, but now I'm trying to integrate it into a production project and I cant get the thing to work!
My publisher code is exactly the same as the publisher example (I've just imported the project to rule out any other issues) but I then create a void function and call it from my WPF app and I get a "you cant call bus without creating an instance of bus" error
public void RunTest()
{
var eventMessage = new MarketPriceMessage();
eventMessage.Ticker = "IBM";
eventMessage.DataType = "Bid";
eventMessage.Value = (decimal)23.23423;
eventMessage.EventId = Guid.NewGuid();
eventMessage.Time = DateTime.Now; // > 30 ? (DateTime?)DateTime.Now : null;
eventMessage.Duration = TimeSpan.FromSeconds(99999D);
Bus.Publish(eventMessage);
}
Any ideas as to whats going on there and where I'm going wrong?
Following #Adam's comments below this is the code I'm using internally in my WPF App:
public partial class App : Application
{
public IBus bus { get; set; }
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
NServiceBus.Configure.With()
.Log4Net()
.SpringBuilder()
.XmlSerializer()
.MsmqTransport()
.UnicastBus()
.LoadMessageHandlers()
.CreateBus()
.Start();
}
}
}
and
namespace WpfApplication2
{
class EndpointConfig : IConfigureThisEndpoint, AsA_Publisher { }
}
and
namespace WpfApplication2
{
public class SubscriptionAuthorizer : IAuthorizeSubscriptions
{
public bool AuthorizeSubscribe(string messageType, string clientEndpoint, string clientWindowsIdentity, IDictionary<string, string> headers)
{
return true;
}
public bool AuthorizeUnsubscribe(string messageType, string clientEndpoint, string clientWindowsIdentity, IDictionary<string, string> headers)
{
return true;
}
}
}
App Config
<configuration>
<configSections>
<section name="MsmqTransportConfig" type="NServiceBus.Config.MsmqTransportConfig, NServiceBus.Core"/>
<section name="UnicastBusConfig" type="NServiceBus.Config.UnicastBusConfig, NServiceBus.Core"/>
</configSections>
<MsmqTransportConfig
InputQueue="WpfApplication2InputQueue"
ErrorQueue="error"
NumberOfWorkerThreads="1"
MaxRetries="5"/>
<UnicastBusConfig>
<!--DistributorControlAddress="" DistributorDataAddress="" ForwardReceivedMessagesTo="">-->
<MessageEndpointMappings>
</MessageEndpointMappings>
</UnicastBusConfig>
When I'm stepping through my code I can see that bus is a null object.
I am including the references as normal
I'm not too familiar with WPF, but it looks like there is an Application.Startup event that may work. You need to "manually" configure the bus as shown here in the docs
If you're not using Autofac or some other container, the problem is you skipped the assignment to your bus variable. I normally put this in Global.asax Application_Startup, but this way should work too.
If you are using a container, and you register the class that implements your ServiceContract, you can get away with having a local IBus constructor/property injected when it's instantiated.
public IBus bus { get; set; }
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
bus = NServiceBus.Configure.With() // keep a reference to the returned bus.
.Log4Net()
.SpringBuilder()
.XmlSerializer()
.MsmqTransport()
.UnicastBus()
.LoadMessageHandlers()
.CreateBus()
.Start();
}