Audit for OneToOne attributes - eclipselink

Using Glassfish 3.1.2 and eclipselink 2.2.0.
I have to track changes for following entity:
#Entity
#EntityListeners({AuditListener.class})
#Customizer(AuditListener.class)
public class Client extends Person {
...
#OneToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private ConsumptionRoomAndPost consumptionRoomAndPost;
...
#OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private List<Document> documentList;
...
AuditListener:
public class AuditListener extends DescriptorEventAdapter implements DescriptorCustomizer {
...
#Override
public void postMerge(DescriptorEvent event) {
if (event.getChangeSet() != null) {
...
}
}
}
This works for a Document list, changeSet is not empty,
but not for ConsumptionRoomAndPost. The changeSet is empty.
Of course I can add own listener ConsumptionRoomAndPostAuditListener for ConsumptionRoomAndPost but for a audit i need a client information and than I have a problem to provide this client information to a ConsumptionRoomAndPostAuditListener.

Solved.
After changing relationships to bidirectional, eclipse link tracks changes for all attributes.
I have added to entity ConsumptionRoomAndPost:
public class ConsumptionRoomAndPost {
...
#OneToOne(mappedBy = "consumptionRoomAndPost", cascade = CascadeType.ALL)
private Client client;
...
}
its all.

Related

SpringBoot serialization issue using TestRestTemplate

I have a simple web controller to return an User entity.
This entity has a property nonEditableProperty that cannot be updated.
It's works fine on the web controller, the nonEditableProperty value is listed but on the UserControllerTest it doesn't work and the returned value is always null.
The annotation #JsonProperty(access = JsonProperty.Access.READ_ONLY) seems to be ignored during the test serialization.
Does anyone have any clue for this issue?
Should I load some Jackson configuration for the tests?
#Getter
#Entity
class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
private String nonEditableProperty;
User() {
}
public User(String name, String nonEditableProperty) {
this.name = name;
this.nonEditableProperty = nonEditableProperty;
}
}
#RestController
#AllArgsConstructor
#RequestMapping("users")
public class UserController {
private final UserRepository userRepository;
#GetMapping
public Collection<User> getAllUsers() {
return (Collection<User>) userRepository.findAll();
}
#GetMapping(path = "/{id}")
public User getUser(#PathVariable Integer id) {
return userRepository.findOne(id);
}
}
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UserControllerTest {
#Autowired
TestRestTemplate testRestTemplate;
#Test
public void getUserShouldReturnData() {
ResponseEntity<User> response = testRestTemplate.getForEntity("/users/{id}", User.class, 1);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody().getName()).isEqualTo("Muhammed SuiƧmez");
assertThat(response.getBody().getNonEditableProperty()).isEqualTo("Non editable property");
}
}
testRestTemplate.getForEntity(URI url, Class<T> responseType) Fetches the api response by hitting url and then converts response to the type given by responseType
Though the API response fetched by hitting URL received the nonEditableProperty value (parse inputMessage.getBody here), While deserializing it to responseType the value was lost, because of READ_ONLY Jackson property.

eclipselink/jpa inheritance problems

