├── Procfile
├── system.properties
├── .gitignore
├── src
└── main
│ ├── webapp
│ ├── images
│ │ └── salesforce.png
│ ├── examples
│ │ ├── chatter-talk
│ │ │ ├── logo.png
│ │ │ ├── share.png
│ │ │ ├── talk.css
│ │ │ ├── chatter-talk.js
│ │ │ └── index.jsp
│ │ └── hello-world
│ │ │ ├── logo.png
│ │ │ └── index.jsp
│ ├── WEB-INF
│ │ └── web.xml
│ ├── index.html
│ └── scripts
│ │ └── json2.js
│ └── java
│ ├── canvas
│ ├── CanvasOrganizationContext.java
│ ├── CanvasClient.java
│ ├── CanvasRequest.java
│ ├── CanvasContext.java
│ ├── CanvasEnvironmentContext.java
│ ├── SignedRequest.java
│ ├── CanvasLinkContext.java
│ └── CanvasUserContext.java
│ ├── servlets
│ ├── OAuthServlet.java
│ └── ProxyServlet.java
│ └── Main.java
├── .gitmodules
├── CODEOWNERS
├── SECURITY.md
├── LICENSE.txt
├── README.md
├── pom.xml
└── CODE_OF_CONDUCT.md
/Procfile:
--------------------------------------------------------------------------------
1 | web: sh target/bin/webapp
2 |
--------------------------------------------------------------------------------
/system.properties:
--------------------------------------------------------------------------------
1 | java.runtime.version=1.8
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | *.iws
3 | *.ipr
4 | *.class
5 | .project
6 | .classpath
7 | keystore
8 | target/
9 | .settings/
10 | .DS_Store
11 |
--------------------------------------------------------------------------------
/src/main/webapp/images/salesforce.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/forcedotcom/SalesforceCanvasFrameworkSDK/HEAD/src/main/webapp/images/salesforce.png
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "src/main/webapp/sdk"]
2 | path = src/main/webapp/sdk
3 | url = git://github.com/forcedotcom/SalesforceCanvasJavascriptSDK.git
4 |
--------------------------------------------------------------------------------
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # Comment line immediately above ownership line is reserved for related gus information. Please be careful while editing.
2 | #ECCN:Open Source
3 |
--------------------------------------------------------------------------------
/src/main/webapp/examples/chatter-talk/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/forcedotcom/SalesforceCanvasFrameworkSDK/HEAD/src/main/webapp/examples/chatter-talk/logo.png
--------------------------------------------------------------------------------
/src/main/webapp/examples/hello-world/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/forcedotcom/SalesforceCanvasFrameworkSDK/HEAD/src/main/webapp/examples/hello-world/logo.png
--------------------------------------------------------------------------------
/src/main/webapp/examples/chatter-talk/share.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/forcedotcom/SalesforceCanvasFrameworkSDK/HEAD/src/main/webapp/examples/chatter-talk/share.png
--------------------------------------------------------------------------------
/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | Canvas SDK
7 |
8 |
9 | index.html
10 |
11 |
12 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | ## Security
2 |
3 | Please report any security issue to [security@salesforce.com](mailto:security@salesforce.com)
4 | as soon as it is discovered. This library limits its runtime dependencies in
5 | order to reduce the total cost of ownership as much as can be, but all consumers
6 | should remain vigilant and have their security stakeholders review all third-party
7 | products (3PP) like this one and their dependencies.
8 |
--------------------------------------------------------------------------------
/src/main/webapp/examples/chatter-talk/talk.css:
--------------------------------------------------------------------------------
1 | #stylized {
2 | border: solid 2px #b7ddf2;
3 | /* Safari 4-5, Chrome 1-9 */
4 | background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#E5F2F7), to(#FEFFFF));
5 |
6 | /* Safari 5.1, Chrome 10+ */
7 | background: -webkit-linear-gradient(top, #FEFFFF, #E5F2F7);
8 |
9 | /* Firefox 3.6+ */
10 | background: -moz-linear-gradient(top, #FEFFFF, #E5F2F7);
11 |
12 | /* IE 10 */
13 | background: -ms-linear-gradient(top, #FEFFFF, #E5F2F7);
14 |
15 | /* Opera 11.10+ */
16 | background: -o-linear-gradient(top, #FEFFFF, #E5F2F7);
17 |
18 | /*background: #ebf4fb;*/
19 | }
20 |
21 | #speech-input-field {
22 | width: 400px;
23 | height: 50px;
24 | padding: 10px 15px;
25 | margin-top: 20px;
26 | font-size: 100%;
27 | border-radius: 10px;
28 | border: 1px solid #ccc;
29 | outline: 0;
30 | }
31 |
32 | button {
33 | clear: both;
34 | width: 48px;
35 | height:24px;
36 | background: #666666 url(/examples/chatter-talk/share.png) no-repeat;
37 | }
--------------------------------------------------------------------------------
/src/main/webapp/examples/chatter-talk/chatter-talk.js:
--------------------------------------------------------------------------------
1 | var chatterTalk;
2 | if (!chatterTalk) {
3 | chatterTalk = {};
4 | }
5 |
6 | (function ($$) {
7 |
8 | "use strict";
9 |
10 | function onClickHandler() {
11 | }
12 |
13 | chatterTalk.init = function(sr, button, input, callback) {
14 | $$.byId(button).onclick=function() {
15 | var value = $$.byId(input).value;
16 | chatterTalk.post(sr, value, callback);
17 | };
18 | };
19 |
20 |
21 | chatterTalk.post = function(sr, message, callback) {
22 | var url = sr.context.links.chatterFeedsUrl+"/news/"+sr.context.user.userId+"/feed-items";
23 | var body = {body : {messageSegments : [{type: "Text", text: message}]}};
24 |
25 | $$.client.ajax(url,
26 | {client : sr.client,
27 | method: 'POST',
28 | contentType: "application/json",
29 | data: JSON.stringify(body),
30 | success : function(data) {
31 | if ($$.isFunction(callback)) {
32 | callback(data);
33 | }
34 | }
35 | });
36 | };
37 |
38 | }(Sfdc.canvas));
39 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012, Salesforce.com, Inc.
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
5 |
6 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8 | Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
10 |
--------------------------------------------------------------------------------
/src/main/webapp/examples/hello-world/index.jsp:
--------------------------------------------------------------------------------
1 | <%@ page import="canvas.SignedRequest" %>
2 | <%@ page import="java.util.Map" %>
3 | <%
4 | // Pull the signed request out of the request body and verify/decode it.
5 | Map parameters = request.getParameterMap();
6 | String[] signedRequest = parameters.get("signed_request");
7 | if (signedRequest == null) {%>
8 | This App must be invoked via a signed request!<%
9 | return;
10 | }
11 | String yourConsumerSecret=System.getenv("CANVAS_CONSUMER_SECRET");
12 | //String yourConsumerSecret="1818663124211010887";
13 | String signedRequestJson = SignedRequest.verifyAndDecodeAsJson(signedRequest[0], yourConsumerSecret);
14 | %>
15 |
16 |
17 |
18 |
19 |
20 | Hello World Canvas Example
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
43 |
44 |
45 |
46 |
Hello
47 |
48 |
49 |
--------------------------------------------------------------------------------
/src/main/webapp/examples/chatter-talk/index.jsp:
--------------------------------------------------------------------------------
1 | <%@ page import="canvas.SignedRequest" %>
2 | <%@ page import="java.util.Map" %>
3 | <%
4 | // Pull the signed request out of the request body and verify/decode it.
5 | Map parameters = request.getParameterMap();
6 | String[] signedRequest = parameters.get("signed_request");
7 | if (signedRequest == null) {%>This App must be invoked via a signed request!<%
8 | return;
9 | }
10 | String yourConsumerSecret=System.getenv("CANVAS_CONSUMER_SECRET");
11 | String signedRequestJson = SignedRequest.verifyAndDecodeAsJson(signedRequest[0], yourConsumerSecret);
12 | %>
13 |
14 |
15 |
16 |
17 |
18 | Chatter Talk Example
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | Chatter Talk
38 |
39 |
40 |
41 |
42 |
43 |
Speech input is not enabled in your browser.
44 | Try running Google Chrome with the --enable-speech-input flag.
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/src/main/java/canvas/CanvasOrganizationContext.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2011, salesforce.com, inc.
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided
6 | * that the following conditions are met:
7 | *
8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the
9 | * following disclaimer.
10 | *
11 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
12 | * the following disclaimer in the documentation and/or other materials provided with the distribution.
13 | *
14 | * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or
15 | * promote products derived from this software without specific prior written permission.
16 | *
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 | * POSSIBILITY OF SUCH DAMAGE.
25 | */
26 |
27 | package canvas;
28 |
29 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
30 | import com.fasterxml.jackson.annotation.JsonProperty;
31 |
32 | /**
33 | * Describes contextual information about the current organization/company.
34 | */
35 | @JsonIgnoreProperties(ignoreUnknown=true)
36 | public class CanvasOrganizationContext {
37 | private String organizationId;
38 | private String name;
39 | private boolean multicurrencyEnabled;
40 | private String currencyISOCode;
41 |
42 | /**
43 | * The organization id of the organization.
44 | */
45 | @JsonProperty("organizationId")
46 | public String getOrganizationId() {
47 | return this.organizationId;
48 | }
49 |
50 | public void setOrganizationId(String organizationId) {
51 | this.organizationId = organizationId;
52 | }
53 |
54 | /**
55 | * The name of the company or organization.
56 | */
57 | @JsonProperty("name")
58 | public String getName() {
59 | return this.name;
60 | }
61 |
62 | public void setName(String name) {
63 | this.name = name;
64 | }
65 |
66 | /**
67 | * Indicates whether the user\u2019s organization uses multiple currencies (true) or not (false).
68 | */
69 | @JsonProperty("multicurrencyEnabled")
70 | public boolean isMulticurrencyEnabled() {
71 | return this.multicurrencyEnabled;
72 | }
73 |
74 | public void setMulticurrencyEnabled(boolean multicurrencyEnabled) {
75 | this.multicurrencyEnabled = multicurrencyEnabled;
76 | }
77 |
78 | /**
79 | * Current company's default currency ISO code (applies only if multi-currency is disabled for the org).
80 | */
81 | @JsonProperty("currencyIsoCode")
82 | public String getCurrencyISOCode() {
83 | return this.currencyISOCode;
84 | }
85 |
86 | public void setCurrencyISOCode(String currencyISOCode) {
87 | this.currencyISOCode = currencyISOCode;
88 | }
89 |
90 | @Override
91 | public String toString()
92 | {
93 | return organizationId + ","+
94 | name + ","+
95 | multicurrencyEnabled + ","+
96 | currencyISOCode;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/main/webapp/index.html:
--------------------------------------------------------------------------------
1 |
26 |
27 |
28 |
29 | Salesforce Canvas SDK
30 |
114 |
115 |
116 |
117 |
118 |
119 |
Welcome to Canvas SDK
120 |
121 |
122 |
123 |
124 | Your sample Java application is up and running.
125 |
126 | For more information on Salesforce Canvas Framework, please visit
127 | our developer documentation.
128 |
129 |
130 |
131 | `
132 |
133 |
134 |
135 |
136 |
--------------------------------------------------------------------------------
/src/main/java/canvas/CanvasClient.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2011, salesforce.com, inc.
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided
6 | * that the following conditions are met:
7 | *
8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the
9 | * following disclaimer.
10 | *
11 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
12 | * the following disclaimer in the documentation and/or other materials provided with the distribution.
13 | *
14 | * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or
15 | * promote products derived from this software without specific prior written permission.
16 | *
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 | * POSSIBILITY OF SUCH DAMAGE.
25 | */
26 | package canvas;
27 |
28 | import java.util.HashMap;
29 | import java.util.Map;
30 |
31 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
32 | import com.fasterxml.jackson.annotation.JsonProperty;
33 |
34 | /**
35 | * Describes contextual information about the current canvas client (third party app). This Object,
36 | * in JS literal notation, needs to ba passed on all Sfdc.canvas.client requests.
37 | *
38 | */
39 | @JsonIgnoreProperties(ignoreUnknown=true)
40 | public class CanvasClient
41 | {
42 | private String OAuthToken;
43 | private String clientId;
44 | private String instanceId;
45 | private String targetOrigin;
46 | private String instanceUrl;
47 |
48 | /**
49 | * The scoped OAuth token to be used to subsequent REST calls
50 | */
51 | @JsonProperty("oauthToken")
52 | public String getOAuthToken() {
53 | return OAuthToken;
54 | }
55 |
56 | /**
57 | * @return The scoped OAuth token to be usd for subsequent REST calls.
58 | */
59 | @JsonProperty("oauthToken")
60 | public void setOAuthToken(String OAuthToken) {
61 | this.OAuthToken = OAuthToken;
62 | }
63 |
64 | @JsonProperty("clientId")
65 | public String getClientId() {
66 | return clientId;
67 | }
68 |
69 | @JsonProperty("clientId")
70 | public void setClientId(String clientId) {
71 | this.clientId = clientId;
72 | }
73 |
74 | /**
75 | * Unique identifier for this canvas app. This value will be different for each instance of a canvas app, even if
76 | * the same canvas app is placed on a page more than once.
77 | * @return Unique identifier for this canvas app
78 | */
79 | @JsonProperty("instanceId")
80 | public String getInstanceId() {
81 | return instanceId;
82 | }
83 |
84 | @JsonProperty("instanceId")
85 | public void setInstanceId(String instanceId) {
86 | this.instanceId = instanceId;
87 | }
88 |
89 | /**
90 | * @returns the origin (http://somesalesforcedomain:port) of the parent to the canvas app. This is used so
91 | * the in browser proxy knows where to post the request to.
92 | */
93 | @JsonProperty("targetOrigin")
94 | public String getTargetOrigin() {
95 | return this.targetOrigin;
96 | }
97 |
98 | @JsonProperty("targetOrigin")
99 | public void setTargetOrigin(String targetOrigin) {
100 | this.targetOrigin = targetOrigin;
101 | }
102 |
103 | /**
104 | * The base url for all subsequent REST call, this has the correct
105 | * Salesforce instance this organization is pinned to.
106 | */
107 | @JsonProperty("instanceUrl")
108 | public String getInstanceUrl() {
109 | return instanceUrl;
110 | }
111 |
112 | @JsonProperty("instanceUrl")
113 | public void setInstanceUrl(String instanceUrl) {
114 | this.instanceUrl = instanceUrl;
115 | }
116 | }
117 |
118 |
--------------------------------------------------------------------------------
/src/main/java/canvas/CanvasRequest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2011, salesforce.com, inc.
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided
6 | * that the following conditions are met:
7 | *
8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the
9 | * following disclaimer.
10 | *
11 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
12 | * the following disclaimer in the documentation and/or other materials provided with the distribution.
13 | *
14 | * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or
15 | * promote products derived from this software without specific prior written permission.
16 | *
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 | * POSSIBILITY OF SUCH DAMAGE.
25 | */
26 |
27 | package canvas;
28 |
29 | import java.util.HashMap;
30 | import java.util.Map;
31 |
32 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
33 | import com.fasterxml.jackson.annotation.JsonProperty;
34 |
35 | /**
36 | *
37 | * The canvas request is what is sent to the client on the very first request. In this canvas
38 | * request is information for authenticating and context about the user, organization and environment.
39 | *
40 | * This class is serialized into JSON on then signed by the signature service to prevent tampering.
41 | *
42 | */
43 | @JsonIgnoreProperties(ignoreUnknown=true)
44 | public class CanvasRequest {
45 |
46 | private String algorithm;
47 | private Integer issuedAt;
48 | private String userId;
49 | private CanvasContext canvasContext;
50 | private CanvasClient client;
51 |
52 | /**
53 | * The algorithm used to sign the request. typically HMAC-SHA256
54 | * @see platform.connect.service.SignRequestService.ALGORITHM
55 | */
56 | @JsonProperty("algorithm")
57 | public String getAlgorithm() {
58 | return algorithm;
59 | }
60 |
61 | @JsonProperty("algorithm")
62 | public void setAlgorithm(String algorithm) {
63 | this.algorithm = algorithm;
64 | }
65 |
66 | /**
67 | * The unix time this request was issued at.
68 | */
69 | @JsonProperty("issuedAt")
70 | public Integer getIssuedAt() {
71 | return issuedAt;
72 | }
73 |
74 | @JsonProperty("issuedAt")
75 | public void setIssuedAt(Integer issuedAt) {
76 | this.issuedAt = issuedAt;
77 | }
78 |
79 | /**
80 | * The Salesforce unique id for this user.
81 | * @return
82 | */
83 | @JsonProperty("userId")
84 | public String getUserId() {
85 | return userId;
86 | }
87 |
88 | @JsonProperty("userId")
89 | public void setUserId(String userId) {
90 | this.userId = userId;
91 | }
92 |
93 | /**
94 | * Context information about the user, org and environment.
95 | */
96 | @JsonProperty("context")
97 | public CanvasContext getContext() {
98 | return canvasContext;
99 | }
100 |
101 | /**
102 | * Client instance information required while using the Sfdc.canvas.client JavaScript API.
103 | */
104 | @JsonProperty("context")
105 | public void setContext(CanvasContext canvasContext) {
106 | this.canvasContext = canvasContext;
107 | }
108 |
109 | /**
110 | * Unique information about this client (including oauth token). This information (in JSON) format needs to be
111 | * included on all client side SDK calls.
112 | */
113 | @JsonProperty("client")
114 | public CanvasClient getClient() {
115 | return client;
116 | }
117 |
118 | @JsonProperty("client")
119 | public void setClient(CanvasClient client) {
120 | this.client = client;
121 | }
122 | }
123 |
124 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Salesforce Force.com Canvas SDK
2 | ============================
3 |
4 | ### Introduction
5 |
6 | Force.com Canvas is a mechanism for consuming third-party applications within Salesforce. Its goal is to connect applications at a UI level instead of just an API level. The purpose of this GitHub repository is to provide third-party applications with a Java/JavaScript SDK and examples so you can easily integrate canvas-style applications into Salesforce, while developing in the technology and platform of your choice.
7 |
8 | The best place to get started building canvas applications is the [online developer guide][1].
9 |
10 | Currently, we provide Java examples in this repository, but you can develop in whatever language you prefer. Most of the integration with Salesforce is through JavaScript and REST. You can also run and test your application locally from your own host, or from [Heroku](http://www.heroku.com/).
11 |
12 |
13 | ### Examples
14 |
15 | This SDK contains some basic Java examples. We recommend you explore the [Heroku Quick Start][2], for additional examples in Java and Ruby.
16 |
17 | For other examples and resources, check out the [developer guide][1].
18 |
19 | ### Prerequisites
20 |
21 | Below are some useful commands and links for your convenience. Before you use them, you'll need to make sure you have the necessary software installed on your computer [here][3].
22 |
23 | ### How to clone the SDK repository
24 |
25 | git clone git@github.com:forcedotcom/SalesforceCanvasFrameworkSDK.git
26 | cd SalesforceCanvasFrameworkSDK
27 | git submodule init
28 | git submodule update
29 |
30 | ### How to build canvas locally
31 |
32 | If you prefer, you can build and test your application locally before you push to Heroku or any other server. If you decide to test locally, you'll also need to generate a local keystore so you can do SSL.
33 |
34 | mvn package
35 |
36 | ### First time keystore generation
37 | This is only needed to support SSL (https) when running locally. Heroku uses [piggyback SSL](https://devcenter.heroku.com/articles/ssl) so it's not needed there.
38 |
39 | > keytool -keystore keystore -alias jetty -genkey -keyalg RSA
40 | Enter keystore password: 123456
41 | Re-enter new password: 123456
42 | What is your first and last name?
43 | [Unknown]: John Doe
44 | What is the name of your organizational unit?
45 | [Unknown]: myorgunit
46 | What is the name of your organization?
47 | [Unknown]: myorg
48 | What is the name of your City or Locality?
49 | [Unknown]: San Fancisco
50 | What is the name of your State or Province?
51 | [Unknown]: CA
52 | What is the two-letter country code for this unit?
53 | [Unknown]: us
54 | Is CN=salesforce.com, OU=platform, O=chimera, L=San Fancisco, ST=CA, C=us correct?
55 | [no]: yes
56 |
57 | Enter key password for
58 | (RETURN if same as keystore password):
59 | Re-enter new password:
60 |
61 |
62 | ### How to run canvas locally
63 |
64 | If you're running and testing locally, this will start your Java Web server.
65 |
66 | sh target/bin/webapp
67 |
68 | ### Canvas URL
69 |
70 | If you're running locally
71 | https://localhost:8443/examples/hello-world/index.jsp
72 |
73 | Or if you're running on Heroku
74 | https://.herokuapp.com/examples/hello-world/index.jsp
75 |
76 | ### Canvas callback URLs
77 |
78 | If you're running locally
79 | https://localhost:8443/sdk/callback.html
80 |
81 | Or if you're running on Heroku
82 | https://.herokuapp.com/sdk/callback.html
83 |
84 | ### How to push new changes to Heroku
85 |
86 | To commit your changes into your local git repository and push those changes to Heroku, use these commands. Note that your repository name may be diffferent than 'heroku', use git remote -v to confirm.
87 |
88 | git add -A
89 | git commit -m "My change comments"
90 | git push heroku master
91 |
92 | ### How to get Heroku logs
93 |
94 | To access your logs on Heroku, use the following command. For more information on Heroku logs click [here](https://devcenter.heroku.com/articles/logging).
95 |
96 | heroku logs --tail
97 |
98 | [1]: https://developer.salesforce.com/docs/atlas.en-us.platform_connect.meta/platform_connect/canvas_framework_intro.htm
99 | [2]: https://developer.salesforce.com/docs/atlas.en-us.platform_connect.meta/platform_connect/quick_start_simple_intro.htm
100 | [3]: https://developer.salesforce.com/docs/atlas.en-us.platform_connect.meta/platform_connect/quick_start_prereqs.htm
101 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 | sfdc
7 | 1.0-SNAPSHOT
8 | Salesforce Canvas Framework SDK
9 | sfdc-canvas-sdk
10 |
11 |
12 | 1.8
13 | UTF-8
14 | 9.0.90
15 |
16 |
17 |
18 |
19 |
20 | jstl
21 | jstl
22 | 1.2
23 |
24 |
25 |
26 | org.apache.tomcat.embed
27 | tomcat-embed-core
28 | ${tomcat.version}
29 |
30 |
31 | org.apache.tomcat.embed
32 | tomcat-embed-jasper
33 | ${tomcat.version}
34 |
35 |
36 | org.apache.tomcat
37 | tomcat-jasper
38 | ${tomcat.version}
39 |
40 |
41 | org.apache.tomcat
42 | tomcat-jasper-el
43 | ${tomcat.version}
44 |
45 |
46 | org.apache.tomcat
47 | tomcat-jsp-api
48 | ${tomcat.version}
49 |
50 |
51 |
52 | com.fasterxml.jackson.core
53 | jackson-core
54 | 2.8.5
55 |
56 |
57 | com.fasterxml.jackson.core
58 | jackson-databind
59 | 2.8.5
60 |
61 |
62 |
63 |
64 | asm
65 | asm
66 | 3.2
67 |
68 |
69 |
70 |
71 | org.json
72 | json
73 | 20080701
74 |
75 |
76 |
77 |
78 | commons-httpclient
79 | commons-httpclient
80 | 3.1
81 |
82 |
83 | commons-codec
84 | commons-codec
85 |
86 |
87 |
88 |
89 |
90 |
91 | commons-codec
92 | commons-codec
93 | 1.4
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 | org.apache.maven.plugins
103 | maven-compiler-plugin
104 | 2.3.2
105 |
106 | ${java.version}
107 | ${java.version}
108 |
109 |
110 |
111 |
112 | org.codehaus.mojo
113 | appassembler-maven-plugin
114 | 1.1.1
115 |
116 | target
117 |
118 |
119 | Main
120 | webapp
121 |
122 |
123 |
124 |
125 |
126 | execution1
127 | package
128 |
129 | assemble
130 |
131 |
132 |
133 |
134 |
135 |
136 |
--------------------------------------------------------------------------------
/src/main/java/canvas/CanvasContext.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2011, salesforce.com, inc.
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided
6 | * that the following conditions are met:
7 | *
8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the
9 | * following disclaimer.
10 | *
11 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
12 | * the following disclaimer in the documentation and/or other materials provided with the distribution.
13 | *
14 | * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or
15 | * promote products derived from this software without specific prior written permission.
16 | *
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 | * POSSIBILITY OF SUCH DAMAGE.
25 | */
26 |
27 | package canvas;
28 |
29 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
30 | import com.fasterxml.jackson.annotation.JsonProperty;
31 |
32 | /**
33 | * Describes all contextual information related to canvas applications.
34 | *
35 | *
36 | * Some information within the context depends on what oauth scopes are allowed
37 | * on the canvas application. Some/all items may be null if the oauth scope is
38 | * not set accordingly.
39 | *
40 | */
41 | @JsonIgnoreProperties(ignoreUnknown=true)
42 | public class CanvasContext {
43 |
44 | private CanvasUserContext userContext = null;
45 | private CanvasOrganizationContext orgContext = null;
46 | private CanvasLinkContext linkContext = null;
47 | private CanvasEnvironmentContext envContext = null;
48 |
49 |
50 | /**
51 | * Provides the context about the current user.
52 | *
53 | * @return The current user context, or null if the oauth scope
54 | * will not allow.
55 | */
56 | @JsonProperty("user")
57 | public CanvasUserContext getUserContext() {
58 | return this.userContext;
59 | }
60 |
61 | /**
62 | * Sets the context about the current user.
63 | */
64 | @JsonProperty("user")
65 | public void setUserContext(CanvasUserContext userContext)
66 | {
67 | this.userContext = userContext;
68 | }
69 |
70 | /**
71 | * Provides the context about the current organization.
72 | *
73 | * @return The current organization context, or null if the oauth scope
74 | * will not allow.
75 | */
76 | @JsonProperty("organization")
77 | public CanvasOrganizationContext getOrganizationContext() {
78 | return orgContext;
79 | }
80 |
81 | /**
82 | * Sets the context about the current organization.
83 | */
84 | @JsonProperty("organization")
85 | public void setOrganizationContext(CanvasOrganizationContext orgContext)
86 | {
87 | this.orgContext = orgContext;
88 | }
89 |
90 | /**
91 | * Provides the context about the current environment (page, url, etc).
92 | */
93 | @JsonProperty("environment")
94 | public CanvasEnvironmentContext getEnvironmentContext() {
95 | if (null == this.envContext){
96 | return new CanvasEnvironmentContext();
97 | }
98 | return envContext;
99 | }
100 |
101 | @JsonProperty("environment")
102 | public void setEnvironmentContext(CanvasEnvironmentContext envContext){
103 | this.envContext = envContext;
104 | }
105 |
106 | /**
107 | * Provides links to external resources within sfdc.
108 | */
109 | @JsonProperty("links")
110 | public CanvasLinkContext getLinkContext() {
111 | return linkContext;
112 | }
113 |
114 | /**
115 | * Sets the link context for this request.
116 | * @param linkContext
117 | */
118 | @JsonProperty("links")
119 | public void setLinkContext(CanvasLinkContext linkContext)
120 | {
121 | this.linkContext = linkContext;
122 | }
123 |
124 | @Override
125 | public String toString()
126 | {
127 | return String.format("Canvas Context:\n\t" +
128 | "User Context:\n\t\t%s\n\t"+
129 | "Org Context:\n\t\t%s\n\t"+
130 | "Environment Context:\n\t\t%s\n\t"+
131 | "Link Context:\n\t\t%s\n",
132 | null != userContext?userContext.toString():"null",
133 | null != orgContext?orgContext.toString():"null",
134 | null != envContext?envContext.toString():"null",
135 | null != linkContext?linkContext.toString():"null");
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Salesforce Open Source Community Code of Conduct
2 |
3 | ## About the Code of Conduct
4 |
5 | Equality is a core value at Salesforce. We believe a diverse and inclusive
6 | community fosters innovation and creativity, and are committed to building a
7 | culture where everyone feels included.
8 |
9 | Salesforce open-source projects are committed to providing a friendly, safe, and
10 | welcoming environment for all, regardless of gender identity and expression,
11 | sexual orientation, disability, physical appearance, body size, ethnicity, nationality,
12 | race, age, religion, level of experience, education, socioeconomic status, or
13 | other similar personal characteristics.
14 |
15 | The goal of this code of conduct is to specify a baseline standard of behavior so
16 | that people with different social values and communication styles can work
17 | together effectively, productively, and respectfully in our open source community.
18 | It also establishes a mechanism for reporting issues and resolving conflicts.
19 |
20 | All questions and reports of abusive, harassing, or otherwise unacceptable behavior
21 | in a Salesforce open-source project may be reported by contacting the Salesforce
22 | Open Source Conduct Committee at ossconduct@salesforce.com.
23 |
24 | ## Our Pledge
25 |
26 | In the interest of fostering an open and welcoming environment, we as
27 | contributors and maintainers pledge to making participation in our project and
28 | our community a harassment-free experience for everyone, regardless of gender
29 | identity and expression, sexual orientation, disability, physical appearance,
30 | body size, ethnicity, nationality, race, age, religion, level of experience, education,
31 | socioeconomic status, or other similar personal characteristics.
32 |
33 | ## Our Standards
34 |
35 | Examples of behavior that contributes to creating a positive environment
36 | include:
37 |
38 | * Using welcoming and inclusive language
39 | * Being respectful of differing viewpoints and experiences
40 | * Gracefully accepting constructive criticism
41 | * Focusing on what is best for the community
42 | * Showing empathy toward other community members
43 |
44 | Examples of unacceptable behavior by participants include:
45 |
46 | * The use of sexualized language or imagery and unwelcome sexual attention or
47 | advances
48 | * Personal attacks, insulting/derogatory comments, or trolling
49 | * Public or private harassment
50 | * Publishing, or threatening to publish, others' private information—such as
51 | a physical or electronic address—without explicit permission
52 | * Other conduct which could reasonably be considered inappropriate in a
53 | professional setting
54 | * Advocating for or encouraging any of the above behaviors
55 |
56 | ## Our Responsibilities
57 |
58 | Project maintainers are responsible for clarifying the standards of acceptable
59 | behavior and are expected to take appropriate and fair corrective action in
60 | response to any instances of unacceptable behavior.
61 |
62 | Project maintainers have the right and responsibility to remove, edit, or
63 | reject comments, commits, code, wiki edits, issues, and other contributions
64 | that are not aligned with this Code of Conduct, or to ban temporarily or
65 | permanently any contributor for other behaviors that they deem inappropriate,
66 | threatening, offensive, or harmful.
67 |
68 | ## Scope
69 |
70 | This Code of Conduct applies both within project spaces and in public spaces
71 | when an individual is representing the project or its community. Examples of
72 | representing a project or community include using an official project email
73 | address, posting via an official social media account, or acting as an appointed
74 | representative at an online or offline event. Representation of a project may be
75 | further defined and clarified by project maintainers.
76 |
77 | ## Enforcement
78 |
79 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
80 | reported by contacting the Salesforce Open Source Conduct Committee
81 | at ossconduct@salesforce.com. All complaints will be reviewed and investigated
82 | and will result in a response that is deemed necessary and appropriate to the
83 | circumstances. The committee is obligated to maintain confidentiality with
84 | regard to the reporter of an incident. Further details of specific enforcement
85 | policies may be posted separately.
86 |
87 | Project maintainers who do not follow or enforce the Code of Conduct in good
88 | faith may face temporary or permanent repercussions as determined by other
89 | members of the project's leadership and the Salesforce Open Source Conduct
90 | Committee.
91 |
92 | ## Attribution
93 |
94 | This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant-home],
95 | version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html.
96 | It includes adaptions and additions from [Go Community Code of Conduct][golang-coc],
97 | [CNCF Code of Conduct][cncf-coc], and [Microsoft Open Source Code of Conduct][microsoft-coc].
98 |
99 | This Code of Conduct is licensed under the [Creative Commons Attribution 3.0 License][cc-by-3-us].
100 |
101 | [contributor-covenant-home]: https://www.contributor-covenant.org (https://www.contributor-covenant.org/)
102 | [golang-coc]: https://golang.org/conduct
103 | [cncf-coc]: https://github.com/cncf/foundation/blob/master/code-of-conduct.md
104 | [microsoft-coc]: https://opensource.microsoft.com/codeofconduct/
105 | [cc-by-3-us]: https://creativecommons.org/licenses/by/3.0/us/
106 |
--------------------------------------------------------------------------------
/src/main/java/canvas/CanvasEnvironmentContext.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2011, salesforce.com, inc.
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided
6 | * that the following conditions are met:
7 | *
8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the
9 | * following disclaimer.
10 | *
11 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
12 | * the following disclaimer in the documentation and/or other materials provided with the distribution.
13 | *
14 | * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or
15 | * promote products derived from this software without specific prior written permission.
16 | *
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 | * POSSIBILITY OF SUCH DAMAGE.
25 | */
26 |
27 | package canvas;
28 |
29 | import java.util.HashMap;
30 | import java.util.Map;
31 |
32 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
33 | import com.fasterxml.jackson.annotation.JsonProperty;
34 |
35 | @JsonIgnoreProperties(ignoreUnknown=true)
36 | public class CanvasEnvironmentContext {
37 |
38 | private String locationUrl;
39 | private String uiTheme;
40 | private Dimensions dimensions;
41 | private SystemVersion version;
42 | private Map parameters;
43 |
44 | /**
45 | * Returns the url of the current location.
46 | */
47 | @JsonProperty("locationUrl")
48 | public String getLocationUrl() {
49 | return this.locationUrl;
50 | }
51 |
52 | public void setLocationUrl(String referrerUrl) {
53 | this.locationUrl = referrerUrl;
54 | }
55 |
56 | /**
57 | * Returns the value Theme2 if the user is using the newer user interface theme of the online application, labeled
58 | * \u201cSalesforce.\u201d Returns Theme1 if the user is using the older user interface theme, labeled
59 | * \u201cSalesforce Classic.\u201d
60 | */
61 | @JsonProperty("uiTheme")
62 | public String getUiTheme() {
63 | return this.uiTheme;
64 | }
65 |
66 | public void setUiTheme(String uiTheme) {
67 | this.uiTheme = uiTheme;
68 | }
69 |
70 | @JsonProperty("dimensions")
71 | public Dimensions getDimensions() {
72 | return this.dimensions;
73 | }
74 |
75 | @JsonProperty("dimensions")
76 | public void setDimensions(Dimensions dimensions) {
77 | this.dimensions = dimensions;
78 | }
79 |
80 | @JsonProperty("version")
81 | public SystemVersion getSystemVersion() {
82 | return this.version;
83 | }
84 |
85 | @JsonProperty("version")
86 | public void setSystemVersion(SystemVersion systemVersion) {
87 | this.version = systemVersion;
88 | }
89 |
90 | @JsonProperty("parameters")
91 | public Map getParameters() {
92 | if (null == this.parameters){
93 | this.parameters = new HashMap();
94 | }
95 | return this.parameters;
96 | }
97 |
98 | @JsonProperty("parameters")
99 | public void setParameters(Map parameters) {
100 | this.parameters = parameters;
101 | }
102 |
103 | @Override
104 | public String toString()
105 | {
106 | return locationUrl + ", " +
107 | uiTheme + "," +
108 | dimensions.toString() + "," +
109 | version.toString();
110 | }
111 |
112 | @JsonIgnoreProperties(ignoreUnknown=true)
113 | public static class Dimensions{
114 | /**
115 | * The width of the iframe
116 | */
117 | private String width;
118 | /**
119 | * The height of the iframe.
120 | */
121 | private String height;
122 |
123 | @JsonProperty("width")
124 | public String getWidth() {
125 | return this.width;
126 | }
127 | @JsonProperty("width")
128 | public void setWidth(String width) {
129 | this.width = width;
130 | }
131 |
132 | @JsonProperty("height")
133 | public String getHeight() {
134 | return this.height;
135 | }
136 | @JsonProperty("height")
137 | public void setHeight(String height) {
138 | this.height = height;
139 | }
140 |
141 | @Override
142 | public String toString(){
143 | return String.format("(w:%s,h:%s)",width,height);
144 | }
145 | }
146 |
147 | @JsonIgnoreProperties(ignoreUnknown=true)
148 | public static class SystemVersion{
149 |
150 | private String api;
151 | private String season;
152 |
153 | // Needs default ctor for Jackson to construct.
154 | public SystemVersion(){
155 | }
156 |
157 | @JsonProperty("api")
158 | public String getApiVersion() {
159 | return this.api;
160 | }
161 |
162 | @JsonProperty("api")
163 | public void setApiVersion(String apiVersion) {
164 | this.api = apiVersion;
165 | }
166 |
167 | @JsonProperty("season")
168 | public String getSeason() {
169 | return this.season;
170 | }
171 |
172 | @JsonProperty("season")
173 | public void setSeason(String season) {
174 | this.season = season;
175 | }
176 |
177 | @Override
178 | public String toString(){
179 | return String.format("%s - %s",api,season);
180 | }
181 |
182 | }
183 |
184 | }
185 |
--------------------------------------------------------------------------------
/src/main/java/servlets/OAuthServlet.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2011, salesforce.com, inc.
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided
6 | * that the following conditions are met:
7 | *
8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the
9 | * following disclaimer.
10 | *
11 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
12 | * the following disclaimer in the documentation and/or other materials provided with the distribution.
13 | *
14 | * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or
15 | * promote products derived from this software without specific prior written permission.
16 | *
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 | * POSSIBILITY OF SUCH DAMAGE.
25 | */
26 |
27 | package servlets;
28 |
29 | import org.apache.commons.httpclient.HttpClient;
30 | import org.apache.commons.httpclient.HttpException;
31 | import org.apache.commons.httpclient.methods.PostMethod;
32 | import org.json.JSONException;
33 | import org.json.JSONObject;
34 | import org.json.JSONTokener;
35 |
36 | import javax.servlet.ServletException;
37 | import javax.servlet.http.HttpServlet;
38 | import javax.servlet.http.HttpServletRequest;
39 | import javax.servlet.http.HttpServletResponse;
40 | import java.io.IOException;
41 | import java.io.InputStreamReader;
42 | import java.io.UnsupportedEncodingException;
43 | import java.net.URLEncoder;
44 |
45 | /**
46 | * A Servlet for handeling OAuth flow.
47 | * This OAuth Servlet is only provided as an example and is provided as-is
48 | */
49 | public class OAuthServlet extends HttpServlet {
50 | private static final long serialVersionUID = 1L;
51 |
52 | public static final String ACCESS_TOKEN = "ACCESS_TOKEN";
53 | public static final String INSTANCE_URL = "INSTANCE_URL";
54 |
55 | private String clientId = null;
56 | private String clientSecret = null;
57 | private String redirectUri = null;
58 | private String authUrl = null;
59 | private String tokenUrl = null;
60 |
61 | public void init() throws ServletException {
62 |
63 | String environment;
64 |
65 | clientId = this.getInitParameter("clientId");
66 | clientSecret = this.getInitParameter("clientSecret");
67 | redirectUri = this.getInitParameter("redirectUri"); // https://canvas.herokuapp.com/oauth/_callback
68 | environment = this.getInitParameter("environment"); // https://login.salesforce.com
69 |
70 | try {
71 | authUrl = environment
72 | + "/services/oauth2/authorize?response_type=code&client_id="
73 | + clientId + "&redirect_uri="
74 | + URLEncoder.encode(redirectUri, "UTF-8");
75 |
76 | // Nots: &scope=email,read_chatter,... would be added here for oauth scope
77 |
78 | } catch (UnsupportedEncodingException e) {
79 | throw new ServletException(e);
80 | }
81 |
82 | tokenUrl = environment + "/services/oauth2/token";
83 | }
84 |
85 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
86 |
87 | System.out.println("Begin OAuth");
88 |
89 | String accessToken = (String) request.getSession().getAttribute(ACCESS_TOKEN);
90 |
91 | if (accessToken == null) {
92 |
93 | String instanceUrl = null;
94 |
95 | if (request.getRequestURI().endsWith("oauth")) {
96 | // we need to send the user to authorize
97 | response.sendRedirect(authUrl);
98 | return;
99 | } else {
100 | System.out.println("Auth successful - got callback");
101 |
102 | String code = request.getParameter("code");
103 |
104 | HttpClient httpclient = new HttpClient();
105 |
106 | PostMethod post = new PostMethod(tokenUrl);
107 | post.addParameter("code", code);
108 | post.addParameter("grant_type", "authorization_code");
109 | post.addParameter("client_id", clientId);
110 | post.addParameter("client_secret", clientSecret);
111 | post.addParameter("redirect_uri", redirectUri);
112 |
113 | try {
114 | httpclient.executeMethod(post);
115 |
116 | try {
117 | JSONObject authResponse = new JSONObject(
118 | new JSONTokener(new InputStreamReader(
119 | post.getResponseBodyAsStream())));
120 | System.out.println("xAuth response: "
121 | + authResponse.toString(2));
122 |
123 | accessToken = authResponse.getString("access_token");
124 |
125 | // Instance URL is Salesforce specific.
126 | instanceUrl = authResponse.getString("instance_url");
127 |
128 | System.out.println("Got access token: " + accessToken);
129 | } catch (JSONException e) {
130 | e.printStackTrace();
131 | throw new ServletException(e);
132 | }
133 | } catch (HttpException e) {
134 | e.printStackTrace();
135 | throw new ServletException(e);
136 | }
137 | finally {
138 | post.releaseConnection();
139 | }
140 | }
141 |
142 | // Set a session attribute so that other servlets can get the access
143 | // token
144 | request.getSession().setAttribute(ACCESS_TOKEN, accessToken);
145 |
146 | // We also get the instance URL from the OAuth response, so set it
147 | // in the session too
148 | request.getSession().setAttribute(INSTANCE_URL, instanceUrl);
149 | }
150 |
151 | response.sendRedirect(request.getContextPath() + "/index.html");
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/src/main/java/canvas/SignedRequest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2011, salesforce.com, inc.
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided
6 | * that the following conditions are met:
7 | *
8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the
9 | * following disclaimer.
10 | *
11 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
12 | * the following disclaimer in the documentation and/or other materials provided with the distribution.
13 | *
14 | * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or
15 | * promote products derived from this software without specific prior written permission.
16 | *
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 | * POSSIBILITY OF SUCH DAMAGE.
25 | */
26 |
27 | package canvas;
28 |
29 | import com.fasterxml.jackson.core.type.TypeReference;
30 | import com.fasterxml.jackson.databind.ObjectMapper;
31 | import com.fasterxml.jackson.databind.ObjectReader;
32 | import org.apache.commons.codec.binary.Base64;
33 |
34 | import javax.crypto.Mac;
35 | import javax.crypto.SecretKey;
36 | import javax.crypto.spec.SecretKeySpec;
37 | import java.io.IOException;
38 | import java.io.StringWriter;
39 | import java.security.InvalidKeyException;
40 | import java.security.NoSuchAlgorithmException;
41 | import java.util.Arrays;
42 | import java.util.HashMap;
43 |
44 | /**
45 | *
46 | * The utility method can be used to validate/verify the signed request. In this case,
47 | * the signed request is verified that it is from Salesforce and that it has not been tampered with.
48 | *
49 | * This utility class has two methods. One verifies and decodes the request as a Java object the
50 | * other as a JSON String.
51 | *
52 | */
53 | public class SignedRequest {
54 |
55 | public static CanvasRequest verifyAndDecode(String input, String secret) throws SecurityException {
56 |
57 | String[] split = getParts(input);
58 |
59 | String encodedSig = split[0];
60 | String encodedEnvelope = split[1];
61 |
62 | // Deserialize the json body
63 | String json_envelope = new String(new Base64(true).decode(encodedEnvelope));
64 | ObjectMapper mapper = new ObjectMapper();
65 | ObjectReader reader = mapper.reader(CanvasRequest.class);
66 | CanvasRequest canvasRequest;
67 | String algorithm;
68 | try {
69 | canvasRequest = reader.readValue(json_envelope);
70 | algorithm = canvasRequest.getAlgorithm() == null ? "HMACSHA256" : canvasRequest.getAlgorithm();
71 | } catch (IOException e) {
72 | throw new SecurityException(String.format("Error [%s] deserializing JSON to Object [%s]", e.getMessage(), CanvasRequest.class.getName()), e);
73 | }
74 |
75 | verify(secret, algorithm, encodedEnvelope, encodedSig);
76 |
77 | // If we got this far, then the request was not tampered with.
78 | // return the request as a Java object
79 | return canvasRequest;
80 | }
81 |
82 |
83 | public static String verifyAndDecodeAsJson(String input, String secret) throws SecurityException {
84 |
85 | String[] split = getParts(input);
86 |
87 | String encodedSig = split[0];
88 | String encodedEnvelope = split[1];
89 |
90 | String json_envelope = new String(new Base64(true).decode(encodedEnvelope));
91 | ObjectMapper mapper = new ObjectMapper();
92 |
93 | String algorithm;
94 | StringWriter writer;
95 | TypeReference> typeRef
96 | = new TypeReference>() { };
97 | try {
98 | HashMap o = mapper.readValue(json_envelope, typeRef);
99 | writer = new StringWriter();
100 | mapper.writeValue(writer, o);
101 | algorithm = (String)o.get("algorithm");
102 | } catch (IOException e) {
103 | throw new SecurityException(String.format("Error [%s] deserializing JSON to Object [%s]", e.getMessage(),
104 | typeRef.getClass().getName()), e);
105 | }
106 |
107 | verify(secret, algorithm, encodedEnvelope, encodedSig);
108 |
109 | // If we got this far, then the request was not tampered with.
110 | // return the request as a JSON string.
111 | return writer.toString();
112 | }
113 |
114 | private static String[] getParts(String input) {
115 |
116 | if (input == null || input.indexOf(".") <= 0) {
117 | throw new SecurityException(String.format("Input [%s] doesn't look like a signed request", input));
118 | }
119 |
120 | String[] split = input.split("[.]", 2);
121 | return split;
122 | }
123 |
124 | private static void verify(String secret, String algorithm, String encodedEnvelope, String encodedSig )
125 | throws SecurityException
126 | {
127 | if (secret == null || secret.trim().length() == 0) {
128 | throw new IllegalArgumentException("secret is null, did you set your environment variable CANVAS_CONSUMER_SECRET?");
129 | }
130 |
131 | SecretKey hmacKey = null;
132 | try {
133 | byte[] key = secret.getBytes();
134 | hmacKey = new SecretKeySpec(key, algorithm);
135 | Mac mac = Mac.getInstance(algorithm);
136 | mac.init(hmacKey);
137 |
138 | // Check to see if the body was tampered with
139 | byte[] digest = mac.doFinal(encodedEnvelope.getBytes());
140 | byte[] decode_sig = new Base64(true).decode(encodedSig);
141 | if (! Arrays.equals(digest, decode_sig)) {
142 | String label = "Warning: Request was tampered with";
143 | throw new SecurityException(label);
144 | }
145 | } catch (NoSuchAlgorithmException e) {
146 | throw new SecurityException(String.format("Problem with algorithm [%s] Error [%s]", algorithm, e.getMessage()), e);
147 | } catch (InvalidKeyException e) {
148 | throw new SecurityException(String.format("Problem with key [%s] Error [%s]", hmacKey, e.getMessage()), e);
149 | }
150 |
151 | // If we got here and didn't throw a SecurityException then all is good.
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/src/main/java/Main.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2011, salesforce.com, inc.
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided
6 | that the following conditions are met:
7 |
8 | Redistributions of source code must retain the above copyright notice, this list of conditions and the
9 | following disclaimer.
10 |
11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
12 | the following disclaimer in the documentation and/or other materials provided with the distribution.
13 |
14 | Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or
15 | promote products derived from this software without specific prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
18 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 | POSSIBILITY OF SUCH DAMAGE.
25 | */
26 |
27 | import org.apache.catalina.WebResourceRoot;
28 | import org.apache.catalina.core.StandardContext;
29 | import org.apache.catalina.startup.Tomcat;
30 | import org.apache.catalina.webresources.DirResourceSet;
31 | import org.apache.catalina.webresources.StandardRoot;
32 | import org.apache.catalina.startup.Tomcat;
33 | import org.apache.catalina.startup.Tomcat;
34 | import org.apache.catalina.connector.Connector;
35 | import org.apache.coyote.http11.AbstractHttp11Protocol;
36 | import org.apache.catalina.Context;
37 | import org.apache.catalina.startup.Tomcat;
38 | import org.apache.catalina.connector.Connector;
39 | import org.apache.catalina.Context;
40 | import org.apache.catalina.connector.Connector;
41 | import org.apache.catalina.startup.Tomcat;
42 | import org.apache.tomcat.util.scan.StandardJarScanner;
43 | import java.io.File;
44 |
45 | /**
46 | *
47 | * This class launches the web application in an embedded Jetty container.
48 | * This is the entry point to your application. The Java command that is used for
49 | * launching should fire this main method.
50 | *
51 | */
52 | public class Main {
53 |
54 | /**
55 | * @param args
56 | */
57 | public static void main(String[] args) throws Exception{
58 |
59 | boolean heroku = false;
60 | // A hacky way to determine if we are running on heroku or not
61 | String basedir = (String)System.getProperty("basedir");
62 | if (basedir != null && basedir.endsWith("/app/target")) {
63 | heroku = true;
64 | }
65 |
66 | if (!heroku) {
67 | System.out.println("Looks like we are NOT running on Heroku.");
68 |
69 | String webappDirLocation = "src/main/webapp/";
70 |
71 | String webPort = System.getenv("PORT");
72 | if (webPort == null || webPort.isEmpty()) {
73 | webPort = "8080";
74 | }
75 |
76 | String sslPort = System.getenv("SSLPORT");
77 | if (sslPort == null || sslPort.isEmpty()) {
78 | sslPort = System.getenv("SSL_PORT");
79 | if (sslPort == null || sslPort.isEmpty()) {
80 | sslPort = "8443";
81 | }
82 | }
83 |
84 | Tomcat tomcat = new Tomcat();
85 | tomcat.setPort(Integer.parseInt(webPort));
86 |
87 | // HTTP Connector
88 | Connector httpConnector = new Connector("HTTP/1.1");
89 | httpConnector.setPort(Integer.parseInt(webPort));
90 | tomcat.getService().addConnector(httpConnector);
91 |
92 | // HTTPS Connector
93 | Connector httpsConnector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
94 | httpsConnector.setPort(Integer.parseInt(sslPort));
95 | httpsConnector.setSecure(true);
96 | httpsConnector.setScheme("https");
97 | httpsConnector.setAttribute("keyAlias", "jetty2");
98 | httpsConnector.setAttribute("keystorePass", "123456");
99 | httpsConnector.setAttribute("keystoreFile", "keystore");
100 | httpsConnector.setAttribute("clientAuth", "false");
101 | httpsConnector.setAttribute("sslProtocol", "TLS");
102 | httpsConnector.setAttribute("SSLEnabled", true);
103 | tomcat.getService().addConnector(httpsConnector);
104 |
105 | // Set the default connector to HTTP
106 | tomcat.setConnector(httpConnector);
107 |
108 | // Add the web application context
109 | StandardContext ctx = (StandardContext) tomcat.addWebapp("/", new File(webappDirLocation).getAbsolutePath());
110 | System.out.println("configuring app with basedir: " + new File("./" + webappDirLocation).getAbsolutePath());
111 |
112 | File additionWebInfClasses = new File("target/classes");
113 | WebResourceRoot resources = new StandardRoot(ctx);
114 | resources.addPreResources(new DirResourceSet(resources, "/WEB-INF/classes",
115 | additionWebInfClasses.getAbsolutePath(), "/"));
116 | ctx.setResources(resources);
117 |
118 |
119 | tomcat.start();
120 | tomcat.getServer().await();
121 | }
122 | else {
123 |
124 | // Heroku does it's own SSL piggyback thing
125 | System.out.println("Looks like we are running on heroku.");
126 |
127 | String webappDirLocation = "src/main/webapp/";
128 |
129 | //The port that we should run on can be set into an environment variable
130 | //Look for that variable and default to 8080 if it isn't there.
131 | String webPort = System.getenv("PORT");
132 | if(webPort == null || webPort.isEmpty()) {
133 | webPort = "8080";
134 | }
135 |
136 | Tomcat tomcat = new Tomcat();
137 | tomcat.setPort(Integer.valueOf(webPort));
138 |
139 | Connector conn = new Connector();
140 | conn.setPort(Integer.valueOf(webPort));
141 | tomcat.setConnector(conn);
142 |
143 | StandardContext ctx = (StandardContext) tomcat.addWebapp("/", new File(webappDirLocation).getAbsolutePath());
144 | System.out.println("configuring app with basedir: " + new File("./" + webappDirLocation).getAbsolutePath());
145 |
146 | File additionWebInfClasses = new File("target/classes");
147 | WebResourceRoot resources = new StandardRoot(ctx);
148 | resources.addPreResources(new DirResourceSet(resources, "/WEB-INF/classes",
149 | additionWebInfClasses.getAbsolutePath(), "/"));
150 | ctx.setResources(resources);
151 |
152 | tomcat.start();
153 | tomcat.getServer().await();
154 | }
155 | }
156 |
157 | }
158 |
--------------------------------------------------------------------------------
/src/main/java/canvas/CanvasLinkContext.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2011, salesforce.com, inc.
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided
6 | * that the following conditions are met:
7 | *
8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the
9 | * following disclaimer.
10 | *
11 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
12 | * the following disclaimer in the documentation and/or other materials provided with the distribution.
13 | *
14 | * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or
15 | * promote products derived from this software without specific prior written permission.
16 | *
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 | * POSSIBILITY OF SUCH DAMAGE.
25 | */
26 |
27 | package canvas;
28 |
29 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
30 | import com.fasterxml.jackson.annotation.JsonProperty;
31 |
32 | /**
33 | * Describes all contextual information around external references, or links to external resources.
34 | */
35 | @JsonIgnoreProperties(ignoreUnknown=true)
36 | public class CanvasLinkContext {
37 | private String enterpriseUrl;
38 | private String metadataUrl;
39 | private String partnerUrl;
40 | private String restUrl;
41 | private String sobjectUrl;
42 | private String searchUrl;
43 | private String queryUrl;
44 | private String recentItemsUrl;
45 | private String userProfileUrl;
46 | private String chatterFeedsUrl;
47 | private String chatterGroupsUrl;
48 | private String chatterUsersUrl;
49 | private String chatterFeedItemsUrl;
50 |
51 | /**
52 | * Provide the url for enterprise-wide external clients.
53 | */
54 | @JsonProperty("enterpriseUrl")
55 | public String getEnterpriseUrl() {
56 | return this.enterpriseUrl;
57 | }
58 |
59 | public void setEnterpriseUrl(String enterpriseUrl) {
60 | this.enterpriseUrl = enterpriseUrl;
61 | }
62 |
63 | /**
64 | * Provide the base url for the metadata api to access information about custom objects, apex classes, etc.
65 | */
66 | @JsonProperty("metadataUrl")
67 | public String getMetadataUrl() {
68 | return this.metadataUrl;
69 | }
70 |
71 | public void setMetadataUrl(String metadataUrl) {
72 | this.metadataUrl = metadataUrl;
73 | }
74 |
75 | /**
76 | * Access to the partner api for developing client applications for multiple organizations.
77 | */
78 | @JsonProperty("partnerUrl")
79 | public String getPartnerUrl() {
80 | return this.partnerUrl;
81 | }
82 |
83 | public void setPartnerUrl(String partnerUrl) {
84 | this.partnerUrl = partnerUrl;
85 | }
86 |
87 | /**
88 | * Access to the base url for RESTful services.
89 | */
90 | @JsonProperty("restUrl")
91 | public String getRestUrl() {
92 | return this.restUrl;
93 | }
94 |
95 | public void setRestUrl(String restUrl) {
96 | this.restUrl = restUrl;
97 | }
98 |
99 | /**
100 | * Access to custom sobject definitions.
101 | */
102 | @JsonProperty("sobjectUrl")
103 | public String getSobjectUrl() {
104 | return this.sobjectUrl;
105 | }
106 |
107 | public void setSobjectUrl(String sobjectUrl) {
108 | this.sobjectUrl = sobjectUrl;
109 | }
110 |
111 | /**
112 | * Access to search api.
113 | */
114 | @JsonProperty("searchUrl")
115 | public String getSearchUrl() {
116 | return this.searchUrl;
117 | }
118 |
119 | public void setSearchUrl(String searchUrl) {
120 | this.searchUrl = searchUrl;
121 | }
122 |
123 | /**
124 | * Access to the SOQL query api.
125 | */
126 | @JsonProperty("queryUrl")
127 | public String getQueryUrl() {
128 | return this.queryUrl;
129 | }
130 |
131 | public void setQueryUrl(String queryUrl) {
132 | this.queryUrl = queryUrl;
133 | }
134 |
135 | /**
136 | * Access to the recent items feed.
137 | */
138 | @JsonProperty("recentItemsUrl")
139 | public String getRecentItemsUrl() {
140 | return this.recentItemsUrl;
141 | }
142 |
143 | public void setRecentItemsUrl(String recentItemsUrl) {
144 | this.recentItemsUrl = recentItemsUrl;
145 | }
146 |
147 | /**
148 | * Retrieve more information about the current user.
149 | */
150 | @JsonProperty("userUrl")
151 | public String getUserUrl() {
152 | return this.userProfileUrl;
153 | }
154 |
155 | public void setUserUrl(String userProfileUrl) {
156 | this.userProfileUrl = userProfileUrl;
157 | }
158 |
159 | /**
160 | * Access to Chatter Feeds. Note: Requires user profile permissions, otherwise this will be null.
161 | */
162 | @JsonProperty("chatterFeedsUrl")
163 | public String getChatterFeedsUrl() {
164 | return this.chatterFeedsUrl;
165 | }
166 |
167 | public void setChatterFeedsUrl(String chatterFeedsUrl) {
168 | this.chatterFeedsUrl = chatterFeedsUrl;
169 | }
170 |
171 | /**
172 | * Access to Chatter Groups. Note: Requires user profile permissions, otherwise this will be null.
173 | */
174 | @JsonProperty("chatterGroupsUrl")
175 | public String getChatterGroupsUrl() {
176 | return this.chatterGroupsUrl;
177 | }
178 |
179 | public void setChatterGroupsUrl(String chatterGroupsUrl) {
180 | this.chatterGroupsUrl = chatterGroupsUrl;
181 | }
182 |
183 | /**
184 | * Access to Chatter Users. Note: Requires user profile permissions, otherwise this will be null.
185 | */
186 | @JsonProperty("chatterUsersUrl")
187 | public String getChatterUsersUrl() {
188 | return this.chatterUsersUrl;
189 | }
190 |
191 | public void setChatterUsersUrl(String chatterUsersUrl) {
192 | this.chatterUsersUrl = chatterUsersUrl;
193 | }
194 |
195 | /**
196 | * Access to individual Chatter Feed items. Note: Requires user profile permissions, otherwise this will be null.
197 | */
198 | @JsonProperty("chatterFeedItemsUrl")
199 | public String getChatterFeedItemsUrl() {
200 | return this.chatterFeedItemsUrl;
201 | }
202 |
203 | public void setChatterFeedItemsUrl(String chatterFeedItemsUrl) {
204 | this.chatterFeedItemsUrl = chatterFeedItemsUrl;
205 | }
206 |
207 | @Override
208 | public String toString()
209 | {
210 | return enterpriseUrl+ ", " +
211 | metadataUrl+ ", " +
212 | partnerUrl+ ", " +
213 | restUrl+ ", " +
214 | sobjectUrl+ ", " +
215 | searchUrl+ ", " +
216 | queryUrl+ ", " +
217 | recentItemsUrl+ ", " +
218 | userProfileUrl+ ", " +
219 | chatterFeedsUrl+ ", " +
220 | chatterGroupsUrl+ ", " +
221 | chatterUsersUrl+ ", " +
222 | chatterFeedItemsUrl;
223 | }
224 |
225 | }
226 |
--------------------------------------------------------------------------------
/src/main/java/canvas/CanvasUserContext.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2011, salesforce.com, inc.
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided
6 | * that the following conditions are met:
7 | *
8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the
9 | * following disclaimer.
10 | *
11 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
12 | * the following disclaimer in the documentation and/or other materials provided with the distribution.
13 | *
14 | * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or
15 | * promote products derived from this software without specific prior written permission.
16 | *
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 | * POSSIBILITY OF SUCH DAMAGE.
25 | */
26 |
27 | package canvas;
28 |
29 | import java.util.Locale;
30 |
31 |
32 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
33 | import com.fasterxml.jackson.annotation.JsonProperty;
34 |
35 | /**
36 | * Describes contextual information about the current user.
37 | */
38 | @JsonIgnoreProperties(ignoreUnknown=true)
39 | public class CanvasUserContext{
40 |
41 | private String userId;
42 | private String userName;
43 | private String firstName;
44 | private String lastName;
45 | private String email;
46 | private String fullName;
47 | private Locale locale;
48 | private Locale language;
49 | private String timeZone;
50 | private String profileId;
51 | private String roleId;
52 | private String userType;
53 | private String currencyISOCode;
54 | private boolean accessibilityMode;
55 | private String profilePhotoUrl;
56 | private String profileThumbnailUrl;
57 |
58 | /**
59 | * The Salesforce user identifier.
60 | */
61 | @JsonProperty("userId")
62 | public String getUserId(){
63 | return this.userId;
64 | }
65 |
66 | public void setUserId(String userId){
67 | this.userId = userId;
68 | }
69 |
70 | /**
71 | * The Salesforce username.
72 | */
73 | @JsonProperty("userName")
74 | public String getUserName(){
75 | return this.userName;
76 | }
77 |
78 | public void setUserName(String username){
79 | this.userName = username;
80 | }
81 |
82 | /**
83 | * User's first name.
84 | */
85 | @JsonProperty("firstName")
86 | public String getFirstName(){
87 | return this.firstName;
88 | }
89 |
90 | public void setFirstName(String firstName){
91 | this.firstName = firstName;
92 | }
93 |
94 | /**
95 | * User's last name.
96 | */
97 | @JsonProperty("lastName")
98 | public String getLastName(){
99 | return this.lastName;
100 | }
101 |
102 | public void setLastName(String lastName){
103 | this.lastName = lastName;
104 | }
105 |
106 | /**
107 | * Indicates whether user interface modifications for the visually impaired are on (true) or off (false).
108 | */
109 | @JsonProperty("accessibilityModeEnabled")
110 | public boolean isAccessibilityMode(){
111 | return this.accessibilityMode;
112 | }
113 |
114 | public void setAccessibilityMode(boolean accessibilityMode){
115 | this.accessibilityMode = accessibilityMode;
116 | }
117 |
118 | /**
119 | * The user's email address.
120 | */
121 | @JsonProperty("email")
122 | public String getEmail(){
123 | return this.email;
124 | }
125 |
126 | public void setEmail(String email){
127 | this.email = email;
128 | }
129 |
130 | /**
131 | * User's full name.
132 | */
133 | @JsonProperty("fullName")
134 | public String getFullName(){
135 | return this.fullName;
136 | }
137 |
138 | public void setFullName(String fullName){
139 | this.fullName = fullName;
140 | }
141 |
142 | /**
143 | * User\u2019s locale, which controls the formatting of dates and choice of symbols for currency.
144 | */
145 | @JsonProperty("locale")
146 | public Locale getLocale(){
147 | return this.locale;
148 | }
149 |
150 | public void setLocale(Locale locale){
151 | this.locale = locale;
152 | }
153 |
154 | /**
155 | * User's language, which controls the language for labels displayed in an application.
156 | */
157 | @JsonProperty("language")
158 | public Locale getLanguage(){
159 | return this.language;
160 | }
161 |
162 | public void setLanguage(Locale language){
163 | this.language = language;
164 | }
165 |
166 | /**
167 | * The user's configured timezone.
168 | */
169 | @JsonProperty("timeZone")
170 | public String getTimeZone(){
171 | return this.timeZone;
172 | }
173 |
174 | public void setTimeZone(String timezone){
175 | this.timeZone = timezone;
176 | }
177 |
178 | /**
179 | * Information about the user's profile identifier.
180 | */
181 | @JsonProperty("profileId")
182 | public String getProfileId(){
183 | return this.profileId;
184 | }
185 |
186 | public void setProfileId(String profileId){
187 | this.profileId = profileId;
188 | }
189 |
190 | /**
191 | * Role ID of the role currently assigned to the user.
192 | */
193 | @JsonProperty("roleId")
194 | public String getRoleId(){
195 | return this.roleId;
196 | }
197 |
198 | public void setRoleId(String roleId){
199 | this.roleId = roleId;
200 | }
201 |
202 | /**
203 | * Current user's license type in label form.
204 | */
205 | public String getUserType(){
206 | return this.userType;
207 | }
208 |
209 | public void setUserType(String userType){
210 | this.userType = userType;
211 | }
212 |
213 | /**
214 | * Current user's default currency ISO code (applies only if multi-currency is enabled for the org).
215 | */
216 | public String getCurrencyISOCode(){
217 | return this.currencyISOCode;
218 | }
219 |
220 | public void setCurrencyISOCode(String currencyISOCode){
221 | this.currencyISOCode = currencyISOCode;
222 | }
223 |
224 | /**
225 | * Returns the full profile photo of the current user.
226 | */
227 | public String getProfilePhotoUrl(){
228 | return this.profilePhotoUrl;
229 | }
230 |
231 | public void setProfilePhotoUrl(String profilePhotoUrl){
232 | this.profilePhotoUrl = profilePhotoUrl;
233 | }
234 |
235 | /**
236 | * Returns the thumbnail photo of the current user.
237 | */
238 | public String getProfileThumbnailUrl(){
239 | return this.profileThumbnailUrl;
240 | }
241 |
242 | public void setProfileThumbnailUrl(String profileThumbnailUrl){
243 | this.profileThumbnailUrl = profileThumbnailUrl;
244 | }
245 |
246 | @Override
247 | public String toString()
248 | {
249 | return userId+ ","+
250 | userName+ ","+
251 | firstName+","+
252 | lastName+ ","+
253 | email+ ","+
254 | fullName+ ","+
255 | locale+ ","+
256 | language+ ","+
257 | timeZone+ ","+
258 | profileId+ ","+
259 | roleId+ ","+
260 | userType+ ","+
261 | currencyISOCode+ ","+
262 | accessibilityMode+ ","+
263 | profilePhotoUrl+","+
264 | profileThumbnailUrl;
265 |
266 | }
267 | }
268 |
--------------------------------------------------------------------------------
/src/main/java/servlets/ProxyServlet.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2011, salesforce.com, inc.
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided
6 | * that the following conditions are met:
7 | *
8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the
9 | * following disclaimer.
10 | *
11 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
12 | * the following disclaimer in the documentation and/or other materials provided with the distribution.
13 | *
14 | * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or
15 | * promote products derived from this software without specific prior written permission.
16 | *
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 | * POSSIBILITY OF SUCH DAMAGE.
25 | */
26 |
27 | package servlets;
28 |
29 | import javax.servlet.ServletConfig;
30 | import javax.servlet.ServletException;
31 | import javax.servlet.http.HttpServlet;
32 | import javax.servlet.http.HttpServletRequest;
33 | import javax.servlet.http.HttpServletResponse;
34 | import java.io.IOException;
35 | import java.io.InputStream;
36 | import java.io.OutputStream;
37 | import java.net.HttpURLConnection;
38 | import java.net.URL;
39 | import java.util.Enumeration;
40 | import java.util.List;
41 | import java.util.Map;
42 |
43 | /**
44 | * ServerSide proxy for proxying request to remote server to get around cross domain issues.
45 | * This Proxy is only provided as an example and is provided as-is.
46 | */
47 | public class ProxyServlet extends HttpServlet {
48 |
49 | // web.xml config parameter names
50 | private static final String INIT_PARAM_CONNECTION_TIMEOUT = "connection_timeout";
51 | private static final String INIT_PARAM_FOLLOW_REDIRECTS = "follow_redirects";
52 | private static final String INIT_PARAM_READ_TIMEOUT = "read_timeout";
53 | private static final String INIT_PARAM_BUFFER_SIZE = "buffer_size";
54 | private static final String INIT_PARAM_REMOTE_HOST = "remote_host";
55 |
56 | // Configurable variables in web.xml
57 | private int connectionTimeout;
58 | private boolean followRedirects;
59 | private int readTimeout;
60 | private int bufferSize;
61 | private String remoteHost;
62 |
63 | @Override
64 | public void init(ServletConfig config) throws ServletException {
65 | super.init(config);
66 | connectionTimeout = getConfigParam(INIT_PARAM_CONNECTION_TIMEOUT, 5000);
67 | followRedirects = getConfigParam(INIT_PARAM_FOLLOW_REDIRECTS, true);
68 | readTimeout = getConfigParam(INIT_PARAM_READ_TIMEOUT, 0);
69 | bufferSize = getConfigParam(INIT_PARAM_BUFFER_SIZE, 8 * 1024);
70 | remoteHost = getConfigParam(INIT_PARAM_REMOTE_HOST, null);
71 | }
72 |
73 | @Override
74 | protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
75 | if ("PATCH".equals(request.getMethod())) {
76 | invoke("PATCH", request, response);
77 | }
78 | else {
79 | super.service(request, response);
80 | }
81 | }
82 |
83 | @Override
84 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
85 | invoke("GET", req, resp);
86 | }
87 |
88 | @Override
89 | protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
90 | invoke("PUT", req, resp);
91 | }
92 |
93 | @Override
94 | protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
95 | invoke("DELETE", req, resp);
96 | }
97 |
98 | @Override
99 | protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
100 | invoke("POST", req, resp);
101 | }
102 |
103 | @Override
104 | protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
105 | invoke("HEAD", req, resp);
106 | }
107 |
108 | @Override
109 | protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
110 | invoke("OPTIONS", req, resp);
111 | }
112 |
113 | protected void invoke(String verb, HttpServletRequest request, HttpServletResponse response) throws IOException
114 | {
115 | if (remoteHost == null) {
116 | response.sendError(HttpServletResponse.SC_BAD_REQUEST, "remote_host not configured");
117 | return;
118 | }
119 |
120 | String uri = request.getRequestURI();
121 | String remoteUrl = remoteHost + uri;
122 |
123 | if (request == null || remoteUrl == null || remoteUrl.equalsIgnoreCase(request.getRequestURL().toString())) {
124 | String.format("Bad preconditions remoteUrl [%s] request.getRequestURL() [%s]", remoteUrl, request.getRequestURL().toString());
125 | return;
126 | }
127 |
128 | // Good to Go....
129 |
130 | URL url;
131 | HttpURLConnection urlConnection = null;
132 | InputStream inputStream = null;
133 | try
134 | {
135 | //System.out.println("Producer URL: " + remoteUrl);
136 |
137 | url = new URL(remoteUrl);
138 | urlConnection = (HttpURLConnection)url.openConnection();
139 |
140 | urlConnection.setRequestMethod(verb);
141 | urlConnection.setInstanceFollowRedirects(followRedirects);
142 | urlConnection.setConnectTimeout(connectionTimeout);
143 | urlConnection.setDoInput(true);
144 | urlConnection.setDoOutput(true);
145 | if (readTimeout != 0) urlConnection.setReadTimeout(readTimeout);
146 |
147 | copyRequestHeaders(request, urlConnection);
148 |
149 | if (verb.equals("PUT") || verb.equals("PATCH") || verb.equals("POST") ) {
150 | copyBody(urlConnection, request.getInputStream());
151 | }
152 |
153 | response.setContentType(urlConnection.getContentType());
154 | if (! "gzip".equalsIgnoreCase(urlConnection.getContentEncoding())) {
155 | response.setCharacterEncoding(urlConnection.getContentEncoding());
156 | }
157 |
158 | int responseCode = urlConnection.getResponseCode();
159 | copyResponseHeaders(response, urlConnection);
160 |
161 | response.setStatus(responseCode);
162 |
163 | int contentLength = urlConnection.getContentLength();
164 | response.setContentLength(contentLength);
165 |
166 | if (responseCode != HttpURLConnection.HTTP_NOT_MODIFIED) {
167 | OutputStream outputStream = null;
168 | try
169 | {
170 | inputStream = urlConnection.getInputStream();
171 | outputStream = response.getOutputStream();
172 | byte buffer[] = new byte[bufferSize];
173 | while (true) {
174 | int len = inputStream.read(buffer, 0, bufferSize);
175 | if (len < 0) {
176 | break;
177 | }
178 | outputStream.write(buffer, 0, len);
179 | }
180 | } finally {
181 | try { if (outputStream != null) outputStream.flush(); } catch (IOException e) {}
182 | try { if (outputStream != null) outputStream.close(); } catch (IOException e) {}
183 | }
184 | }
185 |
186 | }
187 | finally {
188 | try { if (inputStream != null) inputStream.close(); } catch (IOException e) {}
189 | try { if (urlConnection != null) urlConnection.disconnect(); } catch (Exception e) {}
190 | }
191 | }
192 |
193 | protected void copyBody(HttpURLConnection httpURLConnection, InputStream body) throws IOException
194 | {
195 | OutputStream outputStream = null;
196 | try {
197 | outputStream = httpURLConnection.getOutputStream();
198 | int val;
199 | while ((val = body.read()) != -1) {
200 | outputStream.write(val);
201 | }
202 | }
203 | finally {
204 | try { if (body != null) body.close(); } catch (IOException e) {}
205 | try { if (outputStream != null) outputStream.flush(); } catch (IOException e) {}
206 | try { if (outputStream != null) outputStream.close(); } catch (IOException e) {}
207 | }
208 | }
209 |
210 | protected void copyRequestHeaders(HttpServletRequest request, HttpURLConnection connection) {
211 |
212 | Enumeration headerNames = request.getHeaderNames();
213 | while (headerNames.hasMoreElements()) {
214 |
215 | String headerName = (String) headerNames.nextElement();
216 | Enumeration headerValues = request.getHeaders(headerName);
217 | while (headerValues.hasMoreElements()) {
218 |
219 | String headerValue = (String) headerValues.nextElement();
220 | if (headerValue != null) {
221 | connection.addRequestProperty(headerName, headerValue);
222 | }
223 | }
224 | }
225 | }
226 |
227 | protected void copyResponseHeaders(HttpServletResponse response, HttpURLConnection connection) {
228 |
229 | Map> headers = connection.getHeaderFields();
230 | for (String name : headers.keySet()) {
231 | List values = headers.get(name);
232 |
233 | for (int i = 0; i < values.size(); i++) {
234 | String value = values.get(i);
235 | if (name == null || value == null) {
236 | continue;
237 | }
238 | response.addHeader(name, value);
239 | }
240 | }
241 | }
242 |
243 | // Helper methods
244 | private String getConfigParam(String name, String defaultValue) {
245 | String value = getServletConfig().getInitParameter(name);
246 | return (value == null || value.trim().length() == 0) ? defaultValue : value;
247 | }
248 |
249 | private int getConfigParam(String name, int defaultValue) {
250 | String value = getServletConfig().getInitParameter(name);
251 | return (value == null || value.trim().length() == 0) ? defaultValue : Integer.valueOf(value);
252 | }
253 |
254 | private boolean getConfigParam(String name, boolean defaultValue) {
255 | String value = getServletConfig().getInitParameter(name);
256 | return (value == null || value.trim().length() == 0) ? defaultValue : Boolean.valueOf(value);
257 | }
258 | }
259 |
--------------------------------------------------------------------------------
/src/main/webapp/scripts/json2.js:
--------------------------------------------------------------------------------
1 | /*
2 | http://www.JSON.org/json2.js
3 | 2011-01-18
4 |
5 | Public Domain.
6 |
7 | NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
8 |
9 | See http://www.JSON.org/js.html
10 |
11 |
12 | This code should be minified before deployment.
13 | See http://javascript.crockford.com/jsmin.html
14 |
15 | USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
16 | NOT CONTROL.
17 |
18 |
19 | This file creates a global JSON object containing two methods: stringify
20 | and parse.
21 |
22 | JSON.stringify(value, replacer, space)
23 | value any JavaScript value, usually an object or array.
24 |
25 | replacer an optional parameter that determines how object
26 | values are stringified for objects. It can be a
27 | function or an array of strings.
28 |
29 | space an optional parameter that specifies the indentation
30 | of nested structures. If it is omitted, the text will
31 | be packed without extra whitespace. If it is a number,
32 | it will specify the number of spaces to indent at each
33 | level. If it is a string (such as '\t' or ' '),
34 | it contains the characters used to indent at each level.
35 |
36 | This method produces a JSON text from a JavaScript value.
37 |
38 | When an object value is found, if the object contains a toJSON
39 | method, its toJSON method will be called and the result will be
40 | stringified. A toJSON method does not serialize: it returns the
41 | value represented by the name/value pair that should be serialized,
42 | or undefined if nothing should be serialized. The toJSON method
43 | will be passed the key associated with the value, and this will be
44 | bound to the value
45 |
46 | For example, this would serialize Dates as ISO strings.
47 |
48 | Date.prototype.toJSON = function (key) {
49 | function f(n) {
50 | // Format integers to have at least two digits.
51 | return n < 10 ? '0' + n : n;
52 | }
53 |
54 | return this.getUTCFullYear() + '-' +
55 | f(this.getUTCMonth() + 1) + '-' +
56 | f(this.getUTCDate()) + 'T' +
57 | f(this.getUTCHours()) + ':' +
58 | f(this.getUTCMinutes()) + ':' +
59 | f(this.getUTCSeconds()) + 'Z';
60 | };
61 |
62 | You can provide an optional replacer method. It will be passed the
63 | key and value of each member, with this bound to the containing
64 | object. The value that is returned from your method will be
65 | serialized. If your method returns undefined, then the member will
66 | be excluded from the serialization.
67 |
68 | If the replacer parameter is an array of strings, then it will be
69 | used to select the members to be serialized. It filters the results
70 | such that only members with keys listed in the replacer array are
71 | stringified.
72 |
73 | Values that do not have JSON representations, such as undefined or
74 | functions, will not be serialized. Such values in objects will be
75 | dropped; in arrays they will be replaced with null. You can use
76 | a replacer function to replace those with JSON values.
77 | JSON.stringify(undefined) returns undefined.
78 |
79 | The optional space parameter produces a stringification of the
80 | value that is filled with line breaks and indentation to make it
81 | easier to read.
82 |
83 | If the space parameter is a non-empty string, then that string will
84 | be used for indentation. If the space parameter is a number, then
85 | the indentation will be that many spaces.
86 |
87 | Example:
88 |
89 | text = JSON.stringify(['e', {pluribus: 'unum'}]);
90 | // text is '["e",{"pluribus":"unum"}]'
91 |
92 |
93 | text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
94 | // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
95 |
96 | text = JSON.stringify([new Date()], function (key, value) {
97 | return this[key] instanceof Date ?
98 | 'Date(' + this[key] + ')' : value;
99 | });
100 | // text is '["Date(---current time---)"]'
101 |
102 |
103 | JSON.parse(text, reviver)
104 | This method parses a JSON text to produce an object or array.
105 | It can throw a SyntaxError exception.
106 |
107 | The optional reviver parameter is a function that can filter and
108 | transform the results. It receives each of the keys and values,
109 | and its return value is used instead of the original value.
110 | If it returns what it received, then the structure is not modified.
111 | If it returns undefined then the member is deleted.
112 |
113 | Example:
114 |
115 | // Parse the text. Values that look like ISO date strings will
116 | // be converted to Date objects.
117 |
118 | myData = JSON.parse(text, function (key, value) {
119 | var a;
120 | if (typeof value === 'string') {
121 | a =
122 | /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
123 | if (a) {
124 | return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
125 | +a[5], +a[6]));
126 | }
127 | }
128 | return value;
129 | });
130 |
131 | myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
132 | var d;
133 | if (typeof value === 'string' &&
134 | value.slice(0, 5) === 'Date(' &&
135 | value.slice(-1) === ')') {
136 | d = new Date(value.slice(5, -1));
137 | if (d) {
138 | return d;
139 | }
140 | }
141 | return value;
142 | });
143 |
144 |
145 | This is a reference implementation. You are free to copy, modify, or
146 | redistribute.
147 | */
148 |
149 | /*jslint evil: true, strict: false, regexp: false */
150 |
151 | /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
152 | call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
153 | getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
154 | lastIndex, length, parse, prototype, push, replace, slice, stringify,
155 | test, toJSON, toString, valueOf
156 | */
157 |
158 |
159 | // Create a JSON object only if one does not already exist. We create the
160 | // methods in a closure to avoid creating global variables.
161 |
162 | var JSON;
163 | if (!JSON) {
164 | JSON = {};
165 | }
166 |
167 | (function () {
168 | "use strict";
169 |
170 | function f(n) {
171 | // Format integers to have at least two digits.
172 | return n < 10 ? '0' + n : n;
173 | }
174 |
175 | if (typeof Date.prototype.toJSON !== 'function') {
176 |
177 | Date.prototype.toJSON = function (key) {
178 |
179 | return isFinite(this.valueOf()) ?
180 | this.getUTCFullYear() + '-' +
181 | f(this.getUTCMonth() + 1) + '-' +
182 | f(this.getUTCDate()) + 'T' +
183 | f(this.getUTCHours()) + ':' +
184 | f(this.getUTCMinutes()) + ':' +
185 | f(this.getUTCSeconds()) + 'Z' : null;
186 | };
187 |
188 | String.prototype.toJSON =
189 | Number.prototype.toJSON =
190 | Boolean.prototype.toJSON = function (key) {
191 | return this.valueOf();
192 | };
193 | }
194 |
195 | var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
196 | escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
197 | gap,
198 | indent,
199 | meta = { // table of character substitutions
200 | '\b': '\\b',
201 | '\t': '\\t',
202 | '\n': '\\n',
203 | '\f': '\\f',
204 | '\r': '\\r',
205 | '"' : '\\"',
206 | '\\': '\\\\'
207 | },
208 | rep;
209 |
210 |
211 | function quote(string) {
212 |
213 | // If the string contains no control characters, no quote characters, and no
214 | // backslash characters, then we can safely slap some quotes around it.
215 | // Otherwise we must also replace the offending characters with safe escape
216 | // sequences.
217 |
218 | escapable.lastIndex = 0;
219 | return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
220 | var c = meta[a];
221 | return typeof c === 'string' ? c :
222 | '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
223 | }) + '"' : '"' + string + '"';
224 | }
225 |
226 |
227 | function str(key, holder) {
228 |
229 | // Produce a string from holder[key].
230 |
231 | var i, // The loop counter.
232 | k, // The member key.
233 | v, // The member value.
234 | length,
235 | mind = gap,
236 | partial,
237 | value = holder[key];
238 |
239 | // If the value has a toJSON method, call it to obtain a replacement value.
240 |
241 | if (value && typeof value === 'object' &&
242 | typeof value.toJSON === 'function') {
243 | value = value.toJSON(key);
244 | }
245 |
246 | // If we were called with a replacer function, then call the replacer to
247 | // obtain a replacement value.
248 |
249 | if (typeof rep === 'function') {
250 | value = rep.call(holder, key, value);
251 | }
252 |
253 | // What happens next depends on the value's type.
254 |
255 | switch (typeof value) {
256 | case 'string':
257 | return quote(value);
258 |
259 | case 'number':
260 |
261 | // JSON numbers must be finite. Encode non-finite numbers as null.
262 |
263 | return isFinite(value) ? String(value) : 'null';
264 |
265 | case 'boolean':
266 | case 'null':
267 |
268 | // If the value is a boolean or null, convert it to a string. Note:
269 | // typeof null does not produce 'null'. The case is included here in
270 | // the remote chance that this gets fixed someday.
271 |
272 | return String(value);
273 |
274 | // If the type is 'object', we might be dealing with an object or an array or
275 | // null.
276 |
277 | case 'object':
278 |
279 | // Due to a specification blunder in ECMAScript, typeof null is 'object',
280 | // so watch out for that case.
281 |
282 | if (!value) {
283 | return 'null';
284 | }
285 |
286 | // Make an array to hold the partial results of stringifying this object value.
287 |
288 | gap += indent;
289 | partial = [];
290 |
291 | // Is the value an array?
292 |
293 | if (Object.prototype.toString.apply(value) === '[object Array]') {
294 |
295 | // The value is an array. Stringify every element. Use null as a placeholder
296 | // for non-JSON values.
297 |
298 | length = value.length;
299 | for (i = 0; i < length; i += 1) {
300 | partial[i] = str(i, value) || 'null';
301 | }
302 |
303 | // Join all of the elements together, separated with commas, and wrap them in
304 | // brackets.
305 |
306 | v = partial.length === 0 ? '[]' : gap ?
307 | '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
308 | '[' + partial.join(',') + ']';
309 | gap = mind;
310 | return v;
311 | }
312 |
313 | // If the replacer is an array, use it to select the members to be stringified.
314 |
315 | if (rep && typeof rep === 'object') {
316 | length = rep.length;
317 | for (i = 0; i < length; i += 1) {
318 | k = rep[i];
319 | if (typeof k === 'string') {
320 | v = str(k, value);
321 | if (v) {
322 | partial.push(quote(k) + (gap ? ': ' : ':') + v);
323 | }
324 | }
325 | }
326 | } else {
327 |
328 | // Otherwise, iterate through all of the keys in the object.
329 |
330 | for (k in value) {
331 | if (Object.hasOwnProperty.call(value, k)) {
332 | v = str(k, value);
333 | if (v) {
334 | partial.push(quote(k) + (gap ? ': ' : ':') + v);
335 | }
336 | }
337 | }
338 | }
339 |
340 | // Join all of the member texts together, separated with commas,
341 | // and wrap them in braces.
342 |
343 | v = partial.length === 0 ? '{}' : gap ?
344 | '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
345 | '{' + partial.join(',') + '}';
346 | gap = mind;
347 | return v;
348 | }
349 | }
350 |
351 | // If the JSON object does not yet have a stringify method, give it one.
352 |
353 | if (typeof JSON.stringify !== 'function') {
354 | JSON.stringify = function (value, replacer, space) {
355 |
356 | // The stringify method takes a value and an optional replacer, and an optional
357 | // space parameter, and returns a JSON text. The replacer can be a function
358 | // that can replace values, or an array of strings that will select the keys.
359 | // A default replacer method can be provided. Use of the space parameter can
360 | // produce text that is more easily readable.
361 |
362 | var i;
363 | gap = '';
364 | indent = '';
365 |
366 | // If the space parameter is a number, make an indent string containing that
367 | // many spaces.
368 |
369 | if (typeof space === 'number') {
370 | for (i = 0; i < space; i += 1) {
371 | indent += ' ';
372 | }
373 |
374 | // If the space parameter is a string, it will be used as the indent string.
375 |
376 | } else if (typeof space === 'string') {
377 | indent = space;
378 | }
379 |
380 | // If there is a replacer, it must be a function or an array.
381 | // Otherwise, throw an error.
382 |
383 | rep = replacer;
384 | if (replacer && typeof replacer !== 'function' &&
385 | (typeof replacer !== 'object' ||
386 | typeof replacer.length !== 'number')) {
387 | throw new Error('JSON.stringify');
388 | }
389 |
390 | // Make a fake root object containing our value under the key of ''.
391 | // Return the result of stringifying the value.
392 |
393 | return str('', {'': value});
394 | };
395 | }
396 |
397 |
398 | // If the JSON object does not yet have a parse method, give it one.
399 |
400 | if (typeof JSON.parse !== 'function') {
401 | JSON.parse = function (text, reviver) {
402 |
403 | // The parse method takes a text and an optional reviver function, and returns
404 | // a JavaScript value if the text is a valid JSON text.
405 |
406 | var j;
407 |
408 | function walk(holder, key) {
409 |
410 | // The walk method is used to recursively walk the resulting structure so
411 | // that modifications can be made.
412 |
413 | var k, v, value = holder[key];
414 | if (value && typeof value === 'object') {
415 | for (k in value) {
416 | if (Object.hasOwnProperty.call(value, k)) {
417 | v = walk(value, k);
418 | if (v !== undefined) {
419 | value[k] = v;
420 | } else {
421 | delete value[k];
422 | }
423 | }
424 | }
425 | }
426 | return reviver.call(holder, key, value);
427 | }
428 |
429 |
430 | // Parsing happens in four stages. In the first stage, we replace certain
431 | // Unicode characters with escape sequences. JavaScript handles many characters
432 | // incorrectly, either silently deleting them, or treating them as line endings.
433 |
434 | text = String(text);
435 | cx.lastIndex = 0;
436 | if (cx.test(text)) {
437 | text = text.replace(cx, function (a) {
438 | return '\\u' +
439 | ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
440 | });
441 | }
442 |
443 | // In the second stage, we run the text against regular expressions that look
444 | // for non-JSON patterns. We are especially concerned with '()' and 'new'
445 | // because they can cause invocation, and '=' because it can cause mutation.
446 | // But just to be safe, we want to reject all unexpected forms.
447 |
448 | // We split the second stage into 4 regexp operations in order to work around
449 | // crippling inefficiencies in IE's and Safari's regexp engines. First we
450 | // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
451 | // replace all simple value tokens with ']' characters. Third, we delete all
452 | // open brackets that follow a colon or comma or that begin the text. Finally,
453 | // we look to see that the remaining characters are only whitespace or ']' or
454 | // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
455 |
456 | if (/^[\],:{}\s]*$/
457 | .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
458 | .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
459 | .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
460 |
461 | // In the third stage we use the eval function to compile the text into a
462 | // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
463 | // in JavaScript: it can begin a block or an object literal. We wrap the text
464 | // in parens to eliminate the ambiguity.
465 |
466 | j = eval('(' + text + ')');
467 |
468 | // In the optional fourth stage, we recursively walk the new structure, passing
469 | // each name/value pair to a reviver function for possible transformation.
470 |
471 | return typeof reviver === 'function' ?
472 | walk({'': j}, '') : j;
473 | }
474 |
475 | // If the text is not JSON parseable, then a SyntaxError is thrown.
476 |
477 | throw new SyntaxError('JSON.parse');
478 | };
479 | }
480 | }());
481 |
482 |
483 |
484 |
--------------------------------------------------------------------------------