Morphia and MongoDB - Modeling Something like Settings - java

I'm evaluating MongoDB and Morphia right now. How would I model something like 'settings', where there is only one 'record' (I'm not sure of the proper Mongo term to use). Must I override the save method in my entity class? An example of how to do this and how to use it would be awesome.
For example, I'd like to store the home page configuration:
home page settings
show friends list: false
marketing text: "You'll love it here"
main image: main.jpg

If you basically only want a single copy of settings for your application (like a singleton) then I would suggest something like this:
#Entity
class Settings {
#Id int id = 0;
boolean showFriendsList = false;
String marketingText = "You'll love it";
byte[] mainImage = ...;
}
Since the id is set to a single value then when you call save it will always update the single entity. If you call insert, and there is already one there, you will get an error (if you are checking for errors).
You can update the entity using get/change/save or update semantics.
Datastore ds = ...;
//get/change/save
Settings s = ds.find(Settings.class).get(); //like findOne in the shell/driver
s.showFriendsList = true;
ds.save(s);
//or update
ds.updateFirst(ds.find(Settings.class), ds.creatUpdateOperations(Settings.class).set("showFiendsList", true));

Related

Firestore - Use update method for document but with Object (Java)

I want to update a document with a User object that I have, but I do not want the document to be created if it does not exist, and therefore I cannot use "DocumentReference.set" with "SetOptions.Merge()" (to my understanding).
However, according to this post (Difference between set with {merge: true} and update), "update" is actually the command I need. My problem is, it doesn't seem like update accepts a Java object.
I do not want to check whether or not the document exists myself, as this will result in an unnecessary read.
Is there any way around this?
Here is my code (I have removed success and failure listeners for simplicity):
public void saveUser(User user)
{
CollectionReference collection = db.collection("users");
String id = user.getId();
if (id.equals(""))
{
collection.add(user);
}
else
{
// I need to ensure that the ID variable for my user corresponds
// with an existing ID, as I do not want a new ID to be generated by
// my Java code (all IDs should be generated by Firestore auto-ID)
collection.document(ID).set(user);
}
}
It sounds like you:
Want to update an existing document
Are unsure if it already exists
Are unwilling to read the document to see if it exists
If this is the case, simply call update() and let it fail if the document doesn't exist. It won't crash your app. Simply attach an error listener to the task it returns, and decide what you want to do if it fails.
However you will need to construct a Map of fields and values to update using the source object you have. There are no workarounds for that.

xpages: referencing a view from a different database/application

I am very new to xpages. I have been searching the web for an answer to my question for a while now. Seems like the answer should be simple.
I have been playing around with a snippet of code that I got from Brad Balassaitis's excellent Xcellerent.net site that populates a list of "jumptoitems" for a viewpanel dynamically. The code is run from the beforeRenderResponse event of the xpage.
var viewName = getComponent('viewPanel1').getData().getViewName();
var vw = database.getView(viewName);
var colNum = 1;
var cols:Vector = vw.getColumns();
for (var i=0; i < cols.length; i++) {
if (cols[i].isSorted() && !cols[i].isHidden()) {
colNum = i + 1;
break;
}
}
var letters = #DbColumn(null, viewName, colNum);
var options = #Trim(#Unique(#UpperCase(#Left(letters, 1))))
viewScope.put('jumpToOptions', options);
It works beautifully - but I want to modify the code to reference a view in a different database. In the post Brad says that the code can be "enhanced" to accomplish this. But I have been experimenting and searching for a while and cannot accomplish the enhancement.
Thanks for any help.
--Lisa&
In your second line, you establish a handle on the view by viewName you pull from the component viewPanel1. Your call is database.getView(viewName). This amounts to a programmatic reference of NotesDatabase.getView(). If you get a handle on the other database you want to connect to, they you can invoke the same .getView() call on that handle.
First, establish your connection to the other database; this is done via the session keyword (which is a NotesSession), as such:
var extDB = session.getDatabase(dbName)
As Howard points out, that session keyword is the current user's session and will be subject to all ACL rights/assignments/roles as that user. If you need to elevate privileges to programmatically expose additional data, you can do so with the sessionAsSigner keyword (which is also a NotesSession, just with the credentials of the signer, yourself, or you can have the NSF signed as the server ID, to give it even higher privileges).
Then proceed as usual with your extDB handle in place of the database keyword (which is about the same as session.getCurrentDatabase()); like so:
var vw = extDB.getView(viewName)
The NotesDatabase.getView() call will return null if a View by that name doesn't exist in that NSF, so you'll want to ensure that it's there and programmatically check for and handle a null return.
[Edit]
Since you're using the ported # function of #DbColumn as it is, to use the approach as Frantisek Kossuth suggests may be easy, but relies on the NotesSession of the current user. To override that user's (lack of) privileges and get full visibility of all documents' values in the separate NSF, you would still need to get a handle on the columnValues for the View as shown above, using the sessionAsSigner keyword.
[/Edit]
Based on your code you need to specify database in #DbColumn formula, too.
var letters = #DbColumn([database], viewName, colNum);
You can read about it here or there...
Aside of documented formats you can use API format "server!!database" as single string value.