I am using spring boot (2.0.0) with eclipse link to persist data (over 500 entity classes) to a postgres db (6.5). Thats works very well. For receiving the data over REST I build an other spring boot application. Here I have some inheriance problem with JPA (sorry for my drawing):
Class C and class D (abstract) inherit from class B. Class A have a reference (attribute1) to class B. This attribute is an instance of entity class E, which inherit from abstract class D. I am using inheritance strategy table per class. Every class using the annotation Entity with the table name. In the database, table from class A have a correct foreign key to table from class E, but if I want to read the data the attribute1 is null. I see from the log level that eclipse link only look inside table from class C. How can I resolve this problem?
Greets Benjamin
here are the classes, class E:
#Entity(name="ep_core_voltagelevel")
public class VoltageLevel extends EquipmentContainer {
#Embedded
#AttributeOverrides(#AttributeOverride(name="value", column=#Column(name="highVoltageLimit_value")
)
)
private myPackage.DomainProfile.Voltage highVoltageLimit;
public myPackage.DomainProfile.Voltage getHighVoltageLimit() {
return highVoltageLimit;
}
public void setHighVoltageLimit(myPackage.DomainProfile.Voltage parameter) {
this.highVoltageLimit = parameter;
}
#Embedded
#AttributeOverrides(#AttributeOverride(name="value", column=#Column(name="lowVoltageLimit_value")
)
)
private myPackage.DomainProfile.Voltage lowVoltageLimit;
public myPackage.DomainProfile.Voltage getLowVoltageLimit() {
return lowVoltageLimit;
}
public void setLowVoltageLimit(myPackage.DomainProfile.Voltage parameter) {
this.lowVoltageLimit = parameter;
}
#ManyToOne(cascade={CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinColumn(nullable=false, name="basevoltage_id")
private BaseVoltage baseVoltage;
public BaseVoltage getBaseVoltage() {
return baseVoltage;
}
public void setBaseVoltage(BaseVoltage parameter) {
this.baseVoltage = parameter;
}
#ManyToOne(cascade={CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinColumn(nullable=false, name="substation_id")
private Substation substation;
public Substation getSubstation() {
return substation;
}
public void setSubstation(Substation parameter) {
this.substation = parameter;
}
}
Class D:
#Entity(name = "ep_core_equipmentcontainer")
public abstract class EquipmentContainer extends ConnectivityNodeContainer {
}
Class B:
#Entity(name="ep_core_connectivitynodecontainer")
public abstract class ConnectivityNodeContainer extends PowerSystemResource {
}
Class A:
public class ConnectivityNode extends IdentifiedObject {
#ManyToOne(cascade={CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinColumn(nullable=false, name="connectivitynodecontainer_id")
private ConnectivityNodeContainer connectivityNodeContainer;
public ConnectivityNodeContainer getConnectivityNodeContainer() {
return connectivityNodeContainer;
}
public void setConnectivityNodeContainer(ConnectivityNodeContainer parameter) {
this.connectivityNodeContainer = parameter;
}
}

Is it possible to get previous entry in #HandleAfterSave event with Spring Data REST?

I have next event handler:
#Component
#RepositoryEventHandler
public class EntityEventHandler {
private final EntityService entityService;
#HandleAfterSave
public void handleAfterSave(final Entity entity) {
// need old entity here to validate if specific field has changed
}
}
Is there any way to get old entity in handleAfterSave ?

How to write data to the Orchard CMS repository from a non HTTP thread

