0

This may be a bit more complicated than it needs to be, but I am using:

  • mongoDB
  • Spark
  • Freemarker
  • java

I cannot figure out how to access embedded data in mongodb to print on the website. I want to be able to look through the records, find which interface has a "label" with the value "MGMT" and then print the info for that interface. The problem is, I embed them in a "networking" collection. How do I access that info?

Important lines of Code:

java:

try {
                        Template helloTemplate = configuration.getTemplate("hello.ftl");

                        DBObject document = collection.findOne();



                        helloTemplate.process(document, writer);

                        System.out.println(writer);
                    } catch (Exception e) {
                        halt(500);
                        e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
                    }

Full Code:

.java:

import com.mongodb.*;
import freemarker.template.Configuration;
import freemarker.template.Template;
import spark.Request;
import spark.Response;
import spark.Route;
import spark.Spark;

import java.io.StringWriter;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;

/**
 * Created with IntelliJ IDEA.
 * User: sysrjt1
 * Date: 6/13/13
 * Time: 1:09 PM
 * To change this template use File | Settings | File Templates.
 */
public class HelloWorldMongoDBSparkFreemarkerSyle {
    public static void main(String[] args) {
        final Configuration configuration = new Configuration();
        configuration.setClassForTemplateLoading(HelloWorldMongoDBSparkFreemarkerSyle.class, "/");

        MongoClient client = null;
        try {
            client = new MongoClient(new ServerAddress("localhost", 27017 ));
        } catch (UnknownHostException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }

        DB database = client.getDB("systems");
        final DBCollection collection;
        collection = database.getCollection("systems");

        Spark.get(new Route("/") {
            @Override
            public Object handle(Request request, Response response) {
                StringWriter writer = new StringWriter();
                try {
                    Template helloTemplate = configuration.getTemplate("hello.ftl");

                    DBObject document = collection.findOne();



                    helloTemplate.process(document, writer);

                    System.out.println(writer);
                } catch (Exception e) {
                    halt(500);
                    e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
                }
                return writer;
            }
        });
    }
}

my HTML template (hello.ftl):

<!DOCTYPE html>
<html>
<head>
    <title>Welcome!</title>
</head>
<body>
    <table>
        <thead>
            <tr>
                <th>Name</th>
                <th>Management IP</th>
                <th>RAM</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>${name}</td>
                <td>${ipAddr}</td> <! -- this line fails -->
                <td>${ram}</td>
            </tr>
        </tbody>
    </table>

     <h1>Server Name: ${name}</h1>
</body>
</html>

from mongodb:

> db.systems.findOne()
{
        "_id" : ObjectId("51b9dde5d05f675ad37ac990"),
        "name" : "abcdev01",
        "networking" : {
                "interface1" : {
                        "name" : "eth0",
                        "ipAddr" : "12.34.45.56",
                        "sNet" : "255.255.255.0",
                        "label" : "MGMT"
                },
                "interface2" : {
                        "name" : "eth1",
                        "ipAddr" : "1.2.3.4"
                }
        },
        "ram" : 102390
}
Jeff
  • 4,285
  • 15
  • 63
  • 115

2 Answers2

1

I guess (without knowing MongoDB) that the failing line should be something like ${networking.interface1.ipAddr}. But probably you want to list all the interfaces. Then maybe you could do:

<#list networing.keySet() as ifname>
  ...
  ${networking[ifname].ipAddr}
  ...
</#list>

Although, if you only want to print the information about the interface with a given name, you should issue the proper query with the MongoDB API, and only pass that single interface object to FreeMarker.

ddekany
  • 29,656
  • 4
  • 57
  • 64
  • Ya, I'll want to list all of them eventually, but at first I'll need to check the type of them and if the type matches a specific one, then grab the IP. I'm not familiar with this code... Is it a java datatype or a freemarker datatype? – Jeff Jun 14 '13 at 12:18
  • Not sure what do you mean... `keySet()` is a Java method of the MonogoDB object. The others are FreeMarker-ish shorthands for Java `get(ifname)` and `get("ipAddress")`. As of the filtering, those should be done in Java before you pass the object(s) to the template (because, MVC). (Although if you really have to, you can do it with `<#if someCondition>...#if>`-s in the template too.) – ddekany Jun 14 '13 at 14:44
