http://ctp-ms.blogspot.com/2012/12/interoperability-between-java-and.html

 

Introduction

One of the main possibilities that provider hosted apps in SharePoint 2013 disclose is the possibility of seamless integration between SharePoint and external systems implemented in non-Microsoft technologies.

In this post are explored the basics of the integration between SharePoint on premises and a Java web application. The RESTfull web service API of SharePoint leverages the communication with the Java web application. TheSharePoint chrome control allows the Java web application to have the same styling of the hosted web site.

The TokenHelper.cs source file provides the token service authentication for provided hosted .Net applications. At the moment, it wasn’t found a Java implementation equivalent to the TokenHelper.cs for SharePoint on premises. Thus, it will be used NTLM authentication for the web service calls from the Java web application to SharePoint. This topic shall be revisited.

Find below an extract of the critical parts of the solution source code.

The Communication Layer with SharePoint

The Java web application reads and writes to a list of cars in the hosted web. The hosted web is called Interop1 and the list with data is called Cars. The list Cars has the columns: Brand, Model and Price.

The Java web application will be called Cars App.

The following are the dependencies of the Cars App in its Java implementation:

json-lib 2.4 helper classes to manipulate Json objects

resteasy-jaxrs 2.2.1.GA RESTEasy was used to access the RESTfull SharePoint API

commons-httpclient 3.1 dependency of RESTEasy

httpclient 4.1.1 dependency of RESTEasy

spring-web 2.5.6

spring-core 2.5.6

spring-webmvc 2.5.6

commons-logging 1.1.1

The payload class where the data about a car is stored could be as follows (file Car.java):

