{"id":1239,"date":"2015-02-27T10:30:54","date_gmt":"2015-02-27T15:30:54","guid":{"rendered":"http:\/\/cs4760.csl.mtu.edu\/2015\/?page_id=1239"},"modified":"2016-01-08T16:09:47","modified_gmt":"2016-01-08T21:09:47","slug":"deploying-web-apps","status":"publish","type":"page","link":"https:\/\/cs4760.csl.mtu.edu\/2016\/lectures\/deploying-web-apps\/","title":{"rendered":"Deploying Web Apps"},"content":{"rendered":"<p>by\u00a0Robert Pastel, Mark Woodford and Travis Forester<\/p>\n<p>Deploying a web app is fundamentally the operation of packaging the files in your development environment and uploading the package to the server, but there are details of the application that you will need to attend to so that your application runs properly on the server. In particular you will need to:<\/p>\n<ul>\n<li>Configure the database for operation on the server<\/li>\n<li>Configure mail service to run on the server<\/li>\n<li>Configure any file paths that might differ between development and\u00a0production<\/li>\n<li>Package your application into a WAR<\/li>\n<li>Upload the WAR to a server and check that it is running properly<\/li>\n<\/ul>\n<h1>Terminology<\/h1>\n<p>I need to introduce some terminology before we can\u00a0configure and deploy of your applications. Grails can be configured to run differently in different &#8220;environments.&#8221; See<\/p>\n<p><a href=\"https:\/\/grails.org\/wiki\/environments\">https:\/\/grails.org\/wiki\/environments<\/a><\/p>\n<p><a href=\"http:\/\/grails.github.io\/grails-doc\/latest\/guide\/conf.html#environments\">http:\/\/grails.github.io\/grails-doc\/latest\/guide\/conf.html#environments<\/a><\/p>\n<p>Grails has basically three different environments:<\/p>\n<ul>\n<li>development<\/li>\n<li>test<\/li>\n<li>production<\/li>\n<\/ul>\n<p>Typically the <em>development<\/em> environment is the environment while you are programming on your computer at home. The <em>production<\/em> environment is typically a server that is accessed by the public. The <em>test<\/em> environment could be on a\u00a0server that is\u00a0setup to test\u00a0integration of many services and web apps. Although, a\u00a0development process should proceed from the development environment to the test environment and then finally made public in the production environment, we will shorten the process and use only the development and production environment. This implies that you will do final testing in the production environment.<\/p>\n<p>The second term to discuss is the &#8220;WAR.&#8221; WAR stands for &#8220;Web application ARchive.&#8221; It is basically a JAR file that has a specific directory structure for web applications.<\/p>\n<p><a href=\"http:\/\/en.wikipedia.org\/wiki\/WAR_(file_format)\">http:\/\/en.wikipedia.org\/wiki\/WAR_(file_format)<\/a><\/p>\n<p>In Java Server Page (JSP) technology, deployment proceeds by making a WAR file of your application and uploading the WAR file to a special directory on the server. The server un-packages the WAR, called exploding, knows how to interpret the files in your applications, and consequently can serve the files to the public. There are several JSP servers, see the list at Wikipedia<\/p>\n<p><a href=\"http:\/\/en.wikipedia.org\/wiki\/JavaServer_Pages\">http:\/\/en.wikipedia.org\/wiki\/JavaServer_Pages<\/a><\/p>\n<p>We will be using the<em> Apache Tomcat<\/em> server<\/p>\n<p><a href=\"http:\/\/en.wikipedia.org\/wiki\/Apache_Tomcat\">http:\/\/en.wikipedia.org\/wiki\/Apache_Tomcat<\/a><\/p>\n<p>Realize that Tomcat is in essences just a Java application. There is no special hardware associated with the server, but for the server to be public it needs be connected to the Internet and registered in a Domain Name Server (DNS).<\/p>\n<p>OK, that is enough terminology for now.<\/p>\n<h1>Configure the database for operation on the server<\/h1>\n<p>When developing a Grails application, there are a number of database options available. You may choose to use H2 (default), HSQLDB, MySQL, etc. In addition, you can choose how the database will be stored:<\/p>\n<ul>\n<li><strong>hosted on a server<\/strong>: a service such as mysqld, running separately from the web application and possibly on a different machine, handles requests from the application to create, read, update, or delete data within the database<\/li>\n<li><strong>embedded<\/strong>: the database is located within the application itself, and all operations to the database are done directly by the application<\/li>\n<\/ul>\n<p>For rapid prototyping, and for use cases in which the database accessed\u00a0relatively infrequent \u00a0(i.e. there are only a few connections at once) an embedded database will work just fine. For small projects, embedded databases have the advantage of being easy to initialize and maintain.<\/p>\n<p>Another design choice is whether to have the database stored on disk or in memory. If the database is expected to grow very large, then it would be better to store the database files on disk to prevent out-of-memory errors. Keeping the database in memory allows faster access times, but if something goes wrong, all data in memory will be lost. So, it makes sense that during development you would like to use a database that is stored in memory, so that you can quickly make and use the database. But during production you would like the database to be stored on the disk so that the data will persist between stopping and starting the web application.<\/p>\n<p>I now explain how to configure your Grails application to connect to an embedded H2 database stored on disk.<\/p>\n<p>Open the DataSource.groovy configuration file.<\/p>\n<p>Direct your attention to the dataSource settings at the beginning of the file. The property driverClassName should be \u201corg.h2.Driver\u201d. Also, note that the username and password for the database are defined here.<\/p>\n<p>Now locate the environment-specific settings. You should see code structure as below<\/p>\n<pre>environments {\r\n    development {\r\n        dataSource {\r\n            \u2026\r\n        }\r\n    }\r\n    test {\r\n        dataSource {\r\n            \u2026\r\n        }\r\n    }\r\n    production {\r\n        dataSource {\r\n           dbCreate = \"update\"\r\n           url = \u2026\r\n           properties {\r\n                \u2026\r\n           }\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<p>Grails allows you to configure specific settings for development, testing, and production environments. I suggest leaving the development and testing settings how they are. In the production settings, just above the url, insert the following line:<\/p>\n<pre>dbdir = \"${System.properties['catalina.base']}\/db\/${appName}\"<\/pre>\n<p>Lastly, modify the connection url to be:<\/p>\n<pre>url = \"jdbc:h2:file:${dbdir};MVCC=TRUE;LOCK_TIMEOUT=10000\"<\/pre>\n<p>Save the changes to DataSource.groovy. Now your Grails application is configured to connect to an on-disk H2 database at the filepath specified on the tomcat server. Now when you deploy your application (as a WAR file) to the tomcat server, a new H2 database with the given name will be created (if it doesn\u2019t already exist).<\/p>\n<p>For more details about how to configure the data source see the grails documentation<\/p>\n<p><a href=\"http:\/\/grails.github.io\/grails-doc\/latest\/guide\/conf.html#dataSource\">http:\/\/grails.github.io\/grails-doc\/latest\/guide\/conf.html#dataSource<\/a><\/p>\n<p>and for a summary on how data source is configured by environments<\/p>\n<p><a href=\"http:\/\/grails.github.io\/grails-doc\/latest\/guide\/conf.html#dataSourcesAndEnvironments\">http:\/\/grails.github.io\/grails-doc\/latest\/guide\/conf.html#dataSourcesAndEnvironments<\/a><\/p>\n<h3>Problems with Database?<\/h3>\n<p>Please try the above instructions first. Skip to the &#8220;Configure mail service to run on the server&#8221; section and continue reading.\u00a0Do not read this section until after you have tried deploy and have problems.<\/p>\n<p>If you have problems with deployment, check your server logs. If the logs suggest that problem is with the database creation, first check your server directory. Check that the directory db\/ directory is created with a file located in it:<\/p>\n<p style=\"padding-left: 30px;\">\u00a0\/usr\/share\/tomcat_team&lt;number&gt;_&lt;year&gt;01\/db\/<\/p>\n<p>where<\/p>\n<ul>\n<li>&lt;number&gt; is your team number<\/li>\n<li>&lt;year&gt; is the year of the class<\/li>\n<\/ul>\n<p>If the db\/ directory is not created. Try creating the db\/ directory and redeploying. If deployment problem still exist try hard coding the path to the database. Replace the &#8220;dbdir&#8221; assignment to:<\/p>\n<pre>dbdir = \"\/usr\/share\/tomcat_team&lt;number&gt;_&lt;year&gt;01\/db\/&lt;appname&gt;\"\r\n<\/pre>\n<p>Where<\/p>\n<ul>\n<li>&lt;number&gt; is your team number<\/li>\n<li>&lt;year&gt; is the year of the class<\/li>\n<li>&lt;appname&gt; is the name of your app<\/li>\n<\/ul>\n<p>If you are team 2 during the year 2016 making the app called DearWithIt, then the line should be:<\/p>\n<pre>dbdir = \"\/usr\/share\/tomcat_team2_2016501\/db\/DearWithIt\"<\/pre>\n<p>This hardcode\u00a0a filepath for the destination of the on-disk database. It will create a db\/ directory and add the database file named with your app name.\u00a0Please read more about how the filepath is specified in the section below called\u00a0&#8220;App Base&#8221;.<\/p>\n<h1>Configure mail service to run on the server<\/h1>\n<p>If your web application uses a mail service then you will need to configure it for the production mail server. If your application requires users to register in order to use the app then your web app should be using the mail server to send a key to the user&#8217;s email address so that they can finish the registration. This is done for two reasons. The email address is a unique identifier for the user. Also, responding to the email address assures that the user is a &#8220;real&#8221; person and not a robot because a real user can click on the url specified in email. Finally the mail service is used to send a key to the user when they forget their passwords and need to reset their passwords. If you are using Spring Security UI plugin in your application then the above registration and forgotten password procedures are implement in your web app, and your web app is using a mail server and the Grails Mail plugin.<\/p>\n<p>By default the Mail plugin is configured at localhost on port 25. The Mail plugin can be reconfigured in Config.groovy file. On the production server your application will use a special mtu email account. The email address is &#8220;<strong>mushroommailer@mtu.edu<\/strong>&#8221;\u00a0 with a password I&#8217;ll give you later. In the bottom of the Config.groovy file add the following code:<\/p>\n<pre>\/\/ Spring Security Email Configurations\r\ngrails {\r\n    \tmail {\r\n        \t\thost = \"smtp.gmail.com\"\r\n        \t\tport = 465\r\n        \t\tusername = \"mushroommailer@mtu.edu\"\r\n        \t\tpassword = \"actual-password\"\r\n        \t\tprops = [\"mail.smtp.auth\":\"true\",\r\n            \t\t\t\"mail.smtp.socketFactory.port\":\"465\",\r\n            \t\t\t\"mail.smtp.socketFactory.class\":\"javax.net.ssl.SSLSocketFactory\",\r\n            \t\t\t\"mail.smtp.socketFactory.fallback\":\"false\"]\r\n     }\r\n}\r\n<\/pre>\n<p>Note that you will have to replace &#8220;actual-password&#8221; with a password that I will give you via google docs.<\/p>\n<p>You can read more about configuring and using the Mail plugin at<\/p>\n<p><a href=\"http:\/\/grails.org\/plugins\/mail\">http:\/\/grails.org\/plugins\/mail<\/a><\/p>\n<h1>Configure any file paths that might differ between development and production<\/h1>\n<p>I suspect that <strong>most of the teams can skip this step<\/strong>. Meaning your production application is not using different file paths from your development paths. This is because you are storing uploaded files as blobs in the database. But sometimes databases are setup to store only the file path to the uploaded file rather than the complete file. If your database is configured like this then the file paths will be different on the development machine then on the production server. Below is technique to make it convenient to have different file paths.<\/p>\n<p>This is an example of the solution used to determine the photo upload location in the Mushroom Mapper app.<\/p>\n<p>A json file in the project (specifically, MushroomMapper\/web-app\/text\/photo_cfg.json) contains internal and external paths to use for the upload location, for both development and production environments:<\/p>\n<pre>{\r\n   \"production\": {\r\n   \t\t     \"server\": \"\/itss\/local\/home\/cittemp\/public_html\/photos\/\",\r\n   \t     \t\"client\": \"\/photos\/\"\r\n \t },\r\n  \t\"development\": {\r\n        \t\t\"server\": \"web-app\/images\/photos\/\",\r\n       \t\t \"client\": \"\/MushroomMapper\/images\/photos\/\"\r\n \t  }\r\n}\r\n<\/pre>\n<p>This file makes it easy to find and change the paths if necessary, without delving into the code.<\/p>\n<p>A function called initPhotos parses this file, and sets the current server and client variables depending on the current environment context. This function can be a method of a controller.\u00a0 In the case of mushroom mapper it is a method of the main controller, ObservationsController.groovy. The variables set by the function are servletContext.photos.server and servletContext.photos.client.<\/p>\n<pre>void initPhotos() {\r\n    def txt = servletContext.getResourceAsStream('text\/photo_cfg.json')\r\n    def cfg = new JsonSlurper().parseText(txt.getText())\r\n    if (Environment.current == Environment.PRODUCTION)  \r\n         servletContext.photos = cfg.production\r\n    else servletContext.photos = cfg.development\r\n}\r\n<\/pre>\n<p>The \u2018servletContext\u2019 variable (which is the \u2018application\u2019 variable when used in a gsp) behaves as a map whose data persists across all requests and sessions, so this function only needs to be called once for the lifetime of the server. Therefore, the following line is at the start of each action\/method of the controller that uses these variables:<\/p>\n<pre>if (servletContext.photos == null) initPhotos()\r\n<\/pre>\n<p>\u2018photos.server\u2019 is the internal path to the photos directory, and should be used with Groovy File objects, etc. \u2018photos.client\u2019 is the external uri, and should be used in the html, javascript, etc.<\/p>\n<p>Naturally, the user running the server should have +rwx permissions on the directory in question.<\/p>\n<h1>Package your application into a WAR<\/h1>\n<p>Packaging your application into a <em>WAR<\/em> only requires that the WAR file is named properly and that it is built for the correct environment. The WAR should be built for the production environment before uploading it to the production server.<\/p>\n<p>The WAR is named in BuildConfig.groovy configuration file. In the BuildConfig.groovy, uncomment the war.file specification. It is near the top of the file, and edit it so that it looks like<\/p>\n<pre>grails.project.war.file = \"target\/${appName}.war\"<\/pre>\n<p>Note we do not want the version number included in the war file name. The WAR file name must match the project name or the web app will not deploy properly. You can edit the WAR file name if you want without hurting the contents.\u00a0 Once your have configured the WAR name properly, you&#8217;ll not have to do this again.<\/p>\n<p>To make the WAR file you run a grails command. You&#8217;ll need to do this step each time you want to deploy a new version of your web application. On your development machine, your home machine, make the war file in production environment mode. Production environment is the default for the &#8220;war&#8221; command but not for the &#8220;run-war&#8221; command. The command executed in your project directory is simply<\/p>\n<pre>grails war<\/pre>\n<p>The build and compression will take several minutes. You will find the WAR file in your project\u2019s target\/ directory.<\/p>\n<h1>Uploading the WAR to your Server and Insure it is running<\/h1>\n<p>Tomcat servers have a special directory called the <em>appbase<\/em>. The WAR files are uploaded to the appbase directory and then tomcat will expand\/explode the WAR file in the directory. This deploys the web application. If there is already a WAR file with the same name then the old WAR file should be deleted before uploading the new WAR file. Tomcat will detect that the WAR file is deleted and delete the corresponding exploded web application. This is called undeploying. You must undeploy before redeploying.<\/p>\n<p>Because Tomcat is just a Java application, the machine running Tomcat can have multiple instances of Tomcat. In order that the Tomcat servers do not interfere with each other while serving pages, the different instances of the Tomcat server are configured to receive their request and send their responses through different port numbers. Recall\u00a0that while you were programming and testing your individual assignments you would point your browser to the URL<\/p>\n<pre>http:\/\/localhost:8080\/cs4760progassign\/<\/pre>\n<ul>\n<li>http: &#8211; represents the protocol, in this case hypertext transfer protocol<\/li>\n<li>localhost \u2013 is the domain name for the server, in this case it means the current machine<\/li>\n<li>8080 \u2013 is the port number<\/li>\n<\/ul>\n<p>Each team in the class has their own instance of Tomcat to use for deployment. But each Tomcat will be listening and sending to a different port.<\/p>\n<h2>Domain<\/h2>\n<p>The domain name for the machine with the tomcat instances is<\/p>\n<pre>cshci-dev.mtu.edu<\/pre>\n<h2>AppBase<\/h2>\n<p>To deploy your\u00a0 WAR file you need to know the appbase for your tomcat instances. The appBase for the tomcat intstances for each teams are at<\/p>\n<ul>\n<li>\/usr\/share\/tomcat_team1_201501\/webapps\/<\/li>\n<li>\/usr\/share\/tomcat_team1_201501\/webapps\/<\/li>\n<li>\/usr\/share\/tomcat_team3_201501\/webapps\/<\/li>\n<li>\u2026<\/li>\n<\/ul>\n<p>In other words the directory for the different tomcat instances have the format that specifies the &lt;team-number&gt;, &lt;year&gt; and &lt;semester&gt;. Team number is the your team number in the course.<\/p>\n<pre>tomcat_team&lt;team-number&gt;_&lt;year&gt;&lt;semester&gt;<\/pre>\n<p>The spring semester is &lt;semester&gt; = 01<\/p>\n<p>The year for 2015 is &lt;year&gt; = 2015. For the year 2016 it will be &lt;year&gt; = 2016, etc<\/p>\n<p>Each team has access only to their tomcat instance&#8217;s directories. If your explore your tomcat directories your should find the logs\/ directory.\u00a0 You may need to study the log files if you have errors after deploying. The log file that the errors will printed in are the catalina.***.log. A new log file is created each day. You\u00a0find most recent entry in the log file you scroll to the bottom of the log file.<\/p>\n<h2>Port Numbers<\/h2>\n<p>As mention above each tomcat instance is configured to listen on a different point.<\/p>\n<ul>\n<li>tomcat_team1 listens on port 8081<\/li>\n<li>tomcat_team2 listens on port 8082<\/li>\n<li>tomcat_team3 listens on port 8083<\/li>\n<li>etc.<\/li>\n<\/ul>\n<p>The general form of the port numbers is<\/p>\n<pre>808&lt;team-number&gt;<\/pre>\n<p>Where &lt;team-number&gt; is your course team number.<\/p>\n<p>So, when you check your deployed web application, each team will have to use their own port number. Team 1 would point their browser to<\/p>\n<pre>cshci-dev.mtu.edu:8081\/team-1-app-name<\/pre>\n<p>where &#8220;team-1-app-name&#8221; is the app name that team one has for their web application. Team 2 would point their browser to<\/p>\n<pre>cshci-dev.mtu.edu:8082\/team-2-app-name<\/pre>\n<p>etc.<\/p>\n<h2>Special Note about the number of WAR files<\/h2>\n<p>JSP and especially Grails web applications use lots of memory. They can also have a memory leak if they are not redeployed properly. The tomcat instances for the class have enough memory for only one additional web applications besides the web application that comes standard with tomcat.<\/p>\n<p>In your appbase you&#8217;ll notice that there are already some directories which are web applications. They are:<\/p>\n<ul>\n<li>default\/ &#8211; web app for bare domain<\/li>\n<li>docs\/ &#8211; documents for how to use tomcat<\/li>\n<li>examples\/ &#8211; example web applications<\/li>\n<li>host-manager\/ &#8211; web app for managing domain name<\/li>\n<li>manager\/ &#8211; web app for managing web apps<\/li>\n<\/ul>\n<p>Leave these web apps in your appBase. They do not consume much memory. You can access default, docs, and examples through your browser, and you should do so to check that your tomcat instances is running properly. You need a login to access the host-manager and the manager web app.<\/p>\n<p><strong>Remember: ONLY add one more web app to your appBase, otherwise your tomcat might run out of memory and crash.<\/strong><\/p>\n<h1>Uploading and Deploying<\/h1>\n<p>Using a SFTP client (WinSCP is a very good choice for Windows) login into the Tomcat machine. (See domain name above.) I highly recommend that you use RSA keys so that the client can auto log in during network distributions. If you are logging into the server from a machine not on MTU network then you need to use a virtual network (VPN) with mtu. Point a browser to<\/p>\n<pre>vpn.mtu.edu<\/pre>\n<p>Log in and then select &#8220;Standalone VPN client&#8221;. This will download &#8220;BIGIPEdgeClient.exe&#8221;. Run this executable file\u00a0in admin mode, and it will install the &#8220;BIG-IP Edge Client&#8221; to your apps. You run the client and click the &#8220;connect button.&#8221; A window will pop up asking for your username and password. After clicking the logon button, the VPN is created. You need to keep the app running while\u00a0use your\u00a0SFTP client to upload files to the server. If you do not have a VPN connection, you\u2019ll get an \u201cnetwork error\u201d and not be able to connect.<\/p>\n<p>In the SFTP client, navigate to the appBase. (See above for the appBase directory.)<\/p>\n<p>If you have previously deployed a WAR file for your\u00a0web app that you are currently deploying then it should still exist in the appBase alongside the exploded web app. The exploded wep app is a directory with the same name as the WAR file. <strong>Delete the old WAR in the appBase before uploading the new WAR.<\/strong><\/p>\n<p><strong>Wait<\/strong>. Wait until Tomcat deletes the old exploded web app. If you do not wait then deployment will fail, and Tomcat will never explode the new WAR file.<\/p>\n<p>When the old web app directory disappears, use your SFTP client to upload the new WAR file.<\/p>\n<p><strong>Wait<\/strong>. Sometimes Tomcat takes several minute, up to 10 minutes, to see the new WAR file and explode it.<\/p>\n<p>Point your browser to the web app and inspect it. (See above for port number.)<\/p>\n<h1>Something Goes Wrong<\/h1>\n<p>The above procedure can go wrong. Some typically difficulties are<\/p>\n<ol>\n<li>The database lockfile is not deleted during redeployment<\/li>\n<li>The exploded web application is not deleted during redeployment<\/li>\n<li>Memory overflow locking or crashing the server<\/li>\n<\/ol>\n<p>You can determine the cause of your problems by inspecting the log files. In most cases stopping and starting tomcat will solve your problems. Sometimes you may need to delete the offending file or directory, e.g. the lockfile in the db\/ directory.<\/p>\n<p>You have access to a few commands that are normally run by the root user. These commands are called sudo commands and are proceed by &#8220;sudo.&#8221;<\/p>\n<p>There are four commands<\/p>\n<ul>\n<li>start \u2013 to start tomcat<\/li>\n<li>stop \u2013 to spot tomcat<\/li>\n<li>restart \u2013 to stop and start<\/li>\n<li>status \u2013 to check tomcat status<\/li>\n<\/ul>\n<p>The frequently used commands are stop and start. You first <em>stop<\/em> tomcat then delete or inspect your files and finally you <em>start<\/em> tomcat again. After starting tomcat you can make a fresh deployment.<\/p>\n<p>Team 1 would run commands<\/p>\n<pre>sudo service tomcat_team1 stop\r\nsudo service tomcat_team1 start<\/pre>\n<p>Team 2 would use commands<\/p>\n<pre>sudo service tomcat_team2 stop\r\nsudo service tomcat_team2 start<\/pre>\n<p>etc.<\/p>\n<p><strong>Good luck<\/strong><\/p>\n","protected":false},"excerpt":{"rendered":"<p>by\u00a0Robert Pastel, Mark Woodford and Travis Forester Deploying a web app is fundamentally the operation of packaging the files in your development environment and uploading the package to the server, but there are details of the application that you will need to attend to so that your application runs properly on the server. In particular [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":0,"parent":112,"menu_order":15,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-1239","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/cs4760.csl.mtu.edu\/2016\/wp-json\/wp\/v2\/pages\/1239","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cs4760.csl.mtu.edu\/2016\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/cs4760.csl.mtu.edu\/2016\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/cs4760.csl.mtu.edu\/2016\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/cs4760.csl.mtu.edu\/2016\/wp-json\/wp\/v2\/comments?post=1239"}],"version-history":[{"count":20,"href":"https:\/\/cs4760.csl.mtu.edu\/2016\/wp-json\/wp\/v2\/pages\/1239\/revisions"}],"predecessor-version":[{"id":1535,"href":"https:\/\/cs4760.csl.mtu.edu\/2016\/wp-json\/wp\/v2\/pages\/1239\/revisions\/1535"}],"up":[{"embeddable":true,"href":"https:\/\/cs4760.csl.mtu.edu\/2016\/wp-json\/wp\/v2\/pages\/112"}],"wp:attachment":[{"href":"https:\/\/cs4760.csl.mtu.edu\/2016\/wp-json\/wp\/v2\/media?parent=1239"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}