PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API Ravi Krishnaswamy – Autodesk Inc. CP2568 Learning Objectives At the end of this class, you will be able to: Create an AutoCAD Arx app with a live data connection to Google Docs Create an AutoCAD Arx app that syncs data from Google’s App Engine Identify data and schema mapping considerations in using a PaaS provider data store. Identify cost and scale issues to consider in connecting AutoCAD and its data to a PaaS provider About the Speaker Ravi Krishnaswamy is a Senior Software architect with the AutoCAD group. He is a long time developer and contributor to AutoCAD, starting with Release 12, with almost 19 years at Autodesk. He was part of the small team that introduced Arx to AutoCAD, and implemented among the first AcDb based entities in AutoCAD – including the Ellipse and Region entities. Over the years his contributions to the product have included – drawing compression, xref enhancements including notification and clipping, spatial indexing, CER, dynamic block evaluation, point cloud integration and oversight of several core architectural projects. He holds several patents in various areas of technology implemented in AutoCAD including spatial indexing , dynamic blocks and CER. His focus includes both internal product architecture and development, as well as sharing knowledge of the product and platform with end users and developers, communicating the huge potential of the AutoCAD platform in the context of computing paradigm shifts. Ravi has a PhD in computer science from Washington State University, and a BTech in computer science from the Indian Institute of Technology, Mumbai. Email: ravi.krishnaswamy@autodesk.com PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API 0. Contents: 1. 2. 3. 4. 5. 6. 7. Introduction Useful terms Tools, Libraries Learning objective 1: Arx application for Google Docs Learning objective 2: Googles Data store – and differences from traditional data stores Learning objective 3: Arx app for GAE Learning objective 4: Metering and costs of running a GAE application 1. Introduction A primary goal of this class is to show how to leverage powerful web service based platforms and applications in AutoCAD. At the end of this class, one should have the basic tools and knowledge to implement Arx applications with the cloud platform web services, such as the Google APIs or any other cloud ‘Platforms as a Service’. Note: This class includes advanced programming topics. The AutoCAD programming environment The C++ Arx api is the foundational platform for AutoCAD application development. It is a very broad api, and includes the AutoCAD Object Model, as well as apis for editor interaction. Since key built in objects such as Layers, Linetypes, TextStyles, and entities such as Text, Tables, Lines, and so on are fully exposed – with access to their properties, this gives the developer the opportunity to drive data definition from external sources. Ability to enforce standards and import external information such as tabular data are two examples of taking advantage of this level of programmatic access to the data. PaaS Several companies have made their web application, compute and server infrastructures accessible programmatically. These Platforms as a Service (PaaS) takes care of infrastructure. As a result global and highly scalable compute and storage resources accessible to a developer with very little coding effort. The developer only has to write the application, the platform takes care of the rest. Azure, GAE, Force.com are examples of PaaS. Course contents As an example of developing applications for a platform as a service, we will implement examples using Google’s doc apis and the App Engine apis. To address cross platform considerations, and since the App Engine apis are not .net based, we will be using open source 2 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API tools and libraries with the Java apis. For some of the client side apis, we will offer links and pointers to implementing the equivalent in .net. Since the sample applications involve significant amounts of code and different technologies, we will provide sample code that developers can reuse in their solutions. If you want to try out the samples, make sure to install the libraries and tools as described in this handout. 2. Useful terms These are a list of important terms and concepts that which will be mentioned during implementation of our sample applications. Term Definition AppEngine Google’s Platform for server side applications. It provides Java and Python apis which allows a developer to access the Google infrastructure where one can easily write and deploy their server side solution running on Google’s infrastructure. ApplicationID An ID you will register, once logged in to your google account, that identifies your application with the google platform. For google doc apis access – this is what is used to request permissions to access a users data. For the App Engine, the application id will be used for deployment and monitoring. ATOM/ Feed A protocol, originally started with RSS, that is a foundation for how an entity and a collection of entities are dealt with in cloud/web APIs Credentials A snippet of information generated by registering your application that will be used to Authenticate and Authorize your application to access a users data on Google Docs Data Link AutoCAD mechanism to link contents of a Table entity to sources of data. We will use this to hook our AutoCAD Table entity to a Google Docs spreadsheet. Eclipse Open source IDE we will use for Java development. Google API/GData Apis to google’s applications, including google spreadsheets 3 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API High Replication Datastore Googles NoSQL database solution that is one of the features accessible to developers who use the AppEngine OAuth2 Authorization protocol - used by an application to access resources from another application REST Representational State Transfer. A standard and framework to implement web service apis over http. We will be defining our REST apis for our Google App Engine application. Scopes Method to indicate when registering your application with Google what Google applications you will need access to (like Docs, Gmail, Google+) Table Entity The AutoCAD Table entity. We will use this to store google spreadsheet data in AutoCAD. Jersey A Java library we will use to make implementing REST apis in Java easy. Jetty A Java library we will use to implement a small embedded server for OAuth2 JNI Java Native Interface. We will use this to bridge from the C++ Arx world to Java Xref Graph A programmatically accessible structure that reflects which xref dwgs are referenced from the main drawing. We will use this to represent file dependencies in Googles Datastore. 3. Tools, Libraries In this section we will go through the list of tools and libraries for this class. Java For Java, we will use the Java Standard Edition that comes with the Java Development Kit (jdk) 1.7. This is downloadable at: http://www.oracle.com/technetwork/java/javase/downloads/jdk7u7-downloads-1836413.html Make sure to accept the license agreement, and pick the appropriate platform version you want to install. I use the 64 bit versions of all applications in this class. 4 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API NOTE: If you are developing the Google App Engine sample, and wish to deploy it to Google, you should also install the 1.6 (Java6) sdk. Eclipse Eclipse is the IDE we will use to develop our java applications. This is downloadable from: http://www.eclipse.org/downloads/ I used the Eclipse Classic version in this class. Visual Studio For visual studio, I use VS 2010 which is the recommended IDE for C++ development for AutoCAD 2013. 5 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API Setup for JNI Note that for tying Java into the C++ world of Arx, we will use JNI (java native interface). In order to make sure that the java runtime, the include files, jni libraries are accessible while building and running the applications, you need to make sure: 1. The Java Runtime and tools are on the system path: E.g. I added C:\Java\jre7\bin;C:\Java\jre7\bin\server To the PATH variable so that it is accessible by your application. The jre7\bin\server location is where jvm.dll lives, that will run the Java Virtual Machine inside your arx app. (you set environment variables in Control Panel\System and Security\System -> Advanced System Settings -> Environment Variables on win7). 2. In order to builds in Visual Studio, you need to set the include path to your JDK include files: 3. Set the linker path to the location of jvm.lib 6 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API 4. And specific jvm.lib in your .lib dependency list. Java Libraries We will use two java libraries in our applications to make development easy. 1. Jersey. This is to make it very easy to add REST apis to our Java application that will run on the Google App Engine (server). 2. Jetty. This is a very lightweight Java server that we will use for implementing OAuth2 on our client. Jersey: The homepage is at http://jersey.java.net You can download the .jar files in a zip at http://jersey.java.net/nonav/documentation/latest/chapter_deps.html I downloaded the zip file. Jetty: You can download this from the jetty home page 7 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API http://www.eclipse.org/jetty/downloads.php And, on clicking on the download link, you will get to this page, Further, click on the download link, to get to the following, where you can download the jar files for jetty 8 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API Google Libraries For ther Google Java libraries we have two categories of libraries 1. Google client apis. This is to access the Google Spreadsheet apis. a. These are links to download the OAuth2 and client libraries. http://code.google.com/p/google-api-java-client/wiki/OAuth2 http://code.google.com/p/google-api-java-client/wiki/Setup from which you should download the jar google-api-java-client-1.12.0-beta.zip or whichever one is current. http://code.google.com/p/google-api-java-client/wiki/APIs#Google_OAuth2_API If you click on the latter, pick the following to download the client apis. 9 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API b. You will also need the google GData libraries. These are located at http://code.google.com/p/gdata-java-client/downloads/list You will download the jars from the indicated link below (curiously denoted ‘gdata-src’ – but it actually contains the jars). 2. Google app engine libraries. You will need both the Java app engine SDK as well as the plugin for Eclipse. a. You can download the App Engine SDK for java at 10 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API https://developers.google.com/appengine/downloads b. To install the eclipse plugin for Google’s App Engine, – follow the instructions at: https://developers.google.com/eclipse/docs/download (I used the following link for my install in Eclipse. https://developers.google.com/eclipse/docs/install-eclipse-4.2 At the time you implement, there could be a newer version (this applies to all the libraries mentioned in this document).) It should take a few hours to download and install these components. Most are zip/jar files – i.e. you just download them and place them in a directory. Once you’re done, you should have all the components you need to implement powerful AutoCAD Arx apps using the Google APIs! ARX Development: Classes The ObjectARX sdk is what you will need if you are implementing your app as an arx extension to AutoCAD. http://usa.autodesk.com/adsk/servlet/index?id=773204&siteID=123112 11 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API 4. Learning objective 1: Google Docs In this section, we will use the client apis to access Google spreadsheets. The AutoCAD entity we will use is a table entity. We could chose to map spreadsheet data to any other AutoCAD entity/object as well. A Table entity is the most natural mapping to a spreadsheet table, of course. Google Account You will need to create a google account if you don’t already have one. http://accounts.google.com Registering an Application Id In order to implement a client side app accessing the apis, you will need to register the app. Once logged in to your account, if you go to https://code.google.com/apis/console you will be able to create an app id. You can create a project, i.e. Where you will get to select services you want access for. 12 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API We select the Drive api. Next you click on the ApiAccess menu item, and when prompted at the next screen, pick ‘Create an OAuth2 client Id’. Click Next, and, at the ‘Create Client ID’ dialog, select installed application and ‘other’ for installed application type. 13 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API Creating Client ID and Secret The following page, when you click Create Client Id is important, since this will give you the authentication/authorization information: If you click on the ‘Download Json’ link, you will get: {"installed": {"auth_uri":"https://accounts.google.com/o/oauth2/auth", "client_secret":"IKG_U6NwJ9-VeKlAywRR1Bqm", 14 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API "token_uri":"https://accounts.google.com/o/oauth2/token", "client_email":"", "redirect_uris":["urn:ietf:wg:oauth:2.0:oob","oob"], "client_x509_cert_url":"","client_id": "750928103016.apps.googleusercontent.com","auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs"} } This JSON packet will be what is in the code, as part of what we send to authenticate the client Scope and Google App Apis There are two parts to OAuth2 validation, passing the credentials, and requesting scope of access. There is a decent overview of scopes and api access at https://developers.google.com/academy/apis/drive/drive-apps/auth/scopes For spreadsheet specific access, we get the scopes for spreadsheets at https://developers.google.com/google-apps/spreadsheets/ So – our Java code has the document and spreadsheet access scope as below. private static final String[] scopes = { "https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/userinfo.email", "https://docs.google.com/feeds", "https://spreadsheets.google.com/feeds" }; At this point, you’ve registered the application, obtained the Authorization credentials, defined the scopes you want your application to access. You have all the pieces, finally, to implement your app! 15 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API Getting Started: Let’s get started with implementing the Arx app. We will break the app into 4 distinct pieces: Authorizer, SpreadSheets, JNI and arx. App: Authorizer The main purpose of Authorization is to obtain an ‘AccessToken’ – which is special string, that needs to be sent with every Google API request when getting/setting resources from the user’s google assets (in our case spreadsheets). Authentication/Authorization is a fairly detailed topic in itself, and merits a separate class to get into details. The reader is referred to the google link at https://developers.google.com/accounts/docs/OAuth2 for an in depth discussion. See the section on Installed Apps for the logic our app uses. We have implemented an OAuth2 app, based on Google samples. The app is posted as a runnable jar googleoauth2.jar in the samples Zip file PaaSApps.zip posted as part of the class materials. This is implemented as a stand alone java app, with the class com.adsk.oauth2.Authorizer with the following main public apis: public static int createOAuth2Authorizer() public static int setJsonCredentials(int authIndex, String jsonCredentials) public static int setScopes(int authIndex, String[] scopes) public static int authorize(int authIndex) public static String getAccessToken(int authIndex) createOAuth2Authorizer: Creates an instance of the Authorizer class, returning the id that is used as the authIndex for the subsequent Api calls. setJsonCredentials: Sets the JSON credential string that we obtained when we registered our app with Google as an installable app. setScopes: Sets the scope strings of what the app is requesting access to. authorize: Method that performs the authorization. This will invoke the browser instance, and the user will enter authorization information. The method performs the OAuth2 authorization, and returns token information to the internal class. The access token is used with the Spreadsheet connection object, and internally, is passed with every api call. 16 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API To view source of this project, this eclipse project is as a zip archive googleoauth2.zip - you can import into Eclipse jar. It is in the samples Zip file PaaSApps.zip posted as part of the class materials. If you run the main() of this app, it will prompt the user to log in, and once logged in, you will see a request for access to user information based on your app setup: This is where your app credentials and scopes are used by the google authorization process to gain access to a users data with their permission. App: SpreadSheet The Spreadsheet app builds on googleoauth2.jar to read data from the spreadsheet once access is granted by the user. It is implemented as a runnable jar googledocs.jar, with the main class com.adsk.googledocs.Spreadsheet, that exposes the following main apis: 17 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API public static int createSpreadsheetConnection() public static String[] getSpreadsheetNames(int conn) public static String[] getWorksheetNames(int conn, String ssName) public static String[][] getContents(int conn, String ssName, String wksName) public static int setContents(int conn, String ssName, String wksName, int rows[], int columns[], String values[]) createSpreadsheetConnection: establishes a connection to a user’s google docs account, returning the id used to query/set spreadsheet and worksheet information. It uses the Authorizer’s authorize() method from the previous app to do this. getSpreadsheetNames: returns the list of spreadsheets for that user getWorksheetNames: returns the list of worksheets for a given spreadsheet getContents: gets all the worksheet contents as a 2 dimensional array. setContents: sets the contents for the specified row, column and value lists All of the apis below use the Authorizer to get the access token and set it to the Spreadsheet connection. String accessToken = Authorizer.getAccessToken(mAuthorizer); mSpreadsheetService.setHeader("Authorization", "Bearer " + accessToken); The following classes are used to access and modify spreadsheets: import import import import import import import import import import import import com.google.gdata.data.Link; com.google.gdata.client.spreadsheet.SpreadsheetService; com.google.gdata.data.spreadsheet.SpreadsheetFeed; com.google.gdata.data.spreadsheet.SpreadsheetEntry; com.google.gdata.data.spreadsheet.WorksheetEntry; com.google.gdata.data.spreadsheet.ListFeed; com.google.gdata.data.spreadsheet.ListEntry; com.google.gdata.data.spreadsheet.CellFeed; com.google.gdata.data.spreadsheet.CellEntry; com.google.gdata.data.batch.BatchOperationType; com.google.gdata.data.batch.BatchStatus; com.google.gdata.data.batch.BatchUtils; The class names are self-explanatory. It is best to browse the sample code to view the methods used on these classes. 18 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API To view source of this project, this eclipse project is as a zip archive googledocs.zip - you can import into Eclipse using the Menu selection: File Import Existing Project From Archive – and select the .zip file. The file archive jar is in the samples Zip file PaaSApps.zip posted as part of the class materials. The api protocol is documented at https://developers.google.com/google-apps/spreadsheets/. Where the reader is encouraged to look at the samples for the various classes used. It is instructive to note that – both the Java and .Net apis are built on top of the same basic atom/feed protocol: If you execute the runnable jar googledocs.jar, its main will does a few basic tests – including connecting to the users account, displaying the spreadsheets and modifying a couple of cells. For more details, look at the com.adsk.googledocs.SpreadSheet main() method in the sample application. You need to export the projects as runnable jars for the next step of being able to load these applications in your C++ application. 19 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API For this choose File Export and pick runnable jar. And on the next button, pick the ‘copy the required libraries into a sub folder next to the generated Jar’ option. This will create a .jar file with the libraries it uses available. App: JNI To connect the Java apps to C++, which we need to do in order to implement our Arx application, we use JNI (Java Native Interface). 20 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API The command jnip prints out the signatures of the exported functions you will call from C++, e.g. C:\juno_workspaces\googledocs>c:\Java\jdk1.7.0_07\bin\javap -s classpath ./bin com/adsk/googledocs/SpreadSheet Compiled from "SpreadSheet.java" public class com.adsk.googledocs.SpreadSheet { … public int registerCredentialsAndScopes(java.lang.String, java.lang.String[]); Signature: (Ljava/lang/String;[Ljava/lang/String;)I … ; If you look at the posted sample in the samples Zip file PaaSApps.zip posted as part of the class materials the file gdocsjni.cpp has the following apis using namespace std; int gdocCreateSpreadsheetConnection(); int gdocGetWorksheets(int conn, const string& spreadsheet, vector<string>& names); int gdocGetSpreadsheets(int conn, vector<string>& names); int setWorksheetContents(int conn, const string& ss, const string& wks, const vector<int>& rows, const vector<int>& cols, const vector<string>& values); int getWorksheetContents (int conn, const string& ss, const string& wks, vector<vector<string>>& contents); which we will use to list spreadsheets and worksheets, get a worksheet’s contents, and set it. To run the JVM in the same process, we use the 21 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API AcDbTable apis. The AcDbTable entity is what we will use to represent the spreadsheet data. The AcDbTable entity has a rich set of apis. The reader is referred to the AcDbTable::setSize(), AcDbTable::setValue() and AcDbTable::value() methods as a method to get/set the Spreadsheet data to an AcDbTable entity. The following are code fragments for two Arx Commands that gets and sets a AcDbTable entity from a Google Docs spreadsheet. The full code sample is posted <here>. Getting the spreadsheet size and contents from the Google Spreadsheet: static void getTableInfo() { AcDbTable* pTable = getTable(); . . . int stat = gdocGetWorksheetContents(gConn, cstringToUTF8(css), cstringToUTF8(wks), contents); int numRows = (int) contents.size(); int numCols = (int) contents[0].size(); err = pTable->setSize(numRows, numCols); for (int r = 0; r < numRows; r++) { for (int c = 0; c < numCols; c++) { wchar_t* cellValue = utf8ToWchar(contents[r].at(c).c_str()); pTable->setValue(r, c, cellValue); } } . . . } Setting the spreadsheet size and content from the AcDbTable entity: static void setTableInfo() { AcDbTable* pTable = getTable(); . . . int numRows = pTable->numRows(); int numCols = pTable->numColumns(); if (numRows > 0 && numCols > 0) { vector<int> rows; vector<int> cols; vector<string> values; 22 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API for (int i=0; i < numRows; i++) { for (int j=0; j < numCols; j++) { rows.push_back(i + 1); // Indexing starts at 1 cols.push_back(j + 1); // for google spreadsheets AcValue val = pTable->value(i, j); val.convertTo(AcValue::kString, AcValue::kUnitless, true); const ACHAR* pStrVal; val.get(pStrVal); AcString acStr(pStrVal); string sVal = acStr; values.push_back(sVal); } } . . . int status = gdocSetWorksheetContents(gConn, cstringToUTF8(css), cstringToUTF8(wks), rows, cols, values); } pTable->close(); } Putting it all together For this class’s demo – I’ve implemented two ARX commands GDOCGETTABLE and GDOCSETTABLE that call the functions getTableInfo() and setTableInfo() illustrated above that get and set data from {user, spreadsheet, worksheet} combination. To determine the user and default spreadsheet/worksheet – the command GDOCSHEETINFO sets up the connection (gConn in the above example) by connecting to the users google account through OAuth2, and listing the users spreadsheets and worksheets. It sets the default spreadsheet/worksheet as the first one it finds. The JNI functions gdocCreateSpreadsheetConnection(), gdocGetSpreadsheets() and gdocGetWorksheets() are used to set the defaults. To Recap the steps: 1. We downloaded the JDK, downloaded and installed the libraries and SDKs, installed Eclipse and the App Engine plugin for eclipse. 2. We registered our app id in our google account, generating the client secret json. 3. We imported or created two eclipse Java projects: a. The OAuth2 project b. The google docs project And exported two runnable jars, selecting the option to copy referenced libraries to a subdirectory. 4. We created a VS2010 JNI project – with the C++ access to the java APIs, making sure the .jars created in step 3 are loaded. 23 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API 5. We created an ARX app, incorporating the JNI apis, and commands to get/set the spreadsheet data to the AcDbTable entity. 6. We set NEXTFIBERWORLD to 0 in AutoCAD, quit, and noted in the next session, FIBERWORLD was 0. We then ran the app. 5. Learning objective 2: Googles Data store Setting up a Google App Engine account The App Engine is where we run the server part of our application on the Google platform. https://developers.google.com/web-toolkit/doc/latest/tutorial/appengine This gives a step by step procedure of creating an account and the console. Anatomy of an App Engine app We will use eclipse for creating the Java based App Engine app. If you’ve installed the GAE plugin, under File New Project – if you pick ‘Google’ under that you will see ‘Web Application Project’. The details on the Java app engine project are in the link above. We use a non GWT based application for our example. High Replication Data Store In this exercise, we will be using Google’s so-called ‘High Replication Data Store’ NoSQL data base to store information. See https://developers.google.com/appengine/docs/java/datastore/ for details and an overview. Key concepts for the store are the notion of Entities – these are the referenceable objects that hold properties Properties – these are named properties and entity has Kind – this qualifies the type of entity being created Key - this is an object that lets you reference an entity. Details on the High Replication data store is a large topic in itself, beyond the scope of this class. It’s worth pointing out a few aspects, however. 24 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API Data Storage and retrieval , De Normalization, cost issues In traditional databases, one of the principles is to normalize data, i.e. avoid data copy or duplication. With stores such as the high replication store, we need to be concerned about costs, and retrieval and query costs are more than storage costs especially if done frequently so sometimes, storing a copy of data is not a bad thing. It’s also faster to retrieve a single Entity with many properties than breaking up that data across several entities, retrieving all of them. Spatial locality and efficiencies There are many articles on how to optimize for scalable applications. The following link is a source for such information. https://developers.google.com/appengine/articles/scaling/overview One of the points worth noting from the above discussion is the notion of an “Entity Group”. When you instantiate an Entity in the Store, if you supply a parent key – that creates the Entity in a ‘group’ under the parent. This means that any operation on one Entity will prevent operations on another Entity in the same group until the operation on the first one completes. This enforces transactional consistency, though does cause contention if simultaneous updates are common. Query The following link explains the query mechanisms for the Store. https://developers.google.com/appengine/docs/java/datastore/queries In our application, we use very simple queries based on common ancestor. The getAllOwnees() method of com.adsk.logger.store.AcQuery in the sample code in the project archived as googleappeng.zip implements the query. Included is the code below, illustrating how easy it is to query the data store for all Entities of a certain type owned by a specified entity . import com.google.appengine.api.datastore.*; public { Iterable<Entity> getAllOwnees(Key ownerKey, String kind) Query entsQuery; if (kind != null) entsQuery = new Query(kind); else entsQuery = new Query(); if (ownerKey != null) entsQuery.setAncestor(ownerKey); 25 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API PreparedQuery pq = gStore.prepare(entsQuery); return pq.asList(FetchOptions.Builder.withDefaults()); } 6. Learning objective 2: GAE In this exercise we will implement an Arx application that uses Googles Data Store to represent references between drawing files. While the item in the example is a dwg file – this app can be modified to represent references between any types of file or non file data, as we will see. Defining the reference problem If we define an item with two strings: ContentId. This would be a signature of the content of the data. In the case of a file – it could be a digital hash, or, as in our case, for a dwg – we will use the version GUID. For a dwg, if it is saved through RealDWG or AutoCAD - it is guaranteed that he Guid is changed. So two dwgs with the same Version GUID are guaranteed to have the same content. LocalPath. This is the file name. We can represent an item and its references as { c, l, {{c1, l1, c2, l2 …, cn, ln}}} where c, l are the content id and local path of the item, and the list c1,l1, c2, l2 etc. are the items referenced. If we enter this item and its references into the data store, where we use the <contentId, localPath> as the key for the datastore Entity, with children as its references, and at the same time, for each reference, store an Entity that corresponds to the reference, adding a child that is the entity referencing it. i.e. if we get two items and their references as {c1,l1, {{cref1, lref1}, {cref2, lref2}} and {c2, l2, {{cref2, lref2}, {cref3, lref3}}} In the Datastore, for the first item we create 3 entities with lines representing ownership (i.e. the children are created in the Datastore with the entity {c1, l1} as the ancestor). At the same time we add the Entities with the referenced items as root and the original item as the child. In the diagram below, Entities are represented by circles, and ancestor relationship by arrows. 26 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API cref1, lref1 cref2, lref2 cref1, lref1 c1, l1 cref2, lref2 c1, l1 c1, l1 Now, when the item {c2, l2, {{cref2, lref2}, {cref3, lref3}}} is added, the since Entity {cref2, lref2} is referenced both by {c1, l1} and {c2, l2}, {cref2, lref2} gets an additional child {c2, l2}. I.e. cref2, lref2 c1, l1 c2, l2 What this now gives us is the ability to find out, given an item (or file) – what are the referencers of the item. In our example, we store the additional information of timestamp and region (country, state, city) from which the item is added to the data store. With this information stored in the entity it is possible to perform the following queries: 1. List all the items that have different local paths (or names) but identical content. I.e. list duplicates 27 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API 2. 3. 4. 5. List all items referenced by a particular item. List all items referenced from a particular item. Qualify queries 1-3 by Date/Time. I.e. between specific dates. Qualify queries 1-3 by region. I.e. city/state/country. So this application can be used as an automatic auditing/tracking mechanism of content and references. Note we haven’t talked about deletion of data. To keep things simple, one approach is to purge Entities from the database for a given <contentId, localPath> combination that are older than a given date, keeping the latest version of the <contentId, localPath> items – both as the reference root and referencee root. If you look at the AcQuery methods: public Iterable<Entity> getObjectsByProperties( Key ownerKey, String kind, Map<String, Object> props) and public void deleteObjects(Iterable<Key> objKeys) With these two methods, delete operation in a REST api that, given a timestamp interval deletes all the root entities can be implemented. Classes to store reference The Eclipse project googleappeng contains the class LoggerApi that implements the REST api to create, store and retrieve the references trees. The Datastore Entities are created using the AcQuery class apis. Jersey and REST annotation In order to implement the REST apis very easily, we use the Jersey library. If you look at the LoggerApi class, you will notice that the rest Apis are implemented simply as methods: e.g. @Path("/adlogger/") public class LoggerApi { ... @POST @Consumes("application/xml, application/json") @Produces("application/xml, application/json") @Path("/userslog") public String putItem(@Context HttpServletRequest request, FileItem logInfo) 28 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API This method adds an item to the store. The annotations @POST, @Consumes, @Produces and @path define the url and the http verb. If you look at the web.ml file in the project under war\WEB-INF, you will see: <servlet> <servlet-name>Info Logger Servlet</servlet-name> <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer </servlet-class> <init-param> <param-name>com.sun.jersey.config.property.packages</param-name> <param-value>com.adsk.logger.binding</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Info Logger Servlet</servlet-name> <url-pattern>/resources/*</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>Info Logger Servlet</servlet-name> <url-pattern>/rest/*</url-pattern> </servlet-mapping> Which tells the app engine that the servlet for the Jersey is the handler at the http://acadgae.appspot.com/rest and – the rest of the path i.e. http://acadgae.appspot.com/rest/adlogger/userslog is defined by the annotation on the LoggerApi class and the putItem method. Note that the following in web.xml: <security-constraint> <web-resource-collection> <url-pattern>/rest/*</url-pattern> </web-resource-collection> <transport-guarantee>CONFIDENTIAL</transport-guarantee> </security-constraint> Also permits secure access to our REST api via https – which is what the client uses. The annotation element @POST indicates that when a POST is issued at the above URL, putItem is called. 29 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API Serialization/Deserialization In order to pass the file item you might have noticed the @Consumes annotation. This says that the method putItem() takes as the http body of the post either xml or json. The Jersey framework parses that into the FileItem argument to the putItem method. In order to be able to do this, the FileItem class is annotated as: @XmlRootElement(name = "FileItem") public class FileItem { Where the annotation @XmlRootElement makes this Java object serialized to xml via the javax xml binding. By just annotating the object, the Jersey libraries takes care of parsing the XML from http – and all we have to do is program in POJO (Plain Old Java Objects!). Client side On the client side, we use Jersey as well. If you look at the googleappengclient eclipse project, the methods createLogItem getDuplicatesOf getReferences getReferencesTo Implement REST calls using Jersey WebResource and Client objects. These classes make it very easy to build and issue Http GET, POST, DELETE and PUT requests. JNI Similar to the google docs api interfaces, interface to the logging methods is through jni calls in gdocsjni.cpp in the gdocs project. The methods string string string string string gaeCreateLogItem(const string& xml); gaeGetReferences(const string& contentId, const string& localPath); gaeGetReferencesTo(const string& contentId, const string& localPath); gaeGetDuplicatesOf(const string& contentId); gaeDeleteLog(); in gdocs.cpp are invoked to implement the arx command that interface to AutoCAD. 30 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API AutoCAD Xref graph The AcDbXrefGraph represents all the xrefs referenced from a host drawing. We use this to build the FileItem object that is sent to the GAE service. The AcDbXrefGraph is obtained by acedGetCurDwgXrefGraph() – and for just top level references, the root nodes out going references will give the desired list of references. Putting it together In the gdocs.arx app, we implement commands GAELOGREFS, GAEGETREFS, GAEGETREFSTO. Command GAELOGREFS logs a FileItem to GAE – with the host drawing as the root, and all the immediate references as the FileItem references. GAEGETREFS and GAEGETREFSTO when invoked, use the current drawing to determine which are its references, and which other files reference it respectively, based on the data posted to GAE. So to summarize, the steps were: 1. Create and setup an Eclipse project for GAE, including installing the GAE sdk and the GAE plugin. 2. Implement, using Jersey, REST apis that log a FileItem and its references in the High Replication Data Store. 3. Deploy that app to GAE from within eclipse. 4. Implement a Java client app using Jersey to invoke the REST apis to put a FileItem, and get references and references to a file item. 5. Implement a JNI layer to invoke the Java client apis 6. Integrate the JNI calls into the ARX app that implements the GAELOGREFS, GAEGETREFS and GAEGETREFSTO commands. 7. Run the arx app, making sure FIBERWORLD is 0. At this point – you have now created and ARX app that logs reference information to Googles High Replication Data Store! The samples, including prebuilt jars, for this application are in the samples Zip file PaaSApps.zip posted as part of the class materials. 31 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API 7. Learning objective 4: Metering and costs While in this class we run with in the ‘free’ limits for both the google docs apis and the Google App Engine apis, there are resource limits for both. Google APIs and Limits For the google apis – under the https://code.google.com/apis/console, you can navigate the resources available for each project you have: Some api services actually require google checkout/billing. Others require specific requests for increasing quota. Google App Engine Google App engine has many categories of resources including: Instance Costs Bandwidth Costs Database Operation costs Storage Costs 32 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API The following page https://cloud.google.com/pricing/ discusses cost issues. Once you create your App Engine account, you can view the resources consumed by your application, as well as detailed runtime information about your application. Your console dashboard at https://appengine.google.com/dashboard should look like: 33 PaaSt the Desktop: Implementing Cloud-Based Productivity Solutions with the AutoCAD® ObjectARX® API Where you can navigate detailed usage and billing information. Once again, the samples are posted in PaaSApps.zip. If you have further questions – I’d be happy to answer – my email is ravi.krishnaswamy@autodesk.com. 34