public class Car {

private String brand;

private String name;

private long price;

public String getBrand() {

return brand;

}

public void setBrand(String brand) {

this.brand = brand;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public long getPrice() {

return price;

}

public void setPrice(long price) {

this.price = price;

}

}

It has been defined the Car service in order to read and write into the Cars list. It has the declaration (file CarService.java):

public interface CarService {

public List<Car> getCars() throws CarException, HttpException, IOException ;

public long insertCar(Car car) throws CarException;

}

The Cars service implementation has the member variables (file CarServiceImpl.java). These member variables define the authentication parameters, the URIs for the web service endpoints in SharePoint and the field names of the Cars list.

String user = "YOUR-USER";

String pass = "YOUR-PASSWORD";

String urlList =

"http://SHAREPOINT-SERVER/sites/Interop1/_api/web/lists/getbytitle('Cars')/items";

String urlDigest = "http://SHAREPOINT-SERVER/sites/Interop1/_api/contextinfo";

// These tags are the ones that SharePoint uses to identify the columns in the

// list. They can be retrieved by making a HTTP Get to the server and

// checking the returning field names.

String brandTag = "x3aj";

String modelTag = "Title";

String priceTag = "Price";

The Cars service implements the following helper methods.

The helper method callRestEasyService reads data by performing a HTTP GET against the RESTfull API of SharePoint. The URI provided in the req parameter defines exactly what is being retrieved; it follows the OData protocol syntax. In our case it would be the items of the list Cars.

The result is returned in JSON format, as stated in “application/json; odata=verbose”. Also, it would be possible to return the result as XML by setting the format as “application/atom+xml”.

public String callRestEasyService(String req, String user, String pass) {

String output = "nothing";

try {

// For Kerberos authentication:

// Credentials credentials =

// new UsernamePasswordCredentials(user, pass);

NTCredentials credentials =

new NTCredentials(user, pass, "JAVA-MACHINE-NAME", "DOMAIN");

HttpClient httpClient = new HttpClient();

httpClient.getState().setCredentials(AuthScope.ANY, credentials);

httpClient.getParams().setAuthenticationPreemptive(true);

ClientExecutor clientExecutor =

new ApacheHttpClientExecutor(httpClient);

java.net.URI uri = new java.net.URI(req);

ClientRequestFactory fac =

new ClientRequestFactory(clientExecutor, uri);

ClientRequest request = fac.createRequest(req);

request.accept("application/json;odata=verbose");

ClientResponse<String> response = request.get(String.class);

if (response.getStatus() != 200) {

throw new RuntimeException("Failed : HTTP error code : " +

response.getStatus());

}

BufferedReader br = new BufferedReader(new InputStreamReader(

new ByteArrayInputStream(response.getEntity().getBytes())));

System.out.println("Output from Server .... \n");

while ((output = br.readLine()) != null) {

return output;

}

} catch (ClientProtocolException e) {

e.printStackTrace();

return e.getMessage();

} catch (IOException e) {

e.printStackTrace();

return e.getMessage();

} catch (Exception e) {

e.printStackTrace();

return e.getMessage();

}

return null;

}

In order to create objects in SharePoint through the RESTfull API (i.e., items in the Cars list), it’s needed to provide in the call a Form Digest. The Form Digest can be obtained through a POST to the hosted web endpoint “/_api/contextinfo”. The helper addRestEasyRetrieveDigest performs this POST and reads the Form Digest. The exact URI is defined in the variable urlDigest, already declared above.

Note the methods callRestEasyService and addRestEasyRetrieveDigest are practically the same, just the first does GET and the second POST.

public String callRestEasyRetrieveDigest (String req, String user, String pass) {

String output = "nothing";

try {

NTCredentials credentials =

new NTCredentials(user, pass, "JAVA-MACHINE-NAME", "DOMAIN");

HttpClient httpClient = new HttpClient();

httpClient.getState().setCredentials(AuthScope.ANY, credentials);

httpClient.getParams().setAuthenticationPreemptive(true);

ClientExecutor clientExecutor =

new ApacheHttpClientExecutor(httpClient);

java.net.URI uri = new java.net.URI(req);

ClientRequestFactory fac =

new ClientRequestFactory(clientExecutor, uri);

ClientRequest request = fac.createRequest(req);

request.accept("application/json;odata=verbose");

ClientResponse<String> response = request.post(String.class);

if (response.getStatus() != 200) {

throw new RuntimeException("Failed : HTTP error code : " +

response.getStatus());

}

BufferedReader br = new BufferedReader(new InputStreamReader(

new ByteArrayInputStream(response.getEntity().getBytes())));

System.out.println("Output from Server .... \n");

while ((output = br.readLine()) != null) {

return output;

}

} catch (ClientProtocolException e) {

e.printStackTrace();

return e.getMessage();

} catch (IOException e) {

e.printStackTrace();

return e.getMessage();

} catch (Exception e) {

e.printStackTrace();

return e.getMessage();

}

return null;

}

The helper addRestEasyPost allows the creation of objects in SharePoint through POSTs as stated in the OData protocol. This method is used to add cars to the list Cars.

The parameter req has the URI where to create the object. Here, it would be the URI of the Cars list.

Another detail regarding objects creation, it’s the need to state its type. In the Cars list, the list items are of type SP.Data.CarsListItem. The type naming convention is as follows:

SP.Data.<ListName>ListItem

In case of doubts, it’s possible to confirm the object type by performing a HTTP GET for the list items and checking their type.

public String addRestEasyPost(String req, String user, String pass, String digestValue, Car car) {

String output = "nothing";

try {

// For Kerberos authentication:

// Credentials credentials =

// new UsernamePasswordCredentials(user, pass);

NTCredentials credentials =

new NTCredentials(user, pass, "JAVA-MACHINE-NAME", "DOMAIN");

HttpClient httpClient = new HttpClient();

httpClient.getState().setCredentials(AuthScope.ANY, credentials);

httpClient.getParams().setAuthenticationPreemptive(true);

ClientExecutor clientExecutor =

new ApacheHttpClientExecutor(httpClient);

java.net.URI uri = new java.net.URI(req);

ClientRequestFactory fac =

new ClientRequestFactory(clientExecutor, uri);

ClientRequest request = fac.createRequest(req);

request.accept("application/json;odata=verbose");

request.header("content-type", "application/json;odata=verbose");

request.header("X-RequestDigest", digestValue);

JSONObject innerObject = new JSONObject();

innerObject.put("type", "SP.Data.CarsListItem");

JSONObject body = new JSONObject();

body.put("__metadata", innerObject);

body.put(brandTag, car.getBrand());

body.put(modelTag, car.getName());

body.put(priceTag, car.getPrice());

request.body("application/json", body.toString());

// This attribute was not needed (despite the documentation):

// request.header("content-length",

// request.getBody().toString().length());

ClientResponse<String> response = request.post(String.class);

if (response.getStatus() != 201) {

throw new RuntimeException("Failed : HTTP error code : " +

response.getStatus());

}

BufferedReader br = new BufferedReader(new InputStreamReader(

new ByteArrayInputStream(response.getEntity().getBytes())));

System.out.println("Output from Server .... \n");

while ((output = br.readLine()) != null) {

return output;

}

} catch (ClientProtocolException e) {

e.printStackTrace();

return e.getMessage();

} catch (IOException e) {

e.printStackTrace();

return e.getMessage();

} catch (Exception e) {

e.printStackTrace();

return e.getMessage();

}

return null;

}

The helper method parseJsonDigestValue parses the JSON response from the endpoint “/_api/contextinfo” retrieving the form digest.

public static String parseJsonDigestValue(String json) {

JSONObject jsonObject = (JSONObject) JSONSerializer.toJSON(json);

jsonObject = (JSONObject) jsonObject.get("d");

jsonObject = (JSONObject) jsonObject.get("GetContextWebInformation");

return jsonObject.getString("FormDigestValue");

}

The following helper, parseJson, translates the JSON response to the Car payload class, returning a list of cars.

public List<Car> parseJson(String json) {

List<Car> spCars = new ArrayList<Car>();

JSONObject jsonObject = (JSONObject) JSONSerializer.toJSON(json);

jsonObject = (JSONObject) jsonObject.get("d");

JSONArray array = (JSONArray) jsonObject.get("results");

Car car;

for (int i = 0; i < array.size(); i++) {

car = new Car();

jsonObject = (JSONObject) array.get(i);

car.setBrand(jsonObject.getString(brandTag));

car.setName(jsonObject.getString(modelTag));

car.setPrice(jsonObject.getLong(priceTag));

spCars.add(car);

}

return spCars;

}

To read items, the Cars service defines the method getCars. The implementation is as follows:

public List<Car> getCars() throws CarException, HttpException, IOException {

String jsonString = callRestEasyService(urlList, user, pass);

return parseJson(jsonString);

}

To write items, the Cars service defines the method insertCar. The implementation uses the helper method as follows:

public long insertCar(Car car) throws CarException {

cars.add(car);

String digestValue =

parseJsonDigestValue(callRestEasyRetrieveDigest(urlDigest, user, pass));

addRestEasyPost(urlList, user, pass, digestValue, car);

return 0;

}

User Experience (UX) for Apps in SharePoint 2013

The App user experience can be designed on top of the three possibilities within SharePoint:

  1. Full page user experience
  2. App Part user experience
  3. Custom Actions

The full page UX means the App will have the whole browser page for its user interface. In the full page UX, the App can make use of the SharePoint chrome control to render the same styling as of the hosted web and, optionally, render a header like SharePoint pages do.

In the App Part UX the App surfaces its UI on a page in the hosted web. A special kind of web part known as App Part takes care of embedding the App's page into the hosted page. The App Part accomplished this by rendering an iFrame where the source is the App's page. The App page can have the same styling of the hosted web by importing the hosted site's CSS files through JavaScript.

Custom Actions allow the placement of links to App’s pages in the Ribbon or in the Edi Control Block of the hosted web. This option has not been explored in this post.

Implementing the User Interface

The code below implements the App page named Car.jsp; it displays the list of cars. The page can be rendered as in the full page UX or in the App Part UX. The URL parameter inAppPart defines which way the page will be rendered (inAppPart=true, then as in the App Part UX; if none or false, then as in the full page UX).

First add a few declarations to the JSP page (file Car.jsp):

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"

pageEncoding="ISO-8859-1"%>

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

<title><spring:message code="welcome.title" /></title>

The code in the following boxes should be added to the HEAD element of the page.

Add the references to the libraries in the JSP page (file Car.jsp):

<script src="http://ajax.aspnetcdn.com/ajax/4.0/1/MicrosoftAjax.js"

type="text/javascript">

</script>

<script type="text/javascript"

src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.2.min.js">

</script>

Initialise the JavaScript block and a few variables from the URL query string:

<script type="text/javascript">

"use strict";

// define a namespace for the JavaScript objects: Interop

window.Interop = window.Interop || {};

$(document).ready(function() {

// initialisations

Interop.CarsApp.init();

Interop.Styling.init();

});

Interop.CarsApp = {

// load URL parameters

inAppPart: "false",

hostUrl: '',

init: function() {

// if the App page is in an App Part,

// the developer could set a URL parameter to inform that.

// Here: inAppPart=true

this.inAppPart =

Interop.CarsApp.getQueryStringParameter("inAppPart");

// the hosted web URL

this.hostUrl = decodeURIComponent(

Interop.CarsApp.getQueryStringParameter("SPHostUrl"));

},

// Function to retrieve a query string value.

// For production purposes you may want to use

// a library to handle the query string.

getQueryStringParameter: function (paramToRetrieve) {

var params = document.URL.split("?")[1].split("&");

var strParams = "";

for ( var i = 0; i < params.length; i = i + 1) {

var singleParam = params[i].split("=");

if (singleParam[0].toLowerCase() ==

paramToRetrieve.toLowerCase())

return singleParam[1];

}

return "";

}

};

The following script loads the CSS or the chrome control from SharePoint into the page:

Interop.Styling = {

// Load the SharePoint styling

init: function () {

var inAppPart = Interop.CarsApp.inAppPart,

hostUrl = Interop.CarsApp.hostUrl, scriptURI, linkMarkup;

// if the App page will be displayed as in full page or

// in an App Part, distinct styling initialisations are done

if(inAppPart === "true") {

// When loading the App page in an App Part

// (i.e., embedded in the hosted page)

// hide a few elements which are just meant for the

// full page user experience

$('.displayJustInFullPage').hide();

// load the hosted web styling

if (hostUrl !== "") {

linkMarkup = "<link rel='stylesheet' href='" + hostUrl +

"/_layouts/15/defaultcss.ashx' />";

} else {

// if no host web URL is available, load the default styling

linkMarkup = "<link rel='stylesheet' " +

"href='/_layouts/15/1033/styles/themable/corev15.css' />";

}

$('head').append(linkMarkup);

} else {

// When loading the App page as in full page user experience

// Load the js file SP.UI.Controls.js,

// afterwards set the Chrome Control and styling

scriptURI = hostUrl + "/_layouts/15/SP.UI.Controls.js";

$.getScript(scriptURI, Interop.Styling.setChromeControl);

}

},

setChromeControl: function () {

// Set the Chrome control and styling

// (used when the App is displayed in the full page user experience)

var hostUrl = Interop.CarsApp.hostUrl,

options = {},

nav;

options.siteTitle ="Cars";

options.siteUrl = hostUrl;

options.appHelpPageUrl = "cars.html?" + document.URL.split("?")[1];

options.appIconUrl = hostUrl + "/Shared Documents/car.png";

options.appTitle = "Cars App";

options.settingsLinks = [

{

"linkUrl" : "car.html?" + document.URL.split("?")[1],

"displayName" : "Car List"

},

{

"linkUrl" : "carForm.html?" + document.URL.split("?")[1],

"displayName" : "Add a Car"

}

];

nav = new SP.UI.Controls.Navigation("chromeControlContainer", options);

nav.setVisible(true);

}

};

</script>

</head>

The following piece of HTML defines the BODY of the page. The elements which should be seen just in the full page user experience are marked with the CSS class displayJustInFullPage. This allows the JavaScript part to hide or display them as seen above.

<body>

<div id='content'>

<div class='displayJustInFullPage'>

<!-- Chrome control placeholder -->

<div id="chromeControlContainer"></div>

<h1>Cars List</h1>

</div>

<table>

<tr class="ms-viewheadertr ms-vhltr">

<th class="ms-vh2">Brand</th>

<th class="ms-vh2">Model</th>

<th class="ms-vh2">Price</th>

</tr>

<c:forEach items="${cars}" var="car">

<tr>

<td class="ms-vb2">${car.brand}</td>

<td class="ms-vb2">${car.name}</td>

<td class="ms-vb2">${car.price}</td>

<tr>

</c:forEach>

</table>

</div>

</body>

</html>

The declarations above should be applied on every page that should look like the hosted web.

The Provider Hosted App

A SharePoint provider hosted app should be created following the instructions:

How to: Create high-trust apps for SharePoint 2013 using the server-to-server protocol (advanced topic)

Once created, edit the AppManifest.xml, setting the URL to a page in your Java web application. That could be the entry page of your application for the full page user experience:

<Properties>

<Title>YOUR-APP-TITLE</Title>

<StartPage>http://JAVA-WEB-APPLICATION-URL/car.html?{StandardTokens}</StartPage>

</Properties>

The App Part

For the App Part user experience, first add an App Part (known as well as Client Web Part) to your SharePoint project. After that, in the Elements.xml of the App Part, set the URL to the Java web application:

<Content Type="html" Src="http://JAVA-WEB-APPLICATION-URL/car.html?{StandardTokens}&amp;inAppPart=true" />

It is important to set there "inAppPart=true" to ensure the App Part user experience.

For instructions about how to create an App Part with Visual Studio 2012, refer to:

How to: Create app parts to deploy with apps for SharePoint

Conclusion

Provider hosted apps unleash great possibilities of integration between SharePoint and systems developed in other platforms as Java. Here, we have seen how to perform basic read/write operations on SharePoint lists. Also, it was shown how to integrate the App at user interface level with the full page and App Part user experiences.

Interoperability between Java and SharePoint 2013 on Premises的更多相关文章