0

I figured out what I needed (I think) for the java file:

imports...
public class PACManTest {
    public static void main(String[] args) throws UnknownHostException {
        final Configuration configuration = new Configuration();
        configuration.setClassForTemplateLoading(PACManTest.class, "/");

        MongoClient client = new MongoClient(new ServerAddress("localhost", 27017 ));

        DB database = client.getDB("pacman");
        final DBCollection collection = database.getCollection("servers");

        DBObject document = collection.findOne();
        DBObject tempDoc = new BasicDBObject(); //THIS LINE    

//BELOW FROM : http://stackoverflow.com/questions/10345788/retrieve-sub-document-in-array-as-dbobjects

        BasicDBList networking = (BasicDBList) document.get("networking");
        //System.out.println("networking = " + networking.toString());
        BasicDBObject[] networkArr = networking.toArray(new BasicDBObject[0]);
        String mip = new String();
        for (BasicDBObject dbObj : networkArr) {
            if (dbObj.containsValue("MGMT")){
                 mip =  dbObj.get("ip").toString();
            } else {
               //nothing for right now
            }
        }
        tempDoc = new BasicDBObject().append("name", document.get("name")).append("ram", document.get("ram")).append("mip", mip);

final DBObject finalDocument = tempDoc; //put the temp doc into a final doc
        Spark.get(new Route("/") {
            @Override
            public Object handle(Request request, Response response) {
                StringWriter writer = new StringWriter();
                try {
                    Template PACManTemplate = configuration.getTemplate("pacman.ftl");
                    PACManTemplate.process(finalDocument, writer); //write to the template
                } catch (Exception e) {
                    halt(500);
                    e.printStackTrace();
                }
                return writer;
            }
        });

    }
}

This works with the given ftl:

<table>
    <thead>
    <tr>
        <th>Name</th>
        <th>MGMT IP</th>
        <th>Ram</th>
    </tr>
    </thead>
    <tbody>
        <tr>
            <td>${name}</td>
            <td>${mip}</td>
            <td>${ram}</td>
        </tr>
    </tbody>
</table>

I still have to figure out how to do this with multiple objects... I am thinking DBObjectList or maybe a while(cursor.hasNext()) or something, but this is an answer to this current question.

Jeff
  • 4,285
  • 15
  • 63
  • 115
  • 1
    You can just create a `Map` (or a JavaBean) and use it as the 1st parameter to `TemplateProcess`. I mean you don't *have* to create the data-model purely from MongoDB objects if it's inconvenient. Same with listings. You can just drop plain `List` or `array` into the data-model with a name (say, `interfaces`), and iterate through it like `<#list interfaces as interface>...#list>`. – ddekany Jun 16 '13 at 21:54
  • I was thinking about using a DBList, but if I use an array of DBObjects, you think that will work? Am I not understanding? I tried to call `PACManTemplate.process(finalDocument, writer);` two times with the same `finalDocument` variable declared. It just processed the template twice (two headers, two seperate tables). I need to almost create a list of documents and process the whole list at once? – Jeff Jun 17 '13 at 13:14
  • I guess you misunderstand something fundamental about how this all works. If you process the same template twice, of course the *whole* template will run twice. So you put *everything* into the data-model (that's the first parameter to `process`) that you want to *show*, and process the template once. The data-model could be `Map`, so you can put multiple things into it with a name. Like a `BasicDBList` or an array; `#list` doesn't care what it is as far as it knows how to iterate through it. – ddekany Jun 17 '13 at 13:41
  • Ah... I guess I have figured what the misunderstanding is here. `#list` can be used in the middle of the template, like `Hello ${name}! Here are your mails: <#list mails as mail>${mail.title} whatever...#list> Page footer ${currentDate} blah.`. – ddekany Jun 17 '13 at 13:45
  • Right. So do you send a list in to the template or do you process the list and send each item to a template somehow? – Jeff Jun 19 '13 at 16:25
  • 1
    You want to show a list of somethings on that web page, right? So then you send the list to the template. Along with the non-lists if needed (things from which there's only one shown on the web page). So the template can alone render the whole page. – ddekany Jun 19 '13 at 17:21