I'm creating a sort of a social networking site, like Facebook, as a university project. Users can upload photos, but I'm somehow unable to retrieve the list of photos for a particular user.
Here's how I'm doing it right now:
#Entity
#Table(name = "users")
public class User implements Serializable {
#Id
private String emailAddress;
private String password;
private String firstName;
private String lastName;
(...)
#OneToMany(mappedBy = "owner", fetch = FetchType.EAGER)
private List<Photo> photos;
public User() {
}
(...)
public void addPhoto( Photo photo){
photos.add(photo);
}
public List<Photo> getPhotos() {
return photos;
}
}
And here's the Photo entity:
#Entity
public class Photo implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String url;
private String label;
#ManyToOne
private User owner;
public Photo() {
}
(...)
public User getOwner() {
return owner;
}
}
Each photo is uploaded by creating a post that contains it. Here's the EJB that does it:
#Stateless
public class PublicPost implements PublicPostRemote {
#PersistenceContext
EntityManager em;
#Override
public void createPost(LoginUserRemote loginUserBean, String targetEmail, final String content, final String photoURL) {
if (loginUserBean.isLoggedIn()) {
final User author = loginUserBean.getLoggedUser();
System.out.println(targetEmail);
final User target = em.find(User.class, targetEmail);
if (author != null && target != null) {
//See if there's a photo to post as well
Photo photo = null;
if (photoURL != null) {
photo = new Photo(photoURL, author, content);
em.persist(photo);
}
MessageBoard publicMessageBoard = target.getPublicMessageBoard();
Post post = new Post(author, content);
post.setMessageBoard(publicMessageBoard);
if (photo != null) {
post.setPostPhoto(photo);
}
em.persist(post);
em.refresh(publicMessageBoard);
//Send an e-mail to the target (if the author and the target are different)
if (!author.getEmailAddress().equals(target.getEmailAddress())) {
final String subject = "[PhaseBook] " + author.getEmailAddress() + " has posted on your public message board.";
Thread mailThread = new Thread() {
#Override
public void run() {
try {
GMailSender.sendMessage(target.getEmailAddress(), subject, content);
} catch (MessagingException ex) {
Logger.getLogger(PublicPost.class.getName()).log(Level.SEVERE, null, ex);
}
}
};
mailThread.start();
}
}
}
}
}
So what happens is: I create a new post that contains a photo, yet later, when I use this, on the web tier...
LoginUserRemote lur = (LoginUserRemote)session.getAttribute("loginUserBean");
User user = lur.getLoggedUser();
List<Photo> photos = user.getPhotos();
System.out.println();
System.out.println("This user has this many photos: " + photos.size());
...it always tells me that the user has 0 photos. Why is this? Am I defining the relationship between user and photo incorrectly? Am I forgetting to persist/refresh anything? Or does the problem lie somewhere else?
If you store a detached User object (the logged in user) in the HTTP session, and then create and persists photos having this detached user as owner, JPA won't automatically add the photo to the detached user. For the entity manager, this detached user doesn't exist: it's not under its responsibility anymore.
Even if User was still attached, it's your responsibility to maintain the coherence of the object graph. If you modify one side of the association (by setting the user as owner of the photo), you should also modify the other side (by adding the photo to the list of photos of the owner).
I'm not absolutely sure this is the cause of the problem, because you haven't shown us what the loginUserBean was and did to get the logged in user, but it might be the answer.
There is a series of issues here:
Are photos actually stored in the database? Maybe you don't have a transaction open?
You are not updating both sides of the association.
Theoretically you only need to update the owning side, but better be safe than sorry:
photo = new Photo(photoURL, author, content);
em.persist(photo);
author.addPhoto(photo);
You are fetching the user from a session and then retrieving associated collection of photos. Do you really know what this means? If the user has hundreds of photos, do you really want to store them in HTTP session along with the user all the time? This is not how Facebook works ;-).
I think refreshing your entity (with em.refresh(lur.getLoggedUser())) might work, but only at university, not in real life. Loading all the user photos at once into memory is an overkill. Personally I would even remove photos association from user to avoid this. Load one page at a time and only on demand.
Even if you know what you are doing or such a behaviour is acceptable, objects stored in HTTP session are so called detached from persistence context, meaning your persistence provider does no longer keep track of them. So adding a photo does not mean that the photos collection will be magically updated in every object. I think about carefully, this would be even worse.
Last but not least, your createPost() really needs some code review. It does at least 4 things at once, System.out, one time threads created on demand, silently doing nothing when preconditions are not met (like user not being logged in, missing parameters), mixing concerns on different level of abstraction. Don't want to be too meticulous, but your grade might be influenced by the quality of code.
Related
Is it possible to have more than one custom user profile and if it is how to set up web config file and how to manage custom profiles for two website under the same sitecore instance (same VS solution)?
We had one custom user profile and new requirement came about new website under the same sitecore instance but with the new custom user for the second website.
During development of second website we created second custom user profile and everything went fine, we change "inherits" attribute of system.web/profile node in the web.config file to point to second custom use profile and during development it was OK.
The problem now is that only one user profile can log in to the webistes:
if we set inherits attribute to "Namespace.Website.NamespaceA.CustomProfileA, Namespace.Website" only profileA will be able to log in to their domain and if we set it to "Namespace.Website.NamespaceB.CustomProfileB, Namespace.Website" only profileB will be able to login to its domain because the switcher will use this one.
All articles in the web describe how to set custom user profile, switcher and switchingProviders for just one custom user profile but there are no examples for my case.
Thanks,
Srdjan
Unfortunately, there does not seem to be a clean way to have multiple user profile classes created for you by the API. Typically, you will get the user profile via Sitecore.Context.User.Profile. The Context class is static and the methods that initialize the Profile property are private, so there's nowhere to insert your extra logic.
You could, however, create wrapper classes for the Profile. Start with a base class like this:
public abstract class CustomProfileBase
{
public CustomProfileBase(Sitecore.Security.UserProfile innerProfile)
{
Assert.ArgumentNotNull(innerProfile, nameof(innerProfile));
InnerProfile = innerProfile;
}
public Sitecore.Security.UserProfile InnerProfile { get; protected set; }
public virtual string GetCustomProperty(string propertyName)
{
return InnerProfile.GetCustomProperty(propertyName);
}
public virtual void SetCustomProperty(string propertyName, string value)
{
InnerProfile.SetCustomProperty(propertyName, value);
}
public virtual void Save()
{
InnerProfile.Save();
}
public virtual string Email
{
get { return InnerProfile.Email; }
set { InnerProfile.Email = value; }
}
// Other members omitted for brevity
}
This CustomProfileBase class would have a member that wraps each of the public members of Sitecore.Security.UserProfile. Then, you would create your site specific profile like this:
public class SiteOneProfile : CustomProfileBase
{
public SiteOneProfile(UserProfile innerProfile) : base(innerProfile)
{
}
public string CustomPropertyOne
{
get { return GetCustomProperty("CustomPropertyOne"); }
set { SetCustomProperty("CustomPropertyOne", value); }
}
}
Then you would use it from a controller or elsewhere like so:
var profile = new SiteOneProfile(Sitecore.Context.User.Profile);
model.property = profile.CustomPropertyOne;
Update
When using this approach, you would just leave the inherits attribute in the config with its default value. Also, the profile should not have an effect on the ability to login. If you are still having issues with that, please update your question with details of the error you get when logging in.
I've created and deployed a plugin for the Update event of a custom entity but it seems when multiple users update different entities within quick succession the plugin uses the first entity it receives for each call.
To investigate further I added NLog via NuGet and at the beginning of the Execute function I generate a Guid and log the entity Id and the Guid. When I look in the log I can see the same ID and Guid logged 3-4 times before both change.
What I think is happening is the code is being run for each user but using the first entities details, applying only to the first entity.
Why is this happening and how can I stop it? The problem is users are saying the plugin is erratic.
Here is my code:
public class OnUpdateClaimSection : IPlugin
{
private static Logger logger = LogManager.GetCurrentClassLogger();
private string logId = Guid.NewGuid().ToString();
public void Execute(IServiceProvider serviceProvider)
{
try
{
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
{
logger.Debug("{0} {1}|{2}|{3}", logId, context.MessageName, context.PrimaryEntityName, Common.GetSystemUserFullName(service, context.UserId));
var entity = context.InputParameters["Target"] as Entity;
logger.Debug("{0} {1}", logId, entity.Id);
var claimSection = GetClaimSection(service, entity.ToEntity<ClaimSection>());
CalculateClaimTotals(service, claimSection);
}
}
catch (Exception ex)
{
logger.Error("{0} Exception : {1}", logId, ex.Message);
throw;
}
}
}
Plugin classes are instantiated once by the CRM platform and are then reused for requests. Therefore you must be very careful when using class field variables, because they are not guaranteed to be thread-safe.
In your example field logId is modified in the Execute method. Race conditions of multiple threads are causing the effects you describe.
I suggest to only use plugin class fields when you have made sure that their implementation is absolutely thread-safe.
I'm currently working on a multi-tenant application that employs Shared DB/Shared Schema approach. IOW, we enforce tenant data segregation by defining a TenantID column on all tables. By convention, all SQL reads/writes must include a Where TenantID = '?' clause. Not an ideal solution, but hindsight is 20/20.
Anyway, since virtually every page/workflow in our app must display tenant specific data, I made the (poor) decision at the project's outset to employ a Singleton to encapsulate the current user credentials (i.e. TenantID and UserID). My thinking at the time was that I didn't want to add a TenantID parameter to each and every method signature in my Data layer.
Here's what the basic pseudo-code looks like:
public class UserIdentity
{
public UserIdentity(int tenantID, int userID)
{
TenantID = tenantID;
UserID = userID;
}
public int TenantID { get; private set; }
public int UserID { get; private set; }
}
public class AuthenticationModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.AuthenticateRequest +=
new EventHandler(context_AuthenticateRequest);
}
private void context_AuthenticateRequest(object sender, EventArgs e)
{
var userIdentity = _authenticationService.AuthenticateUser(sender);
if (userIdentity == null)
{
//authentication failed, so redirect to login page, etc
}
else
{
//put the userIdentity into the HttpContext object so that
//its only valid for the lifetime of a single request
HttpContext.Current.Items["UserIdentity"] = userIdentity;
}
}
}
public static class CurrentUser
{
public static UserIdentity Instance
{
get { return HttpContext.Current.Items["UserIdentity"]; }
}
}
public class WidgetRepository: IWidgetRepository{
public IEnumerable<Widget> ListWidgets(){
var tenantId = CurrentUser.Instance.TenantID;
//call sproc with tenantId parameter
}
}
As you can see, there are several code smells here. This is a singleton, so it's already not unit test friendly. On top of that you have a very tight-coupling between CurrentUser and the HttpContext object. By extension, this also means that I have a reference to System.Web in my Data layer (shudder).
I want to pay down some technical debt this sprint by getting rid of this singleton for the reasons mentioned above. I have a few thoughts on what a better implementation might be, but if anyone has any guidance or lessons learned they could share, I would be much obliged.
CurrentUser isn't quite a singleton. I'm not exactly sure what you'd call it. (A singleton by definition can only exist one at a time, and any number of UserIdentity instances can be created at will by outside code and coexist without any issues.)
Personally, i'd take CurrentUser.Instance and either move it to UserIdentity.CurrentUser, or put it together with whatever similar "get the global instance" methods and properties you have. Gets rid of the CurrentUser class, at least. While you're at it, make the property settable at the same place -- it's already settable, just in an way that (1) would look like magic if the two classes weren't shown right next to each other, and (2) makes changing how the current user identity is set later harder.
Doesn't get rid of the global, but you're not really gonna get around that without passing the UserIdentity to every function that needs it.
Given that document databases, such as RavenDB, are non-relational, how do you avoid duplicating data that multiple documents have in common? How do you maintain that data if it's okay to duplicate it?
With a document database you have to duplicate your data to some degree. What that degree is will depend on your system and use cases.
For example if we have a simple blog and user aggregates we could set them up as:
public class User
{
public string Id { get; set; }
public string Name { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
public class Blog
{
public string Id { get; set; }
public string Title { get; set; }
public class BlogUser
{
public string Id { get; set; }
public string Name { get; set; }
}
}
In this example I have nested a BlogUser class inside the Blog class with the Id and Name properties of the User Aggregate associated with the Blog. I have included these fields as they are the only fields the Blog class is interested in, it doesn't need to know the users username or password when the blog is being displayed.
These nested classes are going to dependant on your systems use cases, so you have to design them carefully, but the general idea is to try and design Aggregates which can be loaded from the database with a single read and they will contain all the data required to display or manipulate them.
This then leads to the question of what happens when the User.Name gets updated.
With most document databases you would have to load all the instances of Blog which belong to the updated User and update the Blog.BlogUser.Name field and save them all back to the database.
Raven is slightly different as it support set functions for updates, so you are able to run a single update against RavenDB which will up date the BlogUser.Name property of the users blogs without you have to load them and update them all individually.
The code for doing the update within RavenDB (the manual way) for all the blog's would be:
public void UpdateBlogUser(User user)
{
var blogs = session.Query<Blog>("blogsByUserId")
.Where(b.BlogUser.Id == user.Id)
.ToList();
foreach(var blog in blogs)
blog.BlogUser.Name == user.Name;
session.SaveChanges()
}
I've added in the SaveChanges just as an example. The RavenDB Client uses the Unit of Work pattern and so this should really happen somewhere outside of this method.
There's no one "right" answer to your question IMHO. It truly depends on how mutable the data you're duplicating is.
Take a look at the RavenDB documentation for lots of answers about document DB design vs. relational, but specifically check out the "Associations Management" section of the Document Structure Design Considerations document. In short, document DBs use the concepts of reference by IDs when they don't want to embed shared data in a document. These IDs are not like FKs, they are entirely up to the application to ensure the integrity of and resolve.
Explanation:
Let's say I have an object graph that's nested several levels deep and each entity has a bi-directional relationship with each other.
A -> B -> C -> D -> E
Or in other words, A has a collection of B and B has a reference back to A, and B has a collection of C and C has a reference back to B, etc...
Now let's say I want to edit some data for an instance ofC. In Winforms, I would use something like this:
var instanceOfC;
using (var session = SessionFactory.OpenSession())
{
// get the instance of C with Id = 3
instanceOfC = session.Linq<C>().Where(x => x.Id == 3);
}
SendToUIAndLetUserUpdateData(instanceOfC);
using (var session = SessionFactory.OpenSession())
{
// re-attach the detached entity and update it
session.Update(instanceOfC);
}
In plain English, we grab a persistent instance out of the database, detach it, give it to the UI layer for editing, then re-attach it and save it back to the database.
Problem:
This works fine for Winform applications because we're using the same entity all throughout, the only difference being that it goes from persistent to detached to persistent again.
The problem is that now I'm using a web service and a browser, sending over JSON data. The entity gets serialized into a string, and de-serialized into a new entity. It's no longer a detached entity, but rather a transient one that just happens to have the same ID as the persistent one (and updated fields). If I use this entity to update, it will wipe out the relationship to B and D because they don't exist in this new transient entity.
Question:
My question is, how do I serialize detached entities over the web to a client, receive them back, and save them, while preserving any relationships that I didn't explicitly change? I know about ISession.SaveOrUpdateCopy and ISession.Merge() (they seem to do the same thing?), but this will still wipe out the relationships if I don't explicitly set them. I could copy the fields from the transient entity to the persistent entity one by one, but this doesn't work too well when it comes to relationships and I'd have to handle version comparisons manually.
I solved this problem by using an intermediate class to hold data coming in from the web service, then copying its properties to the database entity. For example, let's say I have two entities like so:
Entity Classes
public class Album
{
public virtual int Id { get; set; }
public virtual ICollection Photos { get; set; }
}
public class Photo
{
public virtual int Id { get; set; }
public virtual Album Album { get; set; }
public virtual string Name { get; set; }
public virtual string PathToFile { get; set; }
}
Album contains a collection of Photo objects, and Photo has a reference back to the Album it's in, so it's a bidirectional relationship. I then create a PhotoDTO class:
DTO Class
public class PhotoDTO
{
public virtual int Id { get; set; }
public virtual int AlbumId { get; set; }
public virtual string Name { get; set; }
// note that the DTO does not have a PathToFile property
}
Now let's say I have the following Photo stored in the database:
Server Data
new Photo
{
Id = 15,
Name = "Fluffy Kittens",
Album = Session.Load<Album>(3)
};
The client now wants to update the photo's name. They send over the following JSON to the server:
Client Data
PUT http://server/photos/15
{
"id": 15,
"albumid": 3,
"name": "Angry Kittens"
}
The server then deserializes the JSON into a PhotoDTO object. On the server side, we update the Photo like this:
Server Code
var photoDTO = DeserializeJson();
var photoDB = Session.Load(photoDTO.Id); // or use the ID in the URL
// copy the properties from photoDTO to photoDB
photoDB.Name = photoDTO.Name;
photoDB.Album = Session.Load<Album>(photoDTO.AlbumId);
Session.Flush(); // save the changes to the DB
Explanation
This was the best solution I've found because:
You can choose which properties the client is allowed to modify. For example, PhotoDTO doesn't have a PathToFile property, so the client can never modify it.
You can also choose whether to update a property or not. For example, if the client didn't send over an AlbumId, it will be 0. You can check for that and not change the Album if the ID is 0. Likewise, if the user doesn't send over a Name, you can choose not to update that property.
You don't have to worry about the lifecycle of an entity because it will always be retrieved and updated within the scope of a single session.
AutoMapper
I recommend using AutoMapper to automatically copy the properties from the DTO to the entity, especially if your entites have a lot of properties. It saves you the trouble of having to write every property by hand, and has a lot of configurability.