  1. Integrating SharePoint 2013 with ADFS and Shibboleth

    Time again to attempt to implement that exciting technology, Federation Services (Web Single Sign On ...

  2. SharePoint 2013: Search Architecture in SPC202

    http://social.technet.microsoft.com/wiki/contents/articles/15989.sharepoint-2013-search-architecture ...

  3. 如何在 在SharePoint 2013/2010 解决方案中添加 ashx (HttpHandler)

    本文讲述如何在 在SharePoint 2013/2010 解决方案中添加 ashx (HttpHandler). 一般处理程序(HttpHandler)是·NET众多web组件的一种,ashx是其扩 ...

  4. SharePoint 2013 set site mailbox

    Automating Site Mailboxes in SharePoint 2013 and Exchange 2013 One of the completely new features to ...

  5. SharePoint 2013 create workflow by SharePoint Designer 2013

    这篇文章主要基于上一篇http://www.cnblogs.com/qindy/p/6242714.html的基础上,create a sample workflow by SharePoint De ...

  6. Install and Configure SharePoint 2013 Workflow

    这篇文章主要briefly introduce the Install and configure SharePoint 2013 Workflow. Microsoft 推出了新的Workflow ...

  7. SharePoint 2013 configure and publish infopth

    This article will simply descript how to configure and publish a InfoPath step by step. Note: To con ...

  8. 沙盒解决方案解决SharePoint 2013 以其他身份登陆的问题

    众所周知,SharePoint 2013没有像SharePoint 2010那样有一个叫"以其他身份登录"的菜单项. 当然解决方案也很多,比如你可以直接修改Welcome.ascx ...

  9. 实现一个基于 SharePoint 2013 的 Timecard 应用(中)

    门户视图 随着 Timecard 列表的增多,如何查找和管理这许多的 Timecard 也就成了问题.尤其对于团队经理而言,他除了自己填写的 Timecard,还要审核团队成员的 Timecard 任 ...

随机推荐

  1. 协作图 Collaboration diagram

    概述 协作图也是一种交互图,但一般用的比较少,一般用在大概分析一下对象之间是怎样交互的,跟顺序图是可以相互转化的. 协作图的用处: 在分析的时候(而顺序图一般设计的时候),分析出有哪些对象: 在白板上 ...

  2. [原]Android打包之Gradle打包

    最近尝试了一下Android的Gradle打包,发现确实比Ant打包会方便很多,特此记录下来. 注:android的gradle现在插件的版本已经是0.14.3了,对于一些老的方法和api,有一些已经 ...

  3. HMM 自学教程(一)引言

    本系列文章摘自 52nlp(我爱自然语言处理: http://www.52nlp.cn/),原文链接在 HMM 学习最佳范例,这是针对 国外网站上一个 HMM 教程 的翻译,作者功底很深,翻译得很精彩 ...

  4. SQL查询语句去除重复行

    1.存在两条完全相同的纪录 这是最简单的一种情况,用关键字distinct就可以去掉 select distinct * from table(表名) where (条件) 2.存在部分字段相同的纪录 ...

  5. 妹味6:ajax与ajax封装

    (功能)ajax能且仅能 从服务器读取文件 (环境)需要服务器环境才能测试,可以用工具建立本地服务器环境 (缓存)解决缓存问题:url加时间戳让每次请求地址唯一,如 url='abc.txt?t='+ ...

  6. SQL 分类统计函数

    SELECT TransactionNumber,SUM(CASE WHEN ReasonLevel=0 THEN           TransactionNumber ELSE 0 end ) a ...

  7. [C#高级编程].NET体系结构

    本章内容: 编译和运行面向 .NET的代码 MSIL的优点 值类型和引用类型 数据类型化 理解错误处理和特性 程序集..NET基类和命名空间 本章主要介绍一些概念,内容不多. C#是专门为Micros ...

  8. 使用VS2010开发Qt程序的一点经验

    导读 相比于Qt Creator,我更喜欢用VS2010来进行开发.虽然启动时间相对较慢,但是VS下强大的快捷键和丰富的插件,以及使用多年的经验,都让我觉得在开发过程中得心应手.其中最重要的一点是,有 ...

  9. P6 EPPM 16.1 安装和配置指南 1

    安装和配置指南下一topiccontents这些指南解释如何安装和配置数据库服务器,和P6 EPPM,模块:他们还提供在P6 EPPM能够解决所有模块的概述.标准指南帮助您配置和部署应用程序向导P6 ...

  10. 环信SDK与Apple Watch的结合(3)

    第3章主要介绍怎样在Watch App的页面上显示iPhone程序里的数据.主要操作的是“EMWatchOCDemo WatchKit Extension”这个文件夹,附源码EMWatchOCDemo ...