I have a YANG model (known to MDSAL) which I am using in an opendaylight application. In my application, I am presented with a json formatted String which I want to store in the MDSAL database. I could use the builder of the object that I wish to store and set its with fields presented in the json formatted String one by one but this is laborious and error prone.
Alternatively I could post from within the application to the Northbound API which will eventually write to the MDSAL datastore.
Is there a simpler way to do this?
Thanks,
Assuming that your incoming JSON matches the structure of your YANG model exactly (does it?), I believe what you are really looking for is to transform that JSON into a "binding independant" (not setters of the generated Java class) internal model - NormalizedNode & Co. Somewhere in the controller or mdsal project there is a "codec" class that can do this.
You can either search for such code, and its usages (I find looking at tests are always useful) in the ODL controller and mdsal projects source code, or in other ODL projects which do similar things - I'm thinking specifically browsing around the jsonrpc and daexim projects sources; specifically this looks like it may inspire you: https://github.com/opendaylight/daexim/blob/stable/nitrogen/impl/src/main/java/org/opendaylight/daexim/impl/ImportTask.java
Best of luck.
Based on the information above, I constructed the following (which I am posting here to help others). I still do not know how to get rid of the deprecated reference to SchemaService (perhaps somebody can help).
private void importFromNormalizedNode(final DOMDataReadWriteTransaction rwTrx, final LogicalDatastoreType type,
final NormalizedNode<?, ?> data) throws TransactionCommitFailedException, ReadFailedException {
if (data instanceof NormalizedNodeContainer) {
#SuppressWarnings("unchecked")
YangInstanceIdentifier yid = YangInstanceIdentifier.create(data.getIdentifier());
rwTrx.put(type, yid, data);
} else {
throw new IllegalStateException("Root node is not instance of NormalizedNodeContainer");
}
}
private void importDatastore(String jsonData, QName qname) throws TransactionCommitFailedException, IOException,
ReadFailedException, SchemaSourceException, YangSyntaxErrorException {
// create StringBuffer object
LOG.info("jsonData = " + jsonData);
byte bytes[] = jsonData.getBytes();
InputStream is = new ByteArrayInputStream(bytes);
final NormalizedNodeContainerBuilder<?, ?, ?, ?> builder = ImmutableContainerNodeBuilder.create()
.withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(qname));
try (NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(builder)) {
SchemaPath schemaPath = SchemaPath.create(true, qname);
LOG.info("SchemaPath " + schemaPath);
SchemaNode parentNode = SchemaContextUtil.findNodeInSchemaContext(schemaService.getGlobalContext(),
schemaPath.getPathFromRoot());
LOG.info("parentNode " + parentNode);
try (JsonParserStream jsonParser = JsonParserStream.create(writer, schemaService.getGlobalContext(),
parentNode)) {
try (JsonReader reader = new JsonReader(new InputStreamReader(is))) {
reader.setLenient(true);
jsonParser.parse(reader);
DOMDataReadWriteTransaction rwTrx = domDataBroker.newReadWriteTransaction();
importFromNormalizedNode(rwTrx, LogicalDatastoreType.CONFIGURATION, builder.build());
}
}
}
}
Related
I'm trying to write a custom Nifi processor which will take in the contents of the incoming flow file, perform some math operations on it, then write the results into an outgoing flow file. Is there a way to dump the contents of the incoming flow file into a string or something? I've been searching for a while now and it doesn't seem that simple. If anyone could point me toward a good tutorial that deals with doing something like that it would be greatly appreciated.
The Apache NiFi Developer Guide documents the process of creating a custom processor very well. In your specific case, I would start with the Component Lifecycle section and the Enrich/Modify Content pattern. Any other processor which does similar work (like ReplaceText or Base64EncodeContent) would be good examples to learn from; all of the source code is available on GitHub.
Essentially you need to implement the #onTrigger() method in your processor class, read the flowfile content and parse it into your expected format, perform your operations, and then re-populate the resulting flowfile content. Your source code will look something like this:
#Override
public void onTrigger(final ProcessContext context, final ProcessSession session) throws ProcessException {
FlowFile flowFile = session.get();
if (flowFile == null) {
return;
}
final ComponentLog logger = getLogger();
AtomicBoolean error = new AtomicBoolean();
AtomicReference<String> result = new AtomicReference<>(null);
// This uses a lambda function in place of a callback for InputStreamCallback#process()
processSession.read(flowFile, in -> {
long start = System.nanoTime();
// Read the flowfile content into a String
// TODO: May need to buffer this if the content is large
try {
final String contents = IOUtils.toString(in, StandardCharsets.UTF_8);
result.set(new MyMathOperationService().performSomeOperation(contents));
long stop = System.nanoTime();
if (getLogger().isDebugEnabled()) {
final long durationNanos = stop - start;
DecimalFormat df = new DecimalFormat("#.###");
getLogger().debug("Performed operation in " + durationNanos + " nanoseconds (" + df.format(durationNanos / 1_000_000_000.0) + " seconds).");
}
} catch (Exception e) {
error.set(true);
getLogger().error(e.getMessage() + " Routing to failure.", e);
}
});
if (error.get()) {
processSession.transfer(flowFile, REL_FAILURE);
} else {
// Again, a lambda takes the place of the OutputStreamCallback#process()
FlowFile updatedFlowFile = session.write(flowFile, (in, out) -> {
final String resultString = result.get();
final byte[] resultBytes = resultString.getBytes(StandardCharsets.UTF_8);
// TODO: This can use a while loop for performance
out.write(resultBytes, 0, resultBytes.length);
out.flush();
});
processSession.transfer(updatedFlowFile, REL_SUCCESS);
}
}
Daggett is right that the ExecuteScript processor is a good place to start because it will shorten the development lifecycle (no building NARs, deploying, and restarting NiFi to use it) and when you have the correct behavior, you can easily copy/paste into the generated skeleton and deploy it once.
In the SDK Javadoc, the Community class does not have a "setParentCommunity" method but the CommunityList class does have a getSubCommunities method so there must be a programmatic way to set a parent Community's Uuid on new Community creation. The REST API mentions a "rel="http://www.ibm.com/xmlns/prod/sn/parentcommunity" element". While looking for clues I check an existing Subcommunity's XmlDataHandler's nodes and found a link element. I tried getting the XmlDataHandler for a newly-created Community and adding a link node with href, rel and type nodes similar to those in the existing Community but when trying to update or re-save the Community I got a bad request error. Actually even when I tried calling dataHandler.setData(n) where n was set as Node n=dataHandler.getData(); without any changes, then calling updateCommunity or save I got the same error, so it appears that manipulating the dataHandler XML is not valid.
What is the recommended way to specify a parent Community when creating a new Community so that it is created as a SubCommunity ?
The correct way to create a sub-community programatically is to modify the POST request body for community creation - here is the link to the Connections 45 infocenter - http://www-10.lotus.com/ldd/appdevwiki.nsf/xpDocViewer.xsp?lookupName=IBM+Connections+4.5+API+Documentation#action=openDocument&res_title=Creating_subcommunities_programmatically_ic45&content=pdcontent
We do not have support in the SBT SDK to do this using CommunityService APIs. We need to use low level Java APIs using Endpoint and ClientService classes to directly call the REST APIs with the appropriate request body.
I'd go ahead and extend the class CommunityService
then go ahead and add CommunityService
https://github.com/OpenNTF/SocialSDK/blob/master/src/eclipse/plugins/com.ibm.sbt.core/src/com/ibm/sbt/services/client/connections/communities/CommunityService.java
Line 605
public String createCommunity(Community community) throws CommunityServiceException {
if (null == community){
throw new CommunityServiceException(null, Messages.NullCommunityObjectException);
}
try {
Object communityPayload;
try {
communityPayload = community.constructCreateRequestBody();
} catch (TransformerException e) {
throw new CommunityServiceException(e, Messages.CreateCommunityPayloadException);
}
String communityPostUrl = resolveCommunityUrl(CommunityEntity.COMMUNITIES.getCommunityEntityType(),CommunityType.MY.getCommunityType());
Response requestData = createData(communityPostUrl, null, communityPayload,ClientService.FORMAT_CONNECTIONS_OUTPUT);
community.clearFieldsMap();
return extractCommunityIdFromHeaders(requestData);
} catch (ClientServicesException e) {
throw new CommunityServiceException(e, Messages.CreateCommunityException);
} catch (IOException e) {
throw new CommunityServiceException(e, Messages.CreateCommunityException);
}
}
You'll want to change your communityPostUrl to match...
https://greenhouse.lotus.com/communities/service/atom/community/subcommunities?communityUuid=2fba29fd-adfa-4d28-98cc-05cab12a7c43
and where the Uuid here is the parent uuid.
I followed #PaulBastide 's recommendation and created a SubCommunityService class, currently only containing a method for creation. It wraps the CommunityService rather than subclassing it, since I found that preferrable. Here's the code in case you want to reuse it:
public class SubCommunityService {
private final CommunityService communityService;
public SubCommunityService(CommunityService communityService) {
this.communityService = communityService;
}
public Community createCommunity(Community community, String superCommunityId) throws ClientServicesException {
Object constructCreateRequestBody = community.constructCreateRequestBody();
ClientService clientService = communityService.getEndpoint().getClientService();
String entityType = CommunityEntity.COMMUNITY.getCommunityEntityType();
Map<String, String> params = new HashMap<>();
params.put("communityUuid", superCommunityId);
String postUrl = communityService.resolveCommunityUrl(entityType,
CommunityType.SUBCOMMUNITIES.getCommunityType(), params);
String newCommunityUrl = (String) clientService.post(postUrl, null, constructCreateRequestBody,
ClientService.FORMAT_CONNECTIONS_OUTPUT);
String communityId = newCommunityUrl.substring(newCommunityUrl.indexOf("communityUuid=")
+ "communityUuid=".length());
community.setCommunityUuid(communityId);
return community;
}
}
I writing wicket webapp. I want to:
list all resources - videoPreview in the folder
preview it
add link to show in main preview panel.
I read a lot and look examples about resources, but seems like can't understand smthg. I write such funny code:
RepeatingView rv = new RepeatingView("showVideo");
add(rv);
File vidPrevDir = (new File("data/catalog/"+product+"/videoPreview"));
File[] list = vidPrevDir.listFiles();
for (File file : list) {
final String previewFile = file.getName();
AjaxLink link = new AjaxLink(rv.newChildId()){
#Override
public void onClick(AjaxRequestTarget target) {
container.name="iframe";
container.attrs.clear();
container.attrs.put("class", "viewPanel");
container.attrs.put("allowfullscreen", "yes");
container.attrs.put("src", "http://www.youtube.com/embed/"+previewFile.substring(previewFile.indexOf("___"), previewFile.length()-4));
target.add(container);
}
};
rv.add(link);
link.add(new Image("videoPreview", product+"/videoPreview/"+file.getName()));
}
In application i call
getResourceSettings().addResourceFolder("data");
It's work, but i feel bad when i see that. So my question is how to make such things in wicket? Maybe there is resource listing or java.io.File->wicket.Image converter ?
I only found built-in method:
ServletContext context = WicketApplication.get().getServletContext();
Set productList = context.getResourcePaths("/catalog");
It list filenames, not resources, but it is preferable approach, then i use in question.
I am new to RESTful services and their implementation on Spring 3. I would like your opinion on the best practices for returning type when a client creates a new resource in my server.
#RequestMapping(method = RequestMethod.POST,
value = "/organisation",
headers = "content-type=application/xml")
#ResponseStatus(HttpStatus.CREATED)
public ??? createOrganisation(#RequestBody String xml)
{
StreamSource source = new StreamSource(new StringReader(xml));
Organisation organisation = (Organisation) castorMarshaller.unmarshal(source);
// save
return ???;
}
A simple choice would be javax.ws.rs.core.Response, found in the Java EE's own restful services package. It - simply - tells what the web server should answer to the HTTP request.
For instance:
if (organisation != null)
return Response.ok().build();
else
return Response.serverError().build();
Custom response headers and other exotic things like that are possible with that return type too, but I don't think that would match with "best practices".
uh, I missed that #ResponseStatus(HttpStatus.CREATED)... I guess my answer was not much of help.
Maybe this will help instead: How to return generated ID in RESTful POST?
I would go for a ResponseEntity<byte[]> and you would have take care of the marshalling of your response on your controller method. Notice that you are basically scrapping the V in MVC, there is a MarshallingView on Spring but from experience I consider the previous solution much more flexible and easier to understand.
It is a good idea to return the newly created entity(with the generated id) wrapped in ResponseEntity. You can also set the HttpStatus in ResponseEntity based on the result of the operation.
#RequestMapping(method = RequestMethod.POST,
value = "/organization",
headers = "content-type=application/xml")
public ResponseEntity<Organization> createOrganisation(#RequestBody String xml) {
StreamSource source = new StreamSource(new StringReader(xml));
Organization organisation = (Organization) castorMarshaller.unmarshal(source);
// save
return new ResponseEntity<Organization>(organization, HttpStatus.OK);
}
My script fetches xml via httpConnection and saves to persistent store. No problems there.
Then I loop through the saved data to compose a list of image url's to fetch via queue.
Each of these requests calls the httpConnection thread as so
...
public synchronized void run()
{
HttpConnection connection = (HttpConnection)Connector.open("http://www.somedomain.com/image1.jpg");
connection.setRequestMethod("GET");
String contentType = connection.getHeaderField("Content-type");
InputStream responseData = connection.openInputStream();
connection.close();
outputFinal(responseData, contentType);
}
public synchronized void outputFinal(InputStream result, String contentType) throws SAXException, ParserConfigurationException, IOException
{
if(contentType.startsWith("text/"))
{
// bunch of xml save code that works fine
}
else if(contentType.equals("image/png") || contentType.equals("image/jpeg") || contentType.equals("image/gif"))
{
// how to save images here?
}
else
{
//default
}
}
What I can't find any good documentation on is how one would take the response data and save it to an image stored on the device.
Maybe I just overlooked something very obvious. Any help is very appreciated.
Thanks
I tried following this advise and found the same thing I always find when looking up BB specific issues: nothing.
The problem is that every example or post assumes you know everything about the platform.
Here's a simple question: What line of code writes the read output stream to the blackberry device? What path? How do I retrieve it later?
I have this code, which I do not know if it does anything because I don't know where it is supposedly writing to or if that's even what it is doing at all:
** filename is determined on a loop based on the url called.
FileOutputStream fos = null;
try
{
fos = new FileOutputStream( File.FILESYSTEM_PATRIOT, filename );
byte [] buffer = new byte [262144];
int byteRead;
while ((byteRead = result.read (buffer ))!=- 1)
{
fos.write (buffer, 0, byteRead);
}
fos.flush();
fos.close();
}
catch(IOException ieo)
{
}
finally
{
if(fos != null)
{
fos.close();
}
}
The idea is that I have some 600 images pulled from a server. I need to loop the xml and save each image to the device so that when an entity is called, I can pull the associated image - entity_id.png - from the internal storage.
The documentation from RIM does not specify this, nor does it make it easy to begin figuring it out.
This issue does not seem to be addressed on this forum, or others I have searched.
Thanks
You'll need to use the Java FileOutputStream to do the writing. You'll also want to close the connection after reading the data from the InputStream (move outputFinal above your call to close). You can find all kinds of examples regarding FileOutputStream easily.
See here for more. Note that in order to use the FileOutputStream your application must be signed.