I'm using Milo and its example server and client. I'm adding nodes to the server but I can't figure out how to add EuInformation, i.e., unit and description. I thought about using the ExtensionObject but since EuInformation does not implement Serializable I don't know how to pass it to the ExtensionObject. I'd also like to know how I can get the namespace ID and URI on client side. So far I just set them statically as I have access to the classes.
I've implemeted the AddNodes on server side. I can add nodes, read nodes and write to nodes.
Here's what I'm doing on client side:
// Should somehow get the namespace ID and namespace dynamically.
// Maybe by iterating through all nodes??
ExpandedNodeId parentNodeId = new ExpandedNodeId(
new nodeId(2,DatatypeNamespace.NODE_IDENTIFIER),
datatypeNamespace.NAMESPACE_URI, 0);
NodeId referenceTypeId = Identifiers.String;
// Define the new node.
ExpandedNodeId requestedNewNodeId = new ExpandedNodeId(new NodeId(2, "NewNode"),
DatatypeNamespace.NAMESPACE_URI, 0);
QualifiedName browseName = new QualifiedName(2, "NewNode");
// How to get this to the server??
EUInformation euinfo = new EUInformation(null,-1,LocalizedText.english("MyUnit"),
LocalizedText.english("My Description"));
ExpandedNodeId typeDef = new ExpandedNodeId(Identifiers.BaseVariableType,
DatatypeNamespace.NAMESPACE_URI, 0);
AddNodesItem newItem = new AddNodesItem(parentNodeId, referenceTypeId,
requestedNewNodeId,rowseName,NodeClass.VariableType, null, typeDef);
List<AddNodesItem> items = new ArrayList<AddNodesItem>();
items.add(newItem);
client.addNodes(items).get();
EDIT
With the help of Kevin Herron's answer I worked something out: I adjusted the write() in my namespace class. I can now modify the display name and description of the node with the values of the EUInformation. Here's my write() method:
#Override
public void write(WriteContext context, List<WriteValue> writeValues) {
List<StatusCode> results = Lists.newArrayListWithCapacity(writeValues.size());
for (WriteValue writeValue : writeValues) {
ServerNode node = server.getNodeMap().get(writeValue.getNodeId());
if (node != null) {
// Get the type of the variant thats about to be written to the node
NodeId variantType = writeValue.getValue().getValue().getDataType().get();
if (variantType.equals(Identifiers.Structure)) {
ExtensionObject o = (ExtensionObject) writeValue.getValue().getValue().getValue();
if (o.getEncodingTypeId().equals(Identifiers.EUInformation_Encoding_DefaultBinary)) {
EUInformation euInformation = (EUInformation) o.decode();
node.setDescription(euInformation.getDescription());
node.setDisplayName(euInformation.getDisplayName());
System.out.println("Wrote EUInformation " + euInformation);
results.add(StatusCode.GOOD);
context.complete(results);
return;
}
}
try {
node.writeAttribute(new AttributeContext(context), writeValue.getAttributeId(),
writeValue.getValue(), writeValue.getIndexRange());
results.add(StatusCode.GOOD);
System.out.println(String.format("Wrote value %s to %s attribute of %s",
writeValue.getValue().getValue(),
AttributeId.from(writeValue.getAttributeId()).map(Object::toString).orElse("unknown"),
node.getNodeId()));
} catch (UaException e) {
System.out.println(String.format("Unable to write %s", writeValue.getValue()));
results.add(e.getStatusCode());
}
} else {
results.add(new StatusCode(StatusCodes.Bad_NodeIdUnknown));
}
}
context.complete(results);
}
Ok, so you would add a new VaribleNode with a TypeDefinition of Property (Identifiers.PropertyType).
Then you would write to its Value attribute so it contains the EUInformation object:
EUInformation euInformation = ...
Variant v = new Variant(ExtensionObject.encode(euInformation));
...write the value to the node you created...
Related
I'm trying to retrieve the latest mail received in my mail box from one sender. I have an issue when a sender reply on one of his emails, for example:
screenshot of my exemple
I want to get the last message received on 04/28 instead of getting the two messages.
In my code, I simply did this to get my messages:
defaultFolder = store.getDefaultFolder().getFolder("inbox");
Message [] msg = defaultFolder.getMessages();
Any ideas of how we can get only the latest email of the same sender ?
Thank you!
To get the latest recieved email from the folder you can Sort items by using the Items.Sort method, here is a VBA sample (the Outlook object model is common for all kind of applications):
Sub SortByDueDate()
Dim myNameSpace As Outlook.NameSpace
Dim myFolder As Outlook.Folder
Dim myItem As Outlook.TaskItem
Dim myItems As Outlook.Items
Set myNameSpace = Application.GetNamespace("MAPI")
Set myFolder = myNameSpace.GetDefaultFolder(olFolderInbox)
Set myItems = myFolder.Items
myItems.Sort "[ReceivedTime]", False
For Each myItem In myItems
MsgBox myItem.Subject & "-- " & myItem.DueDate
Next myItem
End Sub
So, the first item will be the latest received. As soon as you got the latest item you can iterate over all items in the same conversation. The MailItem.GetConversation method obtains a Conversation object that represents the conversation to which this item belongs. So, you may get all items from the conversation. Read more about that in the Obtain and Enumerate Selected Conversations article. For example:
void DemoConversation()
{
object selectedItem =
Application.ActiveExplorer().Selection[1];
// This example uses only
// MailItem. Other item types such as
// MeetingItem and PostItem can participate
// in the conversation.
if (selectedItem is Outlook.MailItem)
{
// Cast selectedItem to MailItem.
Outlook.MailItem mailItem =
selectedItem as Outlook.MailItem;
// Determine the store of the mail item.
Outlook.Folder folder = mailItem.Parent
as Outlook.Folder;
Outlook.Store store = folder.Store;
if (store.IsConversationEnabled == true)
{
// Obtain a Conversation object.
Outlook.Conversation conv =
mailItem.GetConversation();
// Check for null Conversation.
if (conv != null)
{
// Obtain Table that contains rows
// for each item in the conversation.
Outlook.Table table = conv.GetTable();
Debug.WriteLine("Conversation Items Count: " +
table.GetRowCount().ToString());
Debug.WriteLine("Conversation Items from Table:");
while (!table.EndOfTable)
{
Outlook.Row nextRow = table.GetNextRow();
Debug.WriteLine(nextRow["Subject"]
+ " Modified: "
+ nextRow["LastModificationTime"]);
}
Debug.WriteLine("Conversation Items from Root:");
// Obtain root items and enumerate the conversation.
Outlook.SimpleItems simpleItems
= conv.GetRootItems();
foreach (object item in simpleItems)
{
// In this example, only enumerate MailItem type.
// Other types such as PostItem or MeetingItem
// can appear in the conversation.
if (item is Outlook.MailItem)
{
Outlook.MailItem mail = item
as Outlook.MailItem;
Outlook.Folder inFolder =
mail.Parent as Outlook.Folder;
string msg = mail.Subject
+ " in folder " + inFolder.Name;
Debug.WriteLine(msg);
}
// Call EnumerateConversation
// to access child nodes of root items.
EnumerateConversation(item, conv);
}
}
}
}
}
void EnumerateConversation(object item,
Outlook.Conversation conversation)
{
Outlook.SimpleItems items =
conversation.GetChildren(item);
if (items.Count > 0)
{
foreach (object myItem in items)
{
// In this example, only enumerate MailItem type.
// Other types such as PostItem or MeetingItem
// can appear in the conversation.
if (myItem is Outlook.MailItem)
{
Outlook.MailItem mailItem =
myItem as Outlook.MailItem;
Outlook.Folder inFolder =
mailItem.Parent as Outlook.Folder;
string msg = mailItem.Subject
+ " in folder " + inFolder.Name;
Debug.WriteLine(msg);
}
// Continue recursion.
EnumerateConversation(myItem, conversation);
}
}
}
I have a small java app and I have used JInterface to essentially expose it as an OTP process in my elixir app. I can call it and get a response successfully.
My problem is that the response I get back in elixir is of a binary but I cannot figure out how to convert a binary to a list of strings which is what the response is.
The code for my OTP node in Java using JInterface is below:
public void performAction(Object requestData, OtpMbox mbox, OtpErlangPid lastPid){
List<String> sentences = paragraphSplitter.splitParagraphIntoSentences((String) requestData, Locale.JAPAN);
mbox.send(lastPid, new OtpErlangBinary(getOtpStrings(sentences)));
System.out.println("OK");
}
private List<OtpErlangString> getOtpStrings(List<String> sentences) {
List<OtpErlangString> erlangStrings = new ArrayList<>();
for(int i = 0; i < sentences.size(); i++){
erlangStrings.add(new OtpErlangString(sentences.get(i)));
}
return erlangStrings;
}
It is necessary to wrap the response in an OtpErlangBinary and I have concerted the strings to OTPErlangString. I have also tried without converting the strings to OTPErlangString.
On the elixir side I can receive the binary response and IO.inspect it.
Does anybody know how to use JInterface to deserialise the results correctly when it's anything other than a single string? Or maybe, if I have made some mistake, how to build the correct response type so that I can deserialise it correctly?
Any help would be really appreciated as I have been trying to figure this out for ages.
Thanks in advance.
I have been playing around with JInterface and Elixir and I think I've got your problem figured out.
So you are trying to send a list of strings from an Elixir/Erlang node to a Java node, but you cannot get it to de-serialize properly.
Elixir has its own types (e.g., atoms, tuples, ..) and Java has its own types (e.g., Object, String, List<String>,..). There needs to be a conversion from the one type to the other if they're supposed to talk to each other. In the end it's just a bunch of 1's and 0's that get sent over the wire anyway.
If an Erlang list is sent to Java, what arrives can always be interpreted as an OtpErlangObject. It's up to you to then try and guess what the actual type is before we can even begin turning it into a Java value.
// We know that everything is at least an OtpErlangObject value!
OtpErlangObject o = mbox.receive();
But given that you know that it's in fact a list, we can turn it into an OtpErlangList value.
// We know o is an Erlang list!
OtpErlangList erlList = (OtpErlangList) o;
The elements of this list however, are still unknown. So at this point its still a list of OtpErlangObjects.
But, we know that it's a list of strings, so we can interpret the list of OtpErlangObjects as list of OtpErlangStrings, and convert those to Java strings.
public static List<String> ErlangListToStringList(OtpErlangList estrs) {
OtpErlangObject[] erlObjs = estrs.elements();
List<String> strs = new LinkedList<String>();
for (OtpErlangObject erlO : erlObjs) {
strs.add(erlO.toString());
}
return strs;
}
Note that I used the term list here a lot, because it's in fact an Erlang list, in Java it's all represented as an array!
My entire code is listed below.
The way to run this is to paste it into a Java IDE, and start a REPL with the following parameters:
iex --name bob#127.0.0.1 --cookie "secret"
Java part:
import com.ericsson.otp.erlang.*;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
public class Main {
public static OtpErlangList StringListToErlangList(List<String> strs) {
OtpErlangObject[] elems = new OtpErlangObject[strs.size()];
int idx = 0;
for (String str : strs) {
elems[idx] = new OtpErlangString(str);
idx++;
}
return new OtpErlangList(elems);
}
public static List<String> ErlangListToStringList(OtpErlangList estrs) {
OtpErlangObject[] erlObjs = estrs.elements();
List<String> strs = new LinkedList<String>();
for (OtpErlangObject erlO : erlObjs) {
strs.add(erlO.toString());
}
return strs;
}
public static void main(String[] args) throws IOException, InterruptedException {
// Do some initial setup.
OtpNode node = new OtpNode("alice", "secret");
OtpMbox mbox = node.createMbox();
mbox.registerName("alice");
// Check that the remote node is actually online.
if (node.ping("bob#127.0.0.1", 2000)) {
System.out.println("remote is up");
} else {
System.out.println("remote is not up");
}
// Create the list of strings that needs to be sent to the other node.
List<String> strs = new LinkedList<String>();
strs.add("foo");
strs.add("bar");
OtpErlangList erlangStrs = StringListToErlangList(strs);
// Create a tuple so the other node can reply to use.
OtpErlangObject[] msg = new OtpErlangObject[2];
msg[0] = mbox.self();
msg[1] = erlangStrs;
OtpErlangTuple tuple = new OtpErlangTuple(msg);
// Send the tuple to the other node.
mbox.send("echo", "bob#127.0.0.1", tuple);
// Await the reply.
while (true) {
try {
System.out.println("Waiting for response!");
OtpErlangObject o = mbox.receive();
if (o instanceof OtpErlangList) {
OtpErlangList erlList = (OtpErlangList) o;
List<String> receivedStrings = ErlangListToStringList(erlList);
for (String s : receivedStrings) {
System.out.println(s);
}
}
if (o instanceof OtpErlangTuple) {
OtpErlangTuple m = (OtpErlangTuple) o;
OtpErlangPid from = (OtpErlangPid) (m.elementAt(0));
OtpErlangList value = (OtpErlangList) m.elementAt(1);
List<String> receivedStrings = ErlangListToStringList(value);
for (String s : receivedStrings) {
System.out.println(s);
}
}
} catch (OtpErlangExit otpErlangExit) {
otpErlangExit.printStackTrace();
} catch (OtpErlangDecodeException e) {
e.printStackTrace();
}
}
}
}
I have written an application that retrieves Active Directory groups and flattens them, i.e. includes recursively members of subgroup to the top parent group.
It works fine for small groups, but with larger groups I am facing a problem.
If number of members does not exceed 1500, they are listed in the member attribute. If there are more - then this attribute is empty and attribute with name member;range:0-1499 appears, containing first 1500 members.
My problem that I don't know how to get the rest of member set over 1500.
We have groups with 8-12 thousand members. Do I need to run another query?
On the Microsoft site I have seen C# code snippet on the similar matter, but couldn't make much sense of it, as they were showing how to specify a range, but not how to plug it into query. If someone knows how to do it in Java, I'd appreciate a tip.
This will obviously give you the next ones:
String[] returnedAtts = { "member;range=1500-2999" };
You need to fetch the users chunk by chunk (1500 chunks) Just make a counter and update you search and retrieve the next ones until you have all of them.
With your help I have a full working code
// Initialize
LdapContext ldapContext = null;
NamingEnumeration<SearchResult> results = null;
NamingEnumeration<?> members = null;
try {
// Initialize properties
Properties properties = new Properties();
properties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
properties.put(Context.PROVIDER_URL, "ldap://" + ldapUrl);
properties.put(Context.SECURITY_PRINCIPAL, adminLoginADOnPremise);
properties.put(Context.SECURITY_CREDENTIALS, adminPasswordADOnPremise);
// Initialize ldap context
ldapContext = new InitialLdapContext(properties, null);
int range = 0;
boolean finish = false;
while (finish != true) {
// Set search controls
SearchControls searchCtls = new SearchControls();
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
searchCtls.setReturningAttributes(generateRangeArray(range));
// Get results
results = ldapContext.search(ldapBaseDn, String.format("(samAccountName=%s)", groupName), searchCtls);
if (results.hasMoreElements() == true) {
SearchResult result = results.next();
try {
members = result.getAttributes().get(generateRangeString(range)).getAll();
while (members.hasMore()) {
String distinguishedName = (String) members.next();
logger.debug(distinguishedName);
}
range++;
} catch (Exception e) {
// Fails means there is no more result
finish = true;
}
}
}
} catch (NamingException e) {
logger.error(e.getMessage());
throw new Exception(e.getMessage());
} finally {
if (ldapContext != null) {
ldapContext.close();
}
if (results != null) {
results.close();
}
}
Two functions missing from the working code example by #Nicolas, I guess they would be something like:
public static String[] generateRangeArray(int i) {
String range = "member;range=" + i * 1500 + "-" + ((i + 1) * 1500 - 1);
String[] returnedAtts = { range };
return returnedAtts;
}
public static String generateRangeString(int i) {
String range = "member;range=" + i * 1500 + "-" + ((i + 1) * 1500 - 1);
return range;
}
The code does not handle the case if the AD group is not so large that the member attribute actually needs to be "chunked", that is if the "member" attribute exists instead.
Given this list as array of strings:
Cars/Truck/Regular
Cars/Truck/4x4/Lifted
Cars/Jeep/4x4
Cars/Convertable
How do I parse this into json like:
[{"name":"Cars","nodes":[{"name":"Truck","nodes":[{"name":"Regular","nodes":[]},{"name":"4x4","nodes":[{"name":"Lifted","nodes":[]}]}]},{"name":"Jeep","nodes":[{"name":"4x4","nodes":[]}]},{"name":"Convertable","nodes":[]}]}]
So far this all I have for recursive going through. Now will have to figure out the node class part.
String[] li = new String[4];
li[0] = "Cars/Truck/Regular";
li[1] = "Cars/Truck/4x4/Lifted";
li[2] = "Cars/Jeep/4x4";
li[3] = "Cars/Convertable";
doAll(li);
public void doAll(String[] files) {
try {
for (String file : files) {
String[] f = file.split("/");
if (f.length>1) {
logger.info("directory:"+file);
f = (String[]) ArrayUtils.removeElement(f, f[0]);
String temp = "";
for(String ff : f) temp=temp+"/"+ff;
temp = temp.replaceFirst("/", "");
doAll(new String[]{temp});
} else if(f.length==1){
logger.info("file:"+file);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
produces:
directory:Cars/Truck/Regular
directory:Truck/Regular
file:Regular
directory:Cars/Truck/4x4/Lifted
directory:Truck/4x4/Lifted
directory:4x4/Lifted
file:Lifted
directory:Cars/Jeep/4x4
directory:Jeep/4x4
file:4x4
directory:Cars/Convertable
file:Convertable
Probably you have to create a structure to holding those data first, then feed all data into the structure, serialize the result to json string at end. Pseudo code something like:
class Node {
String name;
List<Node> nodes; // sub nodes
/*
Add new node under current node.
*/
public void addNode(Node node) {...}
}
You start from the very first value say Car to create the structure, then try to recursively find left values say /Truck, /Truck/Regular in it's sub (or sub's sub) nodes. If found, do nothing, else you create a new sub node and add it to the node you last found.
I'm making a j2me Bluetooth application. I'm also new in java world. Where I have to display a Bluetooth service name to the user. So far it seems all is working correctly except service name. I verified my Bluetooth server is advertising service name correctly by other client (done by qt). I tried as follows:-
public void commandAction(Command command, Item item) {
if (item == deviceChoiceGroup) {
if (command == servicesDiscoverCommand) {
if(deviceList.size()==0) {
return;
}
UUID[] searchList = new UUID[1];
searchList[0] = new UUID("11111111111111111111111111111111",false);
int[] attrSet = new int[1];
attrSet[0] = 0x100;
RemoteDevice currentDevice =
(RemoteDevice) deviceList.elementAt(
getDeviceChoiceGroup().getSelectedIndex());
if(currentDevice == null) {
return;
}
try {
transactionID = bluetoothDiscoveryAgent.searchServices(
new int[] {0x100}, searchList, currentDevice, this);
printToForm("Start services under L2CAP searching...");
form.addCommand(cancelServicesDiscoverCommand);
} catch (BluetoothStateException e) {
//TODO: write handler code
}
}
}
}
public void servicesDiscovered(int transID, ServiceRecord[] serviceRecords){
if (serviceRecords.length>0 && serviceRecords!=null)
{
connectionURL=serviceRecords[0].getConnectionURL(0, false);
int[] ids=serviceRecords[0].getAttributeIDs();
DataElement ServiceName=serviceRecords[0].getAttributeValue(ids[1]);
// tried to convert objedct to string.
String str = (ServiceName.getValue()).toString();
// out is put is like java.util.vector$1#3c60cd14c
printToForm("#Service name: "+str);
printToForm("The Service name is: "+ServiceName.getValue());
}
}
"DataElement.getValue()" which returns object. Thus I can see service name as "java.util.vector$1#3c60cd14c". I tried to convert object to string as "String str = (ServiceName.getValue()).toString();" It doesn't convert correctly.
So how to convert object to string. So that I could see the service name in plain text. Thanks!
By seeing the result : java.util.vector$1#3c60cd14c i guess the returned object's type is Vector.
So try to cast to the Vector and iterate through it to get the values.
Iterator itr = serviceName.getValue().iterator();//do something here
System.out.println("Iterating through Vector elements...");
while(itr.hasNext())
System.out.println(itr.next());