Creating Roles Programmatically for each User Group

I want to create a Role for each User Group within my portal so that I can grant specific access to many users at once, depending on what groups they belong to.
Using Liferay API, is there any way to programmatically add a Role for each User Group that exists within the Portal?
This would be more efficient than adding each role individually through the UI.
Something like
for(i = 0; i < userGroups.size(); i++){
roles.add(userGroups[i].getName());
}
If possible, I'd like to then assign the same User Group to that Role within the same method, otherwise the role would know nothing of the associated user group:
roles.assignUserGroup(userGroups[i]);
Anyone accomplished a task similar to this?
Linked Liferay Forum Post http://liferay.com/community/forums/-/message_boards/message/46355079
In short you need to create a Role with a similar name as that of the UserGroups you have and then assign the UserGroup to that Role.
You can do that programmatically with Liferay's API, since that is how liferay also creates the different types of Roles and assigns users, user-groups etc to the role.
You just need to dig into to the source code to do this. Check source code for RoleLocalServiceImpl, UserGroupLocalServiceImpl and GroupLocalServiceImpl.
I am providing the steps for Liferay v6.2, there should not be much change in 6.0 and 6.1:
Fetch all the UserGroups using UserGroupLocalServiceUtil.getUserGroups(companyId)
Loop through it as you have done:
for (UserGroup userGroup : userGroups) {
... // steps to follow
}
Get the name of the UserGroup: userGroup.getName()
Create a Role using RoleLocalServiceUtil.addRole( ... ) and assign the userGroup to role using GroupLocalServiceUtil.addRoleGroups( ... )
for (UserGroup userGroup : userGroups) {
String userGroupName = userGroup.getName();
// for locale specific title (optional, can be null)
Map<Locale, String> titleMap = new HashMap<Locale, String>();
titleMap.put(Locale.ENGLISH, userGroupName);
// for locale specific description (optional, can be null)
Map<Locale, String> descriptionMap = new HashMap<Locale, String>();
titleMap.put(Locale.ENGLISH, "Role created for UserGroup - " + userGroupName);
int type = RoleConstants.TYPE_REGULAR;
// adding the role
Role role = RoleLocalServiceUtil.addRole(userId, Role.class.getName(), 0, userGroupName, titleMap, descriptionMap, type, null, null);
// assigning the UserGroup to the role
GroupLocalServiceUtil.addRoleGroups(role.getRoleId(), new long[]{userGroup.getGroupId()}); // need to pass groupId and not userGroupId
}
Now were would you write this code-snippet? There are various places depending upon what requirement you have:
Custom plugin portlet with a UI to execute this code. (better if required periodically)
Custom plugin action-hook and the code goes inside a StartUpAction, executes the code when the hook is being deployed. Hook should be undeployed or else will run everytime the hook is deployed. (better for one time use)
Custom plugin upgrade hook, executes the code during deployment once based on the upgrade condition. (better for one time use)
Use Beanshell, Server Administration → Script → Select Beanshell → Paste the code-snippet → Execute. You need to have the proper import statements and then just the code-snippet and you are good to go. For an example of Beanshell usage you can check my other answer. (better for one time use)
Even after all this you would still have to take the pains to give permission to each role ;-)
Hope this helps though.

How can I improve my SmartGWT LGPL implementation of ListGrid server connection/Datasource?