I have an Orchard CMS module that loads up some code which provides service functions. The service code is written to be host agnostic and has been used with ASP.NET and WCF previously. The service code uses MEF to load plugins. One such plugin is for audit.
In an attempt to allow access to the Orchard database for audit I have modified the service code to also allow the host to pass in an audit implementation instance. Thus my Orchard module can pass in an instance when the service starts with the intention that this instance writes audit data as records in the Orchard DB.
I have created a migration for my database:
public int UpdateFrom5()
{
SchemaBuilder.CreateTable("AuditRecord",
table => table
.Column<int>("Id", c => c.PrimaryKey().Identity())
.Column<int>("AuditPoint")
.Column<DateTime>("EventTime")
.Column("CampaignId", DbType.Guid)
.Column("CallId", DbType.Guid)
.Column<String>("Data")
);
return 6;
}
I have created my AuditRecord model in Models:
namespace MyModule.Models
{
public class AuditRecord
{
public virtual int Id { get; set; }
public virtual int AuditPoint { get; set; }
public virtual DateTime EventTime { get; set; }
public virtual Guid CampaignId { get; set; }
public virtual Guid CallId { get; set; }
public virtual String Data { get; set; }
}
}
I have added an IAuditWriter interface that derives from IDependency so that I can inject a new instance when my module starts.
public interface IAuditWriter : IDependency
{
void WriteAuditRecord(AuditRecord data);
}
For my audit writer instance to work with the existing service code it must be derived from an abstract class FlowSinkAudit defined in the service library. The abstract class defines the Audit method. When the service needs to write audit it calls the audit method on all instances derived from the FlowAuditSink abstract class that have been instantiated either through MEF or by passing in an instance at startup.
public class AuditWriter : FlowAuditSink, IAuditWriter
{
private readonly IComponentContext ctx;
private readonly IRepository<AuditRecord> repo;
public AuditWriter(IComponentContext ctx, IRepository<AuditRecord> repo)
{
this.ctx = ctx;
this.repo = repo;
}
public void WriteAuditRecord(AuditRecord data)
{
// Get an audit repo
//IRepository<AuditRecord> repo = (IRepository<AuditRecord>)ctx.Resolve(typeof(IRepository<AuditRecord>));
using (System.Transactions.TransactionScope t = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Suppress))
{
this.repo.Create(data);
}
}
public override void Audit(DateTime eventTime, AuditPoint auditPoint, Guid campaignId, Guid callId, IDictionary<String, Object> auditPointData)
{
// Add code here to write audit into the Orchard DB.
AuditRecord ar = new AuditRecord();
ar.AuditPoint = (int)auditPoint;
ar.EventTime = eventTime;
ar.CampaignId = campaignId;
ar.CallId = callId;
ar.Data = auditPointData.AsString();
WriteAuditRecord(ar);
}
}
My service code is started from a module level class that implements IOrchardShellEvents
public class Module : IOrchardShellEvents
{
private readonly IAuditWriter audit;
private readonly IRepository<ServiceSettingsPartRecord> settingsRepository;
private readonly IScheduledTaskManager taskManager;
private static readonly Object syncObject = new object();
public ILogger logger { get; set; }
public Module(IScheduledTaskManager taskManager, IRepository<ServiceSettingsPartRecord> settingsRepository, IAuditWriter audit)
{
this.audit = audit;
this.settingsRepository = settingsRepository;
this.taskManager = taskManager;
logger = NullLogger.Instance;
}
...
When the service is started during the "Activated" event, I pass this.Audit to the service instance.
public void Activated()
{
lock (syncObject)
{
var settings = settingsRepository.Fetch(f => f.StorageProvider != null).FirstOrDefault();
InitialiseServer();
// Auto start the server
if (!StartServer(settings))
{
// Auto start failed, setup a scheduled task to retry
var tasks = taskManager.GetTasks(ServerAutostartTask.TaskType);
if (tasks == null || tasks.Count() == 0)
taskManager.CreateTask(ServerAutostartTask.TaskType, DateTime.Now + TimeSpan.FromSeconds(60), null);
}
}
}
...
private void InitialiseServer()
{
if (!Server.IsInitialized)
{
var systemFolder = #"C:\Scratch\Plugins";
if (!Directory.Exists(systemFolder))
Directory.CreateDirectory(systemFolder);
var cacheFolder = System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/MyModule/Cache");
if (!Directory.Exists(cacheFolder))
Directory.CreateDirectory(cacheFolder);
Server.Initialise(systemFolder, cacheFolder, null, (FlowAuditSink)audit);
}
}
All of this works as expected and my service code calls the audit sink.
My problem is that when the audit sink is called and I try to write the audit to the database using this.repo.Create(data) nothing is written.
I have also attempted to create a new repository object by using the IComponentContext interface but this errors with object already disposed. I assume this is because the audit sink is a long lived object instance.
I have attempted both with and without the current transaction suspended which doesn't affect the result. I assume this is because the call is not coming through ASP.NET MVC but from a thread created by the service code.
Can anyone tell my how I can get my audit data to appear in the Orchard database?
Thanks
Chris.
Well, I have a solution, but as I'm not very familiar with Orchards architecture it may not be the best way.
After a good deal of delving into the Orchard sources it struck me that the crux of this issue can be summarised as
"how do I access the Orchard autofac injection mechanism from a thread that does not use the Http request pipeline".
I figured that this is what a scheduled task must do so I created a scheduled task and set a breakpoint in IScheduledTaskHandler.Process to discover how the task was executed. Looking at Orchard\Tasks\SweepGenerator.cs showed me the way.
I modified my AuditWriter thusly:
public interface IAuditWriter : ISingletonDependency
{
}
public class AuditWriter : FlowAuditSink, IAuditWriter
{
private readonly IWorkContextAccessor _workContextAccessor;
public AuditWriter(IWorkContextAccessor workContextAccessor)
{
_workContextAccessor = workContextAccessor;
}
public override void Audit(DateTime eventTime, AuditPoint auditPoint, Guid campaignId, Guid callId, IDictionary<String, Object> auditPointData)
{
// Add code here to write audit into the Orchard DB.
AuditRecord ar = new AuditRecord();
ar.AuditPoint = (int)auditPoint;
ar.EventTime = eventTime;
ar.CampaignId = campaignId;
ar.CallId = callId;
ar.Data = auditPointData.AsString();
using (var scope = _workContextAccessor.CreateWorkContextScope())
{
// resolve the manager and invoke it
var repo = scope.Resolve<IRepository<AuditRecord>>();
repo.Create(ar);
repo.Flush();
}
}
}
scope.Resolve works and my data is successfully written to the Orchard DB.
At the moment, I don't think my use of ISingletonDependency is working correctly as my constructor is only called when my module injects an AuditWriter instance in its constructor and it happens more than once.
Anyway it seems that to gain access to the Orchard autofac resolution mechanism from a non Http thread we use IWorkContextAccessor
Please let me know if this is not correct.

Is there a reliable way to scope Ninject bound services to an NServicebus message handler?

I want to bind my Entity Framework context to be scoped per NServicebus message. Would the following code successfully do that?
Bind<IDbContext>().To<MyContext>()
.InScope(x => x.Kernel.Get<IBus>().CurrentMessageContext.Id);
Background
I have a NServicebus service that has several IMessageHandlers that read IEvents off an MSMQ Queue.
Each handler converts the message and saves it to a MS SQL Database by way of a particular IRepository sitting over an Entity Framework context.
The repositories needed by each handler are injected via ninject using NServicebus.ObjectBuilder.Ninject
public class Product
{
public string Code { get; set; }
public Category Category { get; set; }
}
public class Category
{
public string Code { get; set; }
}
public class SampleContext : IDbContext
{
IDbSet<Product> Products { get; }
IDbSet<Category> Categories{ get; }
}
public class ProductRepository : IProductRepository
{
private IDbContext _context;
public ProductRepository(IDbContext ctx) { _context = ctx; }
public void Add(Product p)
{
_context.Products.Add(p);
_context.SaveChanges();
}
}
public class CategoryRepository : ICategoryRepository
{
private IDbContext _context;
public CategoryRepository (IDbContext ctx) { _context = ctx; }
public Category GetByCode(string code)
{
return _context.Categories.FirstOrDefault(x => x.Code == code);
}
}
public class AddProductMessageHandler : IMessageHandler<IAddProductEvent>
{
private IProductRepository _products;
private ICategoryRepository _categories;
public AddProductMessageHandler(IProductRepository p, ICategoryRepository c)
{
_products = p;
_categories = c;
}
public void Handle(IAddProductEvent e)
{
var p = new Product();
p.Code = e.ProductCode;
p.Category = _categories.GetByCode(e.CategoryCode);
_products.Add(p);
}
}
Issue
If the EF context is bound in Transient scope (default) then each bound repository in the handler has it's own instance of the context.
Bind<IDbContext>().To<SampleContext>();
This causes issues if I load an object from one repository and then save it via another.
Likewise, if it's bound in Singleton scope, then the same context is used by all repositories, but then it slowly fills up with tracked changes and goobles up all my ram (and gets slower and slower to boot).
Bind<IDbContext>().To<SampleContext>().InSingletonScope();
Question
Ideally I would like each message handler to have 1 EF context that all required repositories (of that handler) use to load and save entities.
Is scoping the context to the current messages Id property a safe/reliable/good way of doing this?
Bind<IDbContext>().To<SampleContext>()
.InScope(x => x.Kernel.Get<IBus>().CurrentMessageContext.Id);
See my blogpost here which describes the scoping apart from NSB 4.0
http://www.planetgeek.ch/2013/01/16/nservicebus-unitofworkscope-with-ninject/
If you have 3.0 you can look into the current develop branch and port the extension methods to your code. You only have to change the scope name.
I'm not familiar with EF Context so please disregard if the answer below does not make any sense.
If EF Context is similar to a NH ISession, then I think the better option is to use a UoW the same way as NH implementation.
You can read more about UoW here.