JournalArticleLocalServiceUtil.updateArticle fails with "null" - liferay

I want to update a Liferay Web Content, creating a new version, so I wrote this Groovy script:
import com.liferay.portal.service.ServiceContext
import com.liferay.portlet.journal.service.JournalArticleLocalServiceUtil
long companyId=10154
long userId=12328
long groupId=17074933
long folderId = 0
String articleId="17075078"
double version=1
String content='<?xml version="1.0"?><root available-locales="en_US" default-locale="en_US"><static-content language-id="en_US"><![CDATA[Bonjour]]></static-content></root>'
ServiceContext serviceContext = new com.liferay.portal.service.ServiceContext()
serviceContext.setAddCommunityPermissions(true)
serviceContext.setAddGuestPermissions(true)
serviceContext.setScopeGroupId(groupId)
serviceContext.setCompanyId(companyId)
serviceContext.setUserId(userId)
JournalArticleLocalServiceUtil.updateArticle(
userId, groupId, folderId, articleId, version, content, serviceContext)
Unfortunately, the Groovy script console fails, and catalina.out only contains this minimalistic debug information:
null
The identifiers are the ones I have found in the Liferay web interface. Just to check, I verified the content of MySQL, the values are correct:
mysql> select companyId, userId, groupId, folderId, articleId, version from JournalArticle order by createDate desc limit 1;
+-----------+--------+----------+----------+-----------+---------+
| companyId | userId | groupId | folderId | articleId | version |
+-----------+--------+----------+----------+-----------+---------+
| 10154 | 12328 | 17074933 | 0 | 17075078 | 1 |
+-----------+--------+----------+----------+-----------+---------+
What I am doing wrong?
I am using Liferay Portal Enterprise Edition 6.2.10 EE GA1. The same code works well on Liferay Portal Community Edition 6.2 CE GA6, so I might need a workaround to achieve what I want.
When I use JournalArticleLocalServiceUtil.updateContent instead it works, but I can't use it as it does not create a new version of the web content (it just replaces the version).

Self instantiation of a ServiceContext can be error-prone, because you don't know which fields the service implementation requires. I guess there is a setter for your ServiceContext missing, so the updateArticle (or somewhere below eg: validate , updateAsset ...) throws a Nullpointer Exception.
Rather use:
ServiceContext serviceContext = ServiceContextFactory.getInstance(JournalArticle.class.getName(), actionRequest);
Otherwise, you might need to debug (if possible) the Service-Implementation while executing your script.

Related

Fetching all cucumber scenarios that have a particular tag

How can I fetch a list of all the scenarios that have a particular tag. For example
get all scenarios that have #checkout tag.
Lets assume you have 15-20 Scenarios/Scenarios Outline tagged with #checkout.
#checkout
Scenario Outline: Validation of UseCase Guest User Order Placement flow from Search
Given User is on Brand Home Page <Site>
And User searches for a styleId and makes product selection on the basis of given color and size
| Style_ID | Product_Size | Product_Color |
| TestData1 | TestData1 | TestData1 |
| TestData2 | TestData2 | TestData2 |
Then Clicking on Cart icon shall take user to Shopping Bag
Please follow this way to fetch name of scenarios.
File name Hook.java
#Before
public void setUpScenario(Scenario scenario){
String scenarioName = scenario.getName();
//Either you can write down name of the scenario under a file system like excel or implement in the way you want
}
Please lets know if you find it meaningful and it solved your problem.
Dry run to the rescue.
Dry run gives you a way to quickly scan your features without actually running them.
Try the following CucumberOptions annotations (this is Java/Junit version, but the idea applies everywhere)
#RunWith(Cucumber.class)
#CucumberOptions(plugin = { "pretty", "html:target/cucumber-html-report", "json:target/cucumber.json" }, glue = {
"some.stepdef" }, features = { "src/cucumberTest/featureFiles" }, tags = { "#now" }
,dryRun = true, strict=true)
public class CucumberNowTestDryRunner {
}
The cucumber report will look like this

Am I designing and constructing my value objects correctly?

Sorry in advance if this question is unclear. Please tell me what to change to make it a better question.
I am currently maintaining a C# WinForm system where I'm trying to learn and use DDD and CQRS principles. Vaughn Vernon's Implementing Domain Driven Design is my main DDD reference literature.
The system currently uses legacy code which makes use of Data Aware Controls.
In the Asset Inventory Context, i have designed my aggregate root Asset which composes of multiple valueObjects which are standard entries in the system:
In this Context, i'm trying to implement a use case where the user can manually register an Asset to the system.
My current implementation is the following:
From Presentation Layer:
Upon loading the RegisterAssetForm.cs it loads the existing standard entry lists of Group, ItemName, etc. through the Data Aware controls, all consisting of data rows with columnsid: int and name: string.
When the user selects the desired ItemName, Group, PropertyLevel, Department, and Category, then clicks save, a command is performed:
RegisterAssetForm.cs
...
AssetInventoryApplicationService _assetInventoryServ;
...
void btnSave_Click(object sender, EventArgs e)
{
int itemNameId = srcItemName.Value // srcItemName is a custom control whose Value = datarow["id"]
int groupId = srcGroup.Value;
string categoryId = srcCategory.Value;
string departmentId = srcDepartment.Value;
string propLvlId = srcPropLevel.Value;
...
RegisterAssetCommand cmd = new RegisterAssetCommand(itemNameId, groupId, categoryId, departmentId, propLvlId);
_assetInventoryServ.RegisterAsset(cmd);
...
}
From Application Layer:
The AssetInventoryApplicationService depends on domain services.
AssetInventoryApplicationService.cs
...
IAssetRepository _assetRepo;
...
public void RegisterAsset(RegisterAssetCommand cmd)
{
...
AssetFactory factory = new AssetFactory();
AssetID newId = _assetRepo.NextId();
Asset asset = factory.CreateAsset(newId, cmd.ItemNameId, cmd.PropertyLevelId,
cmd.GroupId, cmd.CategoryId, cmd.DepartmentId);
_assetRepo.Save(asset);
...
}
From Domain Layer:
AssetFactory.cs //not my final implementation
...
public class AssetFactory
{
...
public Asset CreateAsset(AssetID id, int itemNameId, int propLvlId, int groupId, int categoryId, int departmentId)
{
ItemName itemName = new ItemName(itemNameId);
PropertyLevel propLvl = new PropertyLevel(propLvlNameId);
Group group = new Group(groupNameId);
Category category = new Category(categoryNameId);
Department department = new Department(departmentNameId);
return new Asset(id, itemName, propLvl, group, category, deparment);
}
...
}
Sample table of what fills my value objects
+------------+--------------+
| CategoryID | CategoryName |
+------------+--------------+
| 1 | Category1 |
| 2 | Category2 |
| 3 | Category3 |
| 4 | Category4 |
| 5 | Category5 |
+------------+--------------+
I know domain models must be persistence-ignorant that's why i intend to use surrogate identites (id field) in Layer Supertype with my valueobject to separate the persistence concern from the domain.
The main property to distinguish my value objects is their Name
From the presentation layer, i send the standard entry value as integer id corresponding to primary keys through a command to the application layer which uses domain services.
Problem
* Is it right for me to pass the standard entry's id when creating the command, or should I pass the string name?
* If id is passed, how do i construct the standard entry value object if name is needed?
* If name is passed, do i need to figure out the id from a repository?
* Or am I simply designing my standard entry value objects incorrectly?
Thanks for your help.
It looks to me that you may be confusing a Value Object and an Entity.
The essential difference is that an Entity needs an Id but a VO is a thing (rather than a specific thing). A telephone number in a CRM would likely be a VO. But it would likely be an Entity in if you are a telephone company.
I have an example of VO in this post which you may find helpful - you can get it here
To answer your 'Problems' more specifically:
If you are creating some entity then it can be advantageous to pass in the id to a command. That way you already know what the id will be.
You shouldn't be able to create an invalid value object.
Why can't you pass in the name and the ID? Again - not sure this is relevant to a Value Object
I think you have designed them incorrectly. But I can't be sure because I don't know your specific domain.
Hope this helps!

Find specific sentence in a web page using powershell

I need to use powershell to resolve IP addresses via whois. My company filters port 43 and WHOIS queries so the workaround I have to use here is to ask powershell to use a website such as https://who.is, read the http stream and look for the Organisation Name matching the IP address.
So far I have managed to get the webpage read into powershell (example here with a WHOIS on yahoo.com) which is https://who.is/whois-ip/ip-address/206.190.36.45
So here is my snippet:
$url=Invoke-WebRequest https://who.is/whois-ip/ip-address/206.190.36.45
now if I do :
$url.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False HtmlWebResponseObject Microsoft.PowerShell.Commands.WebResponseObject
I see this object has several properties:
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
AllElements Property Microsoft.PowerShell.Commands.WebCmdletElementCollection AllElements {get;}
BaseResponse Property System.Net.WebResponse BaseResponse {get;set;}
Content Property string Content {get;}
Forms Property Microsoft.PowerShell.Commands.FormObjectCollection Forms {get;}
Headers Property System.Collections.Generic.Dictionary[string,string] Headers {get;}
Images Property Microsoft.PowerShell.Commands.WebCmdletElementCollection Images {get;}
InputFields Property Microsoft.PowerShell.Commands.WebCmdletElementCollection InputFields {get;}
Links Property Microsoft.PowerShell.Commands.WebCmdletElementCollection Links {get;}
ParsedHtml Property mshtml.IHTMLDocument2 ParsedHtml {get;}
RawContent Property string RawContent {get;}
RawContentLength Property long RawContentLength {get;}
RawContentStream Property System.IO.MemoryStream RawContentStream {get;}
Scripts Property Microsoft.PowerShell.Commands.WebCmdletElementCollection Scripts {get;}
StatusCode Property int StatusCode {get;}
StatusDescription Property string StatusDescription {get;}
but every time I try commands like
$url.ToString() | select-string "OrgName"
Powershell returns the whole HTML code because it interprets the text string as a whole. I found a workaround dumping the output into a file and then read the file through an object (so every line is an element of an array) but I have hundreds of IPs to check so that's not very optimal to create a file all the time.
I would like to know how I could read the content of the web page https://who.is/whois-ip/ip-address/206.190.36.45 and get the line that says :
OrgName: Yahoo! Broadcast Services, Inc.
and just that line only.
Thanks very much for your help! :)
There are most likely better ways to parse this but you were on the right track with you current logic.
$web = Invoke-WebRequest https://who.is/whois-ip/ip-address/206.190.36.45
$web.tostring() -split "[`r`n]" | select-string "OrgName"
Select-String was returning the match as it, previously, was one long string. Using -split we can break it up to just get the return you expected.
OrgName: Yahoo! Broadcast Services, Inc.
Some string manipulation after that will get a cleaner answer. Again, many ways to approach this as well
(($web.tostring() -split "[`r`n]" | select-string "OrgName" | Select -First 1) -split ":")[1].Trim()
I used Select -First 1 as select-string could return more than one object. It would just ensure we are working with 1 when we manipulate the string. The string is just split on a colon and trimmed to remove the spaces that are left behind.
Since you are pulling HTML data we could also walk through those properties to get more specific results. The intention of this was to get 1RedOne answer
$web = Invoke-WebRequest https://who.is/whois-ip/ip-address/206.190.36.45
$data = $web.AllElements | Where{$_.TagName -eq "Pre"} | Select-Object -Expand InnerText
$whois = ($data -split "`r`n`r`n" | select -index 1) -replace ":\s","=" | ConvertFrom-StringData
$whois.OrgName
All that data is stored in the text of the PRE tag in this example. What I do is split up the data into its sections (Sections are defined with blank lines separating them. I look for consecutive newlines). The second group of data contains the org name. Store that in a variable and pull the OrgName as a property: $whois.OrgName. Here is what $whois looks like
Name Value
---- -----
Updated 2013-04-02
City Sunnyvale
Address 701 First Ave
OrgName Yahoo! Broadcast Services, Inc.
StateProv CA
Country US
Ref http://whois.arin.net/rest/org/YAHO
PostalCode 94089
RegDate 1999-11-17
OrgId YAHO
You can also make that hashtable into a custom object if you prefer dealing with those.
[pscustomobject]$whois
Updated : 2017-01-28
City : Sunnyvale
Address : 701 First Ave
OrgName : Yahoo! Broadcast Services, Inc.
StateProv : CA
Country : US
Ref : https://whois.arin.net/rest/org/YAHO
PostalCode : 94089
RegDate : 1999-11-17
OrgId : YAHO
it it very simple to use whois app this is for microsoft put app in System32 or windir and in powershell use whois command then get-string get "orgname" like this
PS C:\> whois.exe -v 206.190.36.45 | Select-String "Registrant Organization"
Registrant Organization: Yahoo! Inc.
I advise you this app because has more information for your work
Here you go, the way to do this is in fact to do an Invoke-WebRequest. If we take a look at some of the properties of the object we get from Invoke-WebRequest, we can see that PowerShell has already parsed some of the HTML and text for us.
All that we have to do is pick out some of the values we'd like to work with. For instance, taking a peek at the ParsedText field, we see these results.
These fields begin on about line 30 or so. In my approach to solving this problem we know that we'll find good data like this mid-way down the page, so if we could scrape the values from these lines, we'd be on our way to working with the data. The code to accomplish this first part is this:
$url = "https://who.is/whois-ip/ip-address/$ipaddress"
$Results = Invoke-WebRequest $url
$ParsedResults = $Results.ParsedHtml.body.outerText.Split("`n")[30..50]
Now, PowerShell has a number of very powerful commands to import and convert data into various formats. For instance, if we could only replace the ':' colon character with an equals sign '=', we could send the whole mess over to ConverFrom-StringData and have rich PowerShell objects to work with. It turns out that we can easily do that using the universal -Replace operator, like this
$Results.ParsedHtml.body.outerText.Split("`n")[30..50] -replace ":","="
I figured you might want to do this again in the future, so I took the entire thing and made it into a simple five line function for you. Throw this into your $Profile and enjoy.
So the finished result looks like this:
Function Get-WhoIsData {
param($ipaddress='206.190.36.45')
$url = "https://who.is/whois-ip/ip-address/$ipaddress"
$Results = Invoke-WebRequest $url
$ParsedResults = $Results.ParsedHtml.body.outerText.Split("`n")[30..50] -replace ":","=" | ConvertFrom-StringData
$ParsedResults }
and using it works this way:
PS C:\windows\system32> Get-WhoIsData -ipaddress 206.190.36.45
Name Value
---- -----
NetRange 206.190.32.0 - 206.190.63.255
CIDR 206.190.32.0/19
NetName NETBLK1-YAHOOBS
NetHandle NET-206-190-32-0-1
Parent NET206 (NET-206-0-0-0-0)
NetType Direct Allocation
OriginAS
Organization Yahoo! Broadcast Services, Inc. (YAHO)
RegDate 1995-12-15
Updated 2012-03-02
Ref http=//whois.arin.net/rest/net/NET-206-190-32-0-1
OrgName Yahoo! Broadcast Services, Inc.
OrgId YAHO
Address 701 First Ave
City Sunnyvale
StateProv CA
PostalCode 94089
You can then select any of the properties you'd like using normal Select-Object or Where-Object commands. For example, to pull out just the orgName property, you'd use this command:
(Get-WhoIsData).OrgName
>Yahoo! Broadcast Services, Inc.

How do I create a Cucumber DataTable?

I want to manually set up a Cucumber DataTable using Java (instead of Gherkin).
In Gherkin, my table would look like this:
| h1 | h2 |
| v1 | v2 |
My Java so far looks like this:
List<String> raw = Arrays.asList( "v1", "v2");
DataTable dataTable = DataTable.create(raw, Locale.getDefault(), "h1", "h2");
What I get back is a DataTable with headers but no contents. It's also longer than expected:
| h1| h2 |
| | |
| | |
I'm sure the solution must be fairly simple, but I'm a bit at a loss right now. What do I need to do to get my table?
Hope this helps. If you're full Gherkins step looks like this...
When I see the following cooked I should say:
| food | say |
| Bacon | Yum! |
| Peas | Really? |
You want this in Java. (Note that the cucumber.api.DataTable passed in is setup with your expected values before the test).
#When("^I see the following cooked I should say:$")
public void theFoodResponse(DataTable expectedCucumberTable) {
// Normally you'd put this in a database or JSON
List<Cuke> actualCukes = new ArrayList();
actualCukes.add(new Cuke("Bacon", "Yum!"));
actualCukes.add(new Cuke("Peas", "Really?"));
Another link to a Full Example.diff(actualCukes)
}
I will say that in the examples by Aslak Hellesøy he doesn't actually use the DataTable.
He would do your example something like this:
#When("^I see the following cooked I should say:$")
public void theFoodResponse(List<Entry> entries) {
for (Entry entry : entries) {
// Test actual app you've written
hungryHuman.setInputValue(entry.food);
hungryHuman.setOutputValue(entry.say);
}
}
public class Entry {
String food;
String say;
}
For a Full Example for more reading check out:
Gherkins calculator: link
Java calculator Step Definitions: link
EDIT:
Sorry for the overkill #Christian, you perhaps didn't need the whole context of how to use it in an app, just a clean way to use DataTable.create and most of what I posted was another way to skin that cat with the Entry class (Which might be helpful for those reading this later.)
So the way you did it in your comment wasn't far off. I'm no pro at collections, so I can't give you any tips on making your 2D List of Strings, but I can clarify on the last two parameters (if you're using all 4).
If your columns are using Date or Calendar fields you can indicate a
Format in the second to last param.
If you don't use the
last one for Column Names, then it will automatically read your top line string for the column names.
Also you can drop the Locale.getDefault() and it will do that for you; so then you're looking at:
.
List<List<String>> infoInTheRaw = Arrays.asList( Arrays.asList("h1", "h2"),
Arrays.asList("v1", "v2") );
DataTable dataTable = DataTable.create(infoInTheRaw);
You could also use the constructor which would be equally messy. :)
Instead of DataTable we can actually create List of custom object using the same fields as in data table.
for e.g.
Then the book list response should contain records as
| book_id | book_title | book_cost |
| B0001 | Head First Java | 350|
| B002 | Head First Spring| 550|
Now your Step class should define a Step as
#Then("^the book list response should contain records as$")
public void validateBookRecords(List<Book> booksList) throws Throwable { //assertions will go here}
And your Book class should look like
public class Book {
public String book_id;
public String book_title;
public String book_cost;
}
Since the fields are all public so no getter/setter are needed. If you decide to declare them private (dont see a reason why) then ensure to add getters/setters as well.
Hope this helps
the cucumber dataTable can be build from nested lists where the first List in the nested lists contains the names of the columns and the others are the data lists per rows:
public static DataTable createTestDataTable() {
List<List<String>> dtList = new ArrayList<>();
List<String> columns = Arrays.asList("col1", "col2", "col3");
List<String> dataRow1 = Arrays.asList("r1c1", "r1c2", "r1c3");
List<String> dataRow2 = Arrays.asList("r2c1", "r2c2", "r2c3");
dtList.add(columns);
dtList.add(dataRow1);
dtList.add(dataRow2);
return DataTable.create(dtList);
}
This corresponds to:
|col1|col2|col3| <-- columns
|r1c1|r1c2|r1c3| <-- data row 1
|r2c1|r2c2|r2c3| <-- data row 2
this is the method that I use to extract the first row as a table headers from the Cucumber dataTabe object.
for this scenario:
When I do some stuff
| header1 | header2 | header3 | header4 |
| N | do | | |
| Nu | | some | stuff |
| qwerty | | some | stuff |
| cook | do | | |
this solution:
public void i_consume_datatable(DataTable table) {
//this is the little optimized version
List<List<String>> dataLists = table.asLists(String.class);
if (dataLists.size() > 1) {
String[] headers = dataLists.get(0).toArray(new String[0]);
dataLists.stream().skip(1).map(rowList -> {
Map<String, String> row = new LinkedHashMap<>();
IntStream.range(0, headers.length).forEach(i -> row.put(headers[i], rowList.get(i)));
return row;
}).forEach(map -> yourActualMethodWhichDoTheWork(map.get(“header1”), map.get("header2”), map.get("header3”), map.get("header4”)));
}
}

Liferay custom-sql using IN operator

I am using Liferay 6.1, Tomcat and MySQL. I have a custom-sql sentence for a list portlet. The custom-sql uses two parameters: an array of groupIds and a result limit.
SELECT
count(articleId) as count,
...
FROM comments
WHERE groupId IN (?)
GROUP BY articleId
ORDER BY count DESC
LIMIT 0, ?
My FinderImpl class has this method:
public List<Comment> findByMostCommented(String groupIds, long maxItems) {
Session session = null;
session = openSession();
String sql = CustomSQLUtil.get(FIND_MOST_COMMENTS);
SQLQuery query = session.createSQLQuery(sql);
query.addEntity("Comment", CommentImpl.class);
QueryPos queryPos = QueryPos.getInstance(query);
queryPos.add(groupIds);
queryPos.add(maxItems);
List<Comment> queryResult = query.list();
return queryResult;
}
This returns 0 results. If I remove the WHERE IN(), it works.
Is IN a valid operator? If not, how can search within different groups?
Perhaps hibernate is quoting your string of groupIds (presumably it is in the form of "1,2,3,4" and when hibernate translates this to sql it is putting quotes around it for you?
You may want to try something like this (from Liferay itself):
String sql = CustomSQLUtil.get(FIND_BY_C_C);
sql = StringUtil.replace(sql, "[$GROUP_IDS$]", groupIds);
And include ([$GROUP_IDS$]) in place of the (?) in your SQL

Resources