To expand my question, you could say that I want to program in SmartGWT instead of programming into SmartGWT ( http://msmvps.com/blogs/jon_skeet/archive/2008/04/23/programming-quot-in-quot-a-language-vs-programming-quot-into-quot-a-language.aspx ).
I have a 2 column ListGrid, populated with data from the 5 column database table. I don't use a DataSource (more on that later), instead I get the data from the async service and populate it on success like this predmetiGrid.setData(PredmetRecord.convertToContractRecordArray(result)); . The user can edit the data and press the Save button to save it. The way I have implemented the save is:
// repeat this for each edited field
for (int i=0; i < predmetiGrid.getAllEditRows().length; i++){
int editedRowIndex = predmetiGrid.getAllEditRows()[i];
// for each edite get the full record
PredmetRecord editedRecord = (PredmetRecord)predmetiGrid.getRecord(editedRowIndex);
// create a new DomainObject - Predmet, and set the ID from the
// Row so I have the ID to use for update later
Integer predmetID = editedRecord.getAttributeAsInt("predmetID");
Predmet predmet = new Predmet(predmetID);
// fill Predmet object with either the edited value, or the
// original value (if only one of the fields was changed and not both)
String editedNazivPredmeta = (String)predmetiGrid.getEditValues(editedRecord).get("nazivPredmeta");
boolean isNazivChanged = editedNazivPredmeta != null;
if (!isNazivChanged){
editedNazivPredmeta = editedRecord.getAttribute("nazivPredmeta");
}
predmet.setNazivPredmeta(editedNazivPredmeta);
String editedOpisPredmeta = (String) predmetiGrid.getEditValues(editedRecord).get("opisPredmeta");
boolean isOpisChanged = editedOpisPredmeta != null;
if (!isOpisChanged){
editedOpisPredmeta = editedRecord.getAttribute("opisPredmeta");
}
predmet.setOpisPredmeta(editedOpisPredmeta);
predmetiList.add(predmet);
}
In another method I call the async service:
public void updatePredmeti(List<Predmet> predmeti) throws RpcException, IllegalArgumentException {
for (int i=0; i<predmeti.size();i++){
JdbcPredgledPredmetaDAO.getInstance().updatePredmet(predmeti.get(i));
}
}
Now there are a few problems with this implementation. The most obvious ones are:
a) I'm not using a Datasource conected with the ListGrid. I don't use it because I don't understand how to use it in my case since the examples are written either for an XML DataSource or for the SmartGWT Pro (or higher) integrated server.
b) The async method needs to have a rollback mechanism if one of the inserts fail, though there could be a smarter implementation of this (e.g. do all inserts in one transaction).
c) I'm "hacking" to get and update the data instead of using object methods/properties but this is, currently, the best I got form the JavaDoc; I'd prefer to see best practice way to write this and learn
I'm using SmartGWT LGPL 3.0, Tomcat 7.0, Java 1.6
You can use a custom Datasource. DataSource.setDataFormat(DSDataFormat.CUSTOM). With this setting the DataSource will not handle the response, instead you have to parse it with transformResponse().

GAE Arabic Support

using this code i persist data to GAE Store
but when storing Arabic it's format in Store become ?????
how to support persist Arabic Text in GAE ?
the code :
PersistenceManager manager = PMF.get().getPersistenceManager();
Category category = new Category(categoryName);
manager.makePersistent(category);
manager.refresh(category);
manager.close();
It's more likely that the text is corrupted when you submit it from a form, or render it to HTML, rather than when it is stored (or retrieved).
As a quick test, try this:
String test = "\u0627\u0644\u0633\u0644\u0627\u0645";
PersistenceManager manager = PMF.get().getPersistenceManager();
Category category = new Category(test);
manager.makePersistent(category);
manager.refresh(category);
manager.close();
If that displays correctly (السلام), then the problem is with the way the input is handled on its way into the application. If it still appears corrupted, try another test where you retrieve the category name, and within your application, compare it to the original value of test. The test might look something like this:
boolean okay = "\u0627\u0644\u0633\u0644\u0627\u0645".equals(category.getName());
Log (or display) the value of okay. If false, then it really is the persistence layer that can't handle Arabic. Post your findings, and we'll work toward a solution once we are more confident where the problem truly is.
Update: The servlet engine is not guaranteed to recognized the character encoding if you set it via setHeader(). Use the setContentType() method or the setCharacterEncoding() method instead.

Categories

Resources