Author: [email protected]
Date: Wed Feb 22 15:46:30 2012
New Revision: 2109
Log:
[AMDATUOPENSOCIAL-198] Fixed:
- Add 'name' property to dashboard
- Do not return gadgets in dashboards for GET /rest/dashboards
- All links should be inside a <links> tag
- Links to the current representation should have rel 'self' instead of
'alternate'
- Gadget links are incorrect; one '/dashboard' to many
- Add dashboard links
- Remove JSON alternative links for subresources in case the main resource is
retrieved as XML, same for XML
- Partially: Remove underscores and upper cases in property names (ie still
userPrefs in gadgets)
Added:
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/rest/DashboardProcessor.java
Modified:
trunk/amdatu-opensocial/opensocial-dashboard/src/main/resources/static/js/dashboard.js
trunk/amdatu-opensocial/opensocial-dashboard/src/main/resources/static/js/rest_dashboard.js
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/bean/Metadata.java
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/bean/dashboard/Dashboard.java
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/bean/dashboard/Gadget.java
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/bean/dashboard/State.java
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/rest/BaseRESTServiceImpl.java
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/rest/DashboardRESTServiceImpl.java
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/rest/GadgetManagementRESTServiceImpl.java
trunk/amdatu-opensocial/release-demo/pom.xml
trunk/amdatu-opensocial/test-integration/tests/src/test/java/org/amdatu/opensocial/test/integration/tests/DashboardTest.java
Modified:
trunk/amdatu-opensocial/opensocial-dashboard/src/main/resources/static/js/dashboard.js
==============================================================================
---
trunk/amdatu-opensocial/opensocial-dashboard/src/main/resources/static/js/dashboard.js
(original)
+++
trunk/amdatu-opensocial/opensocial-dashboard/src/main/resources/static/js/dashboard.js
Wed Feb 22 15:46:30 2012
@@ -154,9 +154,9 @@
shindig.IfrContainer.prototype.gadgetClass = widget.WidgetIfrGadget;
var metadata = widget.metadata;
- if (typeof metadata != 'undefined' && typeof metadata.gadgetUrl !=
'undefined') {
+ if (typeof metadata != 'undefined' && typeof metadata.gadgeturl !=
'undefined') {
var gadget = shindig.container.createGadget(
- {serviceName: widget.id, specUrl: metadata.gadgetUrl, width:
"100%", secureToken: metadata.secureToken, cssClassGadgetContent:"", widget:
widget, height: widget.height});
+ {serviceName: widget.id, specUrl: metadata.gadgeturl, width:
"100%", secureToken: metadata.securetoken, cssClassGadgetContent:"", widget:
widget, height: widget.height});
shindig.container.addGadget(gadget);
shindig.container.renderGadget(gadget);
Modified:
trunk/amdatu-opensocial/opensocial-dashboard/src/main/resources/static/js/rest_dashboard.js
==============================================================================
---
trunk/amdatu-opensocial/opensocial-dashboard/src/main/resources/static/js/rest_dashboard.js
(original)
+++
trunk/amdatu-opensocial/opensocial-dashboard/src/main/resources/static/js/rest_dashboard.js
Wed Feb 22 15:46:30 2012
@@ -63,6 +63,7 @@
success: function(response) {
// Expected result
statusbar.showInfo(response, 2000, true);
+ response.dashboard.name="My dashboard"; // Set default name
dashboard = gadgetsToWidgets(response.dashboard);
jQuery.ajax({
url: putUrl,
@@ -129,7 +130,7 @@
// Returns the user preference values set for the authenticated user
function getUserPrefValues(gadget) {
var url = dbRestBasepath + "/" + gadget.widget.dbid + "/gadgets/" +
gadget.widget.id + "/userprefs";
- url += "?st=" + gadget.secureToken;
+ url += "?st=" + gadget.securetoken;
var userPrefs = {};
jQuery.ajax({
@@ -161,7 +162,7 @@
// Returns the user preference defintions for a particular gadget spec
function getUserPrefDefinitions(gadget) {
var url = dbRestBasepath + "/" + gadget.widget.dbid + "/gadgets/" +
gadget.widget.id;
- url += "?st=" + gadget.secureToken + "&expand=true";
+ url += "?st=" + gadget.securetoken + "&expand=true";
var userPrefs = {};
@@ -191,7 +192,7 @@
function saveUserPrefs(gadget) {
var url = dbRestBasepath + "/" + gadget.widget.dbid + "/gadgets/" +
gadget.widget.id + "/userprefs";
- url += "?st=" + gadget.secureToken;
+ url += "?st=" + gadget.securetoken;
var userprefsBean = {};
userprefsBean["userprefs"] = {};
@@ -237,8 +238,8 @@
this["open"] = this.state.windowstate;
this["url"] = "";
this["metadata"] = {};
- this["metadata"]["secureToken"] = this.state.secureToken;
- this["metadata"]["gadgetUrl"] = this.spec.url;
+ this["metadata"]["securetoken"] = this.state.securetoken;
+ this["metadata"]["gadgeturl"] = this.spec.url;
});
return widgets;
}
@@ -255,7 +256,7 @@
// Create the spec
var spec = {};
- spec["url"] = widgets[i].metadata.gadgetUrl;
+ spec["url"] = widgets[i].metadata.gadgeturl;
gadget["spec"] = spec;
// Create the state
Modified:
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/bean/Metadata.java
==============================================================================
---
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/bean/Metadata.java
(original)
+++
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/bean/Metadata.java
Wed Feb 22 15:46:30 2012
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2010, 2011 The Amdatu Foundation
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@@ -18,22 +18,22 @@
import com.google.gson.annotations.SerializedName;
public class Metadata {
- @SerializedName("secureToken") private String m_secureToken;
- @SerializedName("gadgetUrl") private String m_gadgetUrl;
+ @SerializedName("securetoken") private String m_securetoken;
+ @SerializedName("gadgeturl") private String m_gadgeturl;
- public String getSecureToken() {
- return m_secureToken;
+ public String getSecuretoken() {
+ return m_securetoken;
}
- public void setSecureToken(String secureToken) {
- m_secureToken = secureToken;
+ public void setSecuretoken(String securetoken) {
+ m_securetoken = securetoken;
}
- public String getGadgetUrl() {
- return m_gadgetUrl;
+ public String getGadgeturl() {
+ return m_gadgeturl;
}
- public void setGadgetUrl(String gadgetUrl) {
- m_gadgetUrl = gadgetUrl;
+ public void setGadgeturl(String gadgeturl) {
+ m_gadgeturl = gadgeturl;
}
}
Modified:
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/bean/dashboard/Dashboard.java
==============================================================================
---
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/bean/dashboard/Dashboard.java
(original)
+++
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/bean/dashboard/Dashboard.java
Wed Feb 22 15:46:30 2012
@@ -38,6 +38,9 @@
@SerializedName("id")
private String m_id;
+ @SerializedName("name")
+ private String m_name;
+
@SerializedName("layout")
private Layout m_layout;
@@ -55,6 +58,14 @@
m_id = id;
}
+ public String getName() {
+ return m_name;
+ }
+
+ public void setName(String name) {
+ m_name = name;
+ }
+
public Layout getLayout() {
return m_layout;
}
@@ -80,7 +91,8 @@
m_gadgets.add(gadget);
}
- @XmlElement(name = "links")
+ @XmlElementWrapper(name = "links")
+ @XmlElement(name = "link")
public List<AtomSyndicationLink> getLinks() {
return m_links;
}
@@ -107,9 +119,9 @@
dashboard.setId("db1");
dashboard.setLayout(Layout.getExampleInstance());
dashboard.addGadget(Gadget.getExampleInstance());
- String baseUrl = "http://localhost/rest/dashboards/dashboard/db1?alt=";
+ String baseUrl = "http://localhost/rest/dashboards/db1?alt=";
dashboard.addLink(new AtomSyndicationLink().setHref(baseUrl +
"json").setRel("alternate").setType("application/json"));
- dashboard.addLink(new AtomSyndicationLink().setHref(baseUrl +
"xml").setRel("alternate").setType("application/xml"));
+ dashboard.addLink(new AtomSyndicationLink().setHref(baseUrl +
"xml").setRel("self").setType("application/xml"));
return dashboard;
}
}
Modified:
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/bean/dashboard/Gadget.java
==============================================================================
---
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/bean/dashboard/Gadget.java
(original)
+++
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/bean/dashboard/Gadget.java
Wed Feb 22 15:46:30 2012
@@ -22,6 +22,7 @@
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import com.google.gson.annotations.SerializedName;
@@ -76,7 +77,8 @@
m_userPrefs = userPrefs;
}
- @XmlElement(name = "links")
+ @XmlElementWrapper(name = "links")
+ @XmlElement(name = "link")
public List<AtomSyndicationLink> getLinks() {
return m_links;
}
@@ -106,7 +108,7 @@
gadget.setUserPrefs(UserPrefsBean.getExampleInstance());
String baseUrl =
"http://localhost/rest/dashboards/dashboard/db1/gadgets/gadget1?alt=";
gadget.addLink(new AtomSyndicationLink().setHref(baseUrl +
"json").setRel("alternate").setType("application/json"));
- gadget.addLink(new AtomSyndicationLink().setHref(baseUrl +
"xml").setRel("alternate").setType("application/xml"));
+ gadget.addLink(new AtomSyndicationLink().setHref(baseUrl +
"xml").setRel("self").setType("application/xml"));
return gadget;
}
}
Modified:
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/bean/dashboard/State.java
==============================================================================
---
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/bean/dashboard/State.java
(original)
+++
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/bean/dashboard/State.java
Wed Feb 22 15:46:30 2012
@@ -27,8 +27,8 @@
@SerializedName("windowstate")
private String m_windowstate;
- @SerializedName("secureToken")
- private String m_secureToken;
+ @SerializedName("securetoken")
+ private String m_securetoken;
public String getPosition() {
return m_position;
@@ -54,19 +54,19 @@
m_windowstate = windowstate;
}
- public String getSecureToken() {
- return m_secureToken;
+ public String getSecuretoken() {
+ return m_securetoken;
}
- public void setSecureToken(String secureToken) {
- m_secureToken = secureToken;
+ public void setSecuretoken(String securetoken) {
+ m_securetoken = securetoken;
}
public static State getExampleInstance() {
State state = new State();
state.setIndex(1);
state.setPosition("first");
- state.setSecureToken("RNlnNnbkaN4g3S2Nn");
+ state.setSecuretoken("RNlnNnbkaN4g3S2Nn");
state.setWindowstate("open");
return state;
}
Modified:
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/rest/BaseRESTServiceImpl.java
==============================================================================
---
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/rest/BaseRESTServiceImpl.java
(original)
+++
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/rest/BaseRESTServiceImpl.java
Wed Feb 22 15:46:30 2012
@@ -135,7 +135,7 @@
// Set metadata
Metadata metadata = new Metadata();
- metadata.setGadgetUrl(gadgetUrl);
+ metadata.setGadgeturl(gadgetUrl);
widget.setMetadata(metadata);
if (gadgetSpec != null) {
Added:
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/rest/DashboardProcessor.java
==============================================================================
--- (empty file)
+++
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/rest/DashboardProcessor.java
Wed Feb 22 15:46:30 2012
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2010, 2011 The Amdatu Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.amdatu.opensocial.gadgetmanagement.rest;
+
+import org.amdatu.opensocial.gadgetmanagement.OpenSocialContainer;
+import org.amdatu.opensocial.gadgetmanagement.UserPrefsBean;
+import org.amdatu.opensocial.gadgetmanagement.bean.dashboard.Dashboard;
+import org.amdatu.opensocial.gadgetmanagement.bean.dashboard.Dashboards;
+import org.amdatu.opensocial.gadgetmanagement.bean.dashboard.Gadget;
+import org.amdatu.opensocial.gadgetmanagement.bean.dashboard.Spec;
+
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * This class is responsible for preparing dashboards to be returned to the
client or
+ * to be persisted.
+ *
+ * @author <a href="mailto:[email protected]">Amdatu Project
Team</a>
+ */
+public class DashboardProcessor {
+ private OpenSocialContainer m_osContainer;
+
+ public DashboardProcessor(OpenSocialContainer OpenSocialContainer) {
+ m_osContainer = OpenSocialContainer;
+ }
+
+ // Prepares a collection of dashboards to be persisted. This removes all
+ // data that does not need to be persisted.
+ public void prepareToPersist(Dashboards dashboards) {
+ if (dashboards.getDashboards() != null) {
+ for (Dashboard dashboard : dashboards.getDashboards()) {
+ prepareToPersist(dashboard);
+ }
+ }
+ }
+
+ // Prepares a single dashboard to be persisted. This removes all
+ // data that does not need to be persisted.
+ public void prepareToPersist(Dashboard dashboard) {
+ // -1- Remove the atom links
+ removeLinks(dashboard);
+
+ // -2- Remove the security tokens
+ removeSecureTokens(dashboard);
+
+ // -3- Remove the spec properties (all but the URL)
+ removeSpec(dashboard);
+ }
+
+ // Prepares a collection of dashboards to be returned
+ public void prepareToReturn(Dashboards dashboards, boolean expand,
HttpServletRequest request,
+ HttpHeaders headers) throws Exception {
+ if (dashboards.getDashboards() == null) {
+ return;
+ }
+
+ for (Dashboard dashboard : dashboards.getDashboards()) {
+ prepareToReturn(dashboard, expand, request, headers);
+ }
+ }
+
+ // Prepares a single dashboard to be returned
+ public void prepareToReturn(Dashboard dashboard, boolean expand,
HttpServletRequest request, HttpHeaders headers)
+ throws Exception {
+ // -1- Append atom links to dashboards and gadgets
+ addLinks(dashboard, new Rel(dashboard, request, headers));
+
+ // -2- Expand or remove gadgets
+ if (expand) {
+ // Update secure tokens, these need to be updated each time they
are retrieved.
+ if (dashboard.getGadgets() != null) {
+ for (Gadget gadget : dashboard.getGadgets()) {
+ prepareToReturn(dashboard, gadget, expand, request,
headers, true);
+ }
+ }
+ }
+ else {
+ // We only return the gadgets section in expand mode
+ dashboard.setGadgets(null);
+ }
+ }
+
+ // Prepares a single gadget to be returned
+ public void prepareToReturn(Dashboard dashboard, Gadget gadget, boolean
expand, HttpServletRequest request,
+ HttpHeaders headers, boolean isSubResource)
+ throws Exception {
+ addLinks(dashboard, gadget, new Rel(dashboard, request, headers),
isSubResource);
+ addSecureToken(gadget, request);
+ addGadgetSpec(gadget, request);
+ }
+
+ private void addLinks(Dashboard dashboard, Rel rel) {
+ dashboard.addLink(rel.getBaseUrl() + "?alt=xml", rel.getXml(),
MediaType.APPLICATION_XML);
+ dashboard.addLink(rel.getBaseUrl() + "?alt=json", rel.getJson(),
MediaType.APPLICATION_JSON);
+ }
+
+ private void addLinks(Dashboard dashboard, Gadget gadget, Rel rel, boolean
isSubResource) {
+ String baseUrl = rel.getBaseUrl() + "/gadgets/" + gadget.getId();
+ if (isSubResource) {
+ // If these gadgets are retrieved as sub resources of the
dashboard resource, omit
+ // the alternative links leaving only the "self" link
+ if ("self".equals(rel.getJson())) {
+ gadget.addLink(baseUrl + "?alt=json", rel.getJson(),
MediaType.APPLICATION_JSON);
+ }
+ else {
+ gadget.addLink(baseUrl + "?alt=xml", rel.getXml(),
MediaType.APPLICATION_XML);
+ }
+ }
+ else {
+ gadget.addLink(baseUrl + "?alt=json", rel.getJson(),
MediaType.APPLICATION_JSON);
+ gadget.addLink(baseUrl + "?alt=xml", rel.getXml(),
MediaType.APPLICATION_XML);
+ }
+ }
+
+ // Generate a new secure token and sets it in the gadget state
+ private void addSecureToken(Gadget gadget, HttpServletRequest request)
throws Exception {
+ if (gadget.getSpec() != null) {
+ String st = generateSecurityToken(gadget.getSpec().getUrl(),
request);
+ gadget.getState().setSecuretoken(st);
+ }
+ }
+
+ private String generateSecurityToken(final String gagdgetUrl, final
HttpServletRequest request) {
+ if (gagdgetUrl == null || gagdgetUrl.isEmpty()) {
+ return "";
+ }
+
+ // Retrieve the gadgetspec to get the title of the gadget
+ String url = gagdgetUrl;
+ if (!url.startsWith("http://") && !url.startsWith("https://")) {
+ // This is a relative URL
+ url = toAbsoluteUrl(gagdgetUrl, request);
+
+ }
+ return m_osContainer.getSecurityToken(url, request);
+ }
+
+ // Expands the properties (title, height and UserPrefs) of the gadget spec
from the gadget spec url
+ private void addGadgetSpec(Gadget gadget, HttpServletRequest request) {
+ Spec spec = gadget.getSpec();
+ String url = spec.getUrl();
+ Map<String, String> gadgetSpec = m_osContainer.getGadgetSpec(request,
url, null);
+ if (gadgetSpec != null) {
+ spec.setHeight(getGadgetSpecValue(gadgetSpec, "height", ""));
+ spec.setTitle(getGadgetSpecValue(gadgetSpec, "title", ""));
+ UserPrefsBean userPrefs = m_osContainer.getUserPreferences(url,
null);
+ gadget.getSpec().setUserprefs(userPrefs.getUserprefs());
+ }
+ }
+
+ private void removeLinks(Dashboard dashboard) {
+ dashboard.setLinks(null);
+ if (dashboard.getGadgets() != null) {
+ for (Gadget gadget : dashboard.getGadgets()) {
+ gadget.setLinks(null);
+ }
+ }
+ }
+
+ private void removeSecureTokens(Dashboard dashboard) {
+ if (dashboard.getGadgets() != null) {
+ for (Gadget gadget : dashboard.getGadgets()) {
+ if (gadget.getState() != null) {
+ gadget.getState().setSecuretoken(null);
+ }
+ }
+ }
+ }
+
+ // Remove the spec properties, leaving only the URL
+ private void removeSpec(Dashboard dashboard) {
+ if (dashboard.getGadgets() != null) {
+ for (Gadget gadget : dashboard.getGadgets()) {
+ if (gadget.getSpec() != null) {
+ gadget.getSpec().setHeight(null);
+ gadget.getSpec().setTitle(null);
+ gadget.getSpec().setUserprefs(null);
+ }
+ }
+ }
+ }
+
+ private String getGadgetSpecValue(Map<String, String> spec, String key,
String defaultValue) {
+ if (spec.get(key) != null && !"".equals(spec.get(key))) {
+ return spec.get(key);
+ }
+ else {
+ return defaultValue;
+ }
+ }
+
+ private String toAbsoluteUrl(final String url, final HttpServletRequest
request) {
+ if (url.startsWith("/")) {
+ // This is a relative URL, convert to absolute URL
+ String baseUrl = request.getScheme() + "://" +
request.getServerName() + ":" + request.getServerPort();
+ return baseUrl + request.getContextPath() + url;
+ }
+ return url;
+ }
+
+ private class Rel {
+ private String m_baseUrl, m_json, m_xml;
+
+ Rel(Dashboard dashboard, HttpServletRequest request, HttpHeaders
headers) {
+ m_baseUrl =
+ request.getServerName() + ":" + request.getServerPort() +
"/rest/dashboards/" + dashboard.getId();
+ MediaType type = headers.getAcceptableMediaTypes().get(0);
+ if (type.equals(MediaType.valueOf(MediaType.APPLICATION_JSON))) {
+ m_json = "self";
+ m_xml = "alternate";
+ }
+ else {
+ // XML is the default
+ m_xml = "self";
+ m_json = "alternate";
+ }
+ }
+
+ String getJson() {
+ return m_json;
+ }
+
+ String getXml() {
+ return m_xml;
+ }
+
+ String getBaseUrl() {
+ return m_baseUrl;
+ }
+ }
+}
Modified:
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/rest/DashboardRESTServiceImpl.java
==============================================================================
---
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/rest/DashboardRESTServiceImpl.java
(original)
+++
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/rest/DashboardRESTServiceImpl.java
Wed Feb 22 15:46:30 2012
@@ -47,6 +47,7 @@
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@@ -63,7 +64,7 @@
* @author <a href="mailto:[email protected]">Amdatu Project
Team</a>
*/
@Path("dashboards")
-@SuppressWarnings({ "unchecked" })
+@SuppressWarnings({"unchecked"})
public class DashboardRESTServiceImpl extends BaseRESTServiceImpl implements
ResourceProvider {
// The name of the property in AppData used to store personal dashboards
for a particular user.
private static final String DASHBOARDS = "dashboards";
@@ -76,14 +77,23 @@
private volatile BundleContext m_bundleContext;
+ private DashboardProcessor m_dbProcessor;
+
+ public void start() {
+ super.start();
+ m_dbProcessor = new DashboardProcessor(getOpenSocialContainer());
+ }
+
/**
* Returns all personalized dashboards for the authenticated user. A
dashboard contains UI properties like
* the layout of the dashboard and the set of gadgets displayed on that
dashboard.
*
* @see org.amdatu.opensocial.gadgetmanagement.bean.dashboard.Dashboards
* @param expand If expand is not provided or does not equal 'true', the
'spec' element of each gadget only
- * returns the url of the gadgetspec xml. If expand is set to
'true', the XML is retrieved and properties like
- * title, height and userprefs are added to the gadget spec
element. As this requires an additional HTTP request
+ * returns the url of the gadgetspec xml. If expand is set to
'true', the XML is retrieved and properties
+ * like
+ * title, height and userprefs are added to the gadget spec
element. As this requires an additional HTTP
+ * request
* for each gadget, adding these properties is optional using this
query parameter.
* @param request The HTTP request
* @return <ul>
@@ -93,8 +103,9 @@
* </ul>
*/
@GET
- @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
- public Response getDashboards(@QueryParam("expand") final String expand,
@Context final HttpServletRequest request) {
+ @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
+ public Response getDashboards(@QueryParam("expand") final String expand,
@Context final HttpServletRequest request,
+ @Context final HttpHeaders headers) {
try {
if (isAuthorized(request)) {
// Persist the dashboard as complete JSON expression
@@ -104,15 +115,7 @@
Object appData = user.getProperties().get(APP_DATA);
if (appData != null) {
Dashboards dashboards = load(user);
-
- // Update secure tokens, these need to be updated each
time they are retrieved.
- updateSecureTokens(dashboards, request);
-
- // Expand gadget specs if requested
- if ("true".equals(expand)) {
- expandGadgetSpecs(dashboards, request);
- }
-
+ m_dbProcessor.prepareToReturn(dashboards,
"true".equals(expand), request, headers);
return get200(dashboards);
}
@@ -136,8 +139,10 @@
* @see org.amdatu.opensocial.gadgetmanagement.bean.dashboard.Dashboard
* @param id The id of the dashboard to write the properties for
* @param expand If expand is not provided or does not equal 'true', the
'spec' element of each gadget only
- * returns the url of the gadgetspec xml. If expand is set to
'true', the XML is retrieved and properties like
- * title, height and userprefs are added to the gadget spec
element. As this requires an additional HTTP request
+ * returns the url of the gadgetspec xml. If expand is set to
'true', the XML is retrieved and properties
+ * like
+ * title, height and userprefs are added to the gadget spec
element. As this requires an additional HTTP
+ * request
* for each gadget, adding these properties is optional using this
query parameter.
* @param request The HTTP servlet request
* @return <ul>
@@ -147,10 +152,10 @@
* </ul>
*/
@POST
- @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
- @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+ @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
+ @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response createDashboard(final Dashboard dashboard,
@QueryParam("expand") final String expand,
- @Context final HttpServletRequest request) {
+ @Context final HttpServletRequest request, @Context final HttpHeaders
headers) {
try {
if (isAuthorized(request)) {
// Retrieve the personal dashboards for this user
@@ -159,18 +164,12 @@
// Generate a unique id for the new dashboard and add links
dashboard.setId(generateId(dashboards));
- addDashboardLinks(dashboard, request);
+ dashboard.setName("Dashboard " + dashboard.getId()); // Set
default name
dashboards.addDashboard(dashboard);
-
- // Expand gadget specs if requested
- if ("true".equals(expand)) {
- expandGadgetSpecs(dashboards, request);
- }
-
- // Set the personal dashboards for this user
save(user, dashboards);
// Return the added dashboard
+ m_dbProcessor.prepareToReturn(dashboard,
"true".equals(expand), request, headers);
return get200(dashboard);
}
return get401("Error 401 - Unauthorized. Only authenticated users
can create a dashboard.");
@@ -196,11 +195,11 @@
* </ul>
*/
@GET
- @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+ @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@Path("/{dashboardid}")
public Response getDashboard(@PathParam("dashboardid") final String
dashboardid,
- @QueryParam("expand") final String expand,
- @Context final HttpServletRequest request) {
+ @QueryParam("expand") final String expand, @Context final
HttpServletRequest request,
+ @Context final HttpHeaders headers) {
try {
if (DEFAULT_DBID.equals(dashboardid)) {
// Special case: the default dashboard is requested, which
contains the gadgets that
@@ -208,12 +207,7 @@
// so the user does not need to be authenticated in order to
retrieve this dashboard.
// return the default dashboard
Dashboard dashboard = getDefaultDashboard(request);
-
- // Expand gadget specs if requested
- if ("true".equals(expand)) {
- expandGadgetSpecs(dashboard, request);
- }
-
+ m_dbProcessor.prepareToReturn(dashboard,
"true".equals(expand), request, headers);
return get200(dashboard);
}
@@ -228,13 +222,7 @@
Dashboards dashboards = load(user);
Dashboard dashboard = findDashboard(dashboards,
dashboardid);
if (dashboard != null) {
- // Generate new security tokens for the gadgets stored
in the dashboard
- updateSecureTokens(dashboards, request);
-
- // Expand gadget specs if requested
- if ("true".equals(expand)) {
- expandGadgetSpecs(dashboard, request);
- }
+ m_dbProcessor.prepareToReturn(dashboard,
"true".equals(expand), request, headers);
// Return the dashboard
return get200(dashboard);
@@ -265,8 +253,8 @@
* </ul>
*/
@PUT
- @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
- @Produces({ MediaType.TEXT_PLAIN })
+ @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
+ @Produces({MediaType.TEXT_PLAIN})
@Path("/{dashboardid}")
public Response setDashboard(@PathParam("dashboardid") final String
dashboardid, final Dashboard dashboard,
@Context final HttpServletRequest request) {
@@ -285,7 +273,10 @@
Dashboard oldDb = findDashboard(dashboards, dashboardid);
if (oldDb != null) {
for (Gadget oldGadget : oldDb.getGadgets()) {
- findGadget(dashboard,
oldGadget.getId()).setUserPrefs(oldGadget.getUserPrefs());
+ Gadget gadget = findGadget(dashboard,
oldGadget.getId());
+ if (gadget != null) {
+ gadget.setUserPrefs(oldGadget.getUserPrefs());
+ }
}
}
@@ -295,6 +286,7 @@
dashboards.addDashboard(dashboard);
// Save the personal dashboards
+ m_dbProcessor.prepareToPersist(dashboards);
save(user, dashboards);
return get200("Dashboard '" + dashboardid + "' for user '" +
user.getName() + "' saved successfully.");
@@ -313,8 +305,10 @@
* @param dashboardid The id of the dashboard to retrieve the gadget from
* @param gadgetid The id of the gadget to retrieve
* @param expand If expand is not provided or does not equal 'true', the
'spec' element of each gadget only
- * returns the url of the gadgetspec xml. If expand is set to
'true', the XML is retrieved and properties like
- * title, height and userprefs are added to the gadget spec
element. As this requires an additional HTTP request
+ * returns the url of the gadgetspec xml. If expand is set to
'true', the XML is retrieved and properties
+ * like
+ * title, height and userprefs are added to the gadget spec
element. As this requires an additional HTTP
+ * request
* @param request The HTTP servlet request
* @return <ul>
* <li>200 (OK) : The specified gadget is returned.</li>
@@ -324,11 +318,11 @@
* </ul>
*/
@GET
- @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
@Path("/{dashboardid}/gadgets/{gadgetid}")
public Response getGadget(@PathParam("dashboardid") final String
dashboardid,
- @PathParam("gadgetid") final String gadgetid,
- @QueryParam("expand") final String expand, @Context final
HttpServletRequest request) {
+ @PathParam("gadgetid") final String gadgetid, @QueryParam("expand")
final String expand,
+ @Context final HttpServletRequest request, @Context final HttpHeaders
headers) {
try {
if (!DEFAULT_DBID.equals(dashboardid) && !isAuthorized(request)) {
return get401("Error 401 - Unauthorized. Only authenticated
users can retrieve gadgets.");
@@ -346,12 +340,7 @@
if (dashboard != null) {
// Find the gadget
Gadget gadget = findGadget(dashboard, gadgetid);
-
- // Expand gadget specs if requested
- if ("true".equals(expand)) {
- expandGadgetSpec(gadget, request);
- }
-
+ m_dbProcessor.prepareToReturn(dashboard, gadget,
"true".equals(expand), request, headers, false);
return get200(gadget);
}
else {
@@ -371,8 +360,10 @@
*
* @see org.amdatu.opensocial.gadgetmanagement.bean.dashboard.Gadget
* @param expand If expand is not provided or does not equal 'true', the
'spec' element of each gadget only
- * returns the url of the gadgetspec xml. If expand is set to
'true', the XML is retrieved and properties like
- * title, height and userprefs are added to the gadget spec
element. As this requires an additional HTTP request
+ * returns the url of the gadgetspec xml. If expand is set to
'true', the XML is retrieved and properties
+ * like
+ * title, height and userprefs are added to the gadget spec
element. As this requires an additional HTTP
+ * request
* @param dashboardid The id of the personalized dashboard to append the
gadget to
* @param gadget The properties of the gadget to add
* @param request The HTTP servlet request
@@ -384,11 +375,12 @@
* </ul>
*/
@POST
- @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
- @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+ @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
@Path("/{dashboardid}/gadgets")
public Response createGadget(@PathParam("dashboardid") final String
dashboardid, final Gadget gadget,
- @QueryParam("expand") final String expand, @Context final
HttpServletRequest request) {
+ @QueryParam("expand") final String expand, @Context final
HttpServletRequest request,
+ @Context final HttpHeaders headers) {
try {
if (DEFAULT_DBID.equals(dashboardid)) {
return get401("Error 401 - Unauthorized. The default dashboard
cannot be updated.");
@@ -400,18 +392,13 @@
Dashboard dashboard = findDashboard(dashboards, dashboardid);
if (dashboard != null) {
// Create and add the gadget links
- createGadgetLinks(dashboard, gadget, request);
gadget.setId(generateId(dashboard));
dashboard.addGadget(gadget);
- // Expand gadget specs if requested
- if ("true".equals(expand)) {
- expandGadgetSpec(gadget, request);
- }
-
// Save the personal dashboards
save(user, dashboards);
+ m_dbProcessor.prepareToReturn(dashboard, gadget,
"true".equals(expand), request, headers, false);
return get200(gadget);
}
else {
@@ -446,7 +433,7 @@
*/
@GET
@Path("{dashboardid}/gadgets/{gadgetid}/userprefs")
- @Produces({ MediaType.APPLICATION_JSON })
+ @Produces({MediaType.APPLICATION_JSON})
public Response getUserPrefs(@PathParam("dashboardid") final String
dashboardid,
@PathParam("gadgetid") final String gadgetid, @Context final
HttpServletRequest request) {
try {
@@ -517,8 +504,8 @@
*/
@PUT
@Path("{dashboardid}/gadgets/{gadgetid}/userprefs")
- @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
- @Produces({ MediaType.TEXT_PLAIN })
+ @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
+ @Produces({MediaType.TEXT_PLAIN})
public Response setUserPrefs(@PathParam("dashboardid") final String
dashboardid,
@PathParam("gadgetid") final String gadgetid, UserPrefsBean userPrefs,
@Context final HttpServletRequest request) {
try {
@@ -577,7 +564,7 @@
return new Integer(id).toString();
}
-// Load the dashboards for the specified user
+ // Load the dashboards for the specified user
private Dashboards load(User user) throws ClassNotFoundException,
IOException {
Object appData = user.getProperties().get(APP_DATA);
if (appData != null) {
@@ -590,7 +577,7 @@
return new Dashboards();
}
-// Save the dashboards for the specified user
+ // Save the dashboards for the specified user
private void save(User user, Dashboards dashboards) throws IOException,
ClassNotFoundException {
Object appData = user.getProperties().get(APP_DATA);
Map<String, Object> appDataValue;
@@ -635,6 +622,7 @@
Layout layout = new Layout();
layout.setId("layout3");
dashboard.setId(DEFAULT_DBID);
+ dashboard.setName("Default dashboard");
dashboard.setLayout(layout);
List<Gadget> gadgets = getDefaultGadgets(request);
for (Gadget gadget : gadgets) {
@@ -670,96 +658,12 @@
spec.setUrl(gadgetUrl);
gadget.setSpec(spec);
- // Generate a security token and retrieve the title and height
from the spec
- updateSecureToken(gadget, request);
-
gadgets.add(gadget);
}
return gadgets;
}
- private String generateSecurityToken(final String gagdgetUrl, final
HttpServletRequest request) {
- if (gagdgetUrl == null || gagdgetUrl.isEmpty()) {
- return "";
- }
-
- // Retrieve the gadgetspec to get the title of the gadget
- String url = gagdgetUrl;
- if (!url.startsWith("http://") && !url.startsWith("https://")) {
- // This is a relative URL
- url = toAbsoluteUrl(gagdgetUrl, request);
-
- }
- return getOpenSocialContainer().getSecurityToken(url, request);
- }
-
- private void addDashboardLinks(Dashboard dashboard, HttpServletRequest
request) {
- String baseUrl =
- request.getServerName() + ":" + request.getServerPort() +
"/rest/dashboards/" + dashboard.getId();
- dashboard.addLink(baseUrl + "?alt=json", "alternate",
MediaType.APPLICATION_JSON);
- dashboard.addLink(baseUrl + "?alt=xml", "alternate",
MediaType.APPLICATION_XML);
- }
-
- private void createGadgetLinks(Dashboard dashboard, Gadget gadget,
HttpServletRequest request) {
- String baseUrl = request.getServerName() + ":" +
request.getServerPort() + "/rest/dashboards/"
- + dashboard.getId() + "/gadgets/" + gadget.getId();
- gadget.addLink(baseUrl + "?alt=json", "alternate",
MediaType.APPLICATION_JSON);
- gadget.addLink(baseUrl + "?alt=xml", "alternate",
MediaType.APPLICATION_XML);
- }
-
-// Generate new security tokens for the gadgets stored in each dashboard.
-// Security tokens are stored in APP_DATA but are typically short lived
-// and so they probably expired when the user logs in. Security tokens
-// should be re-generated each time the user opens the dashboard.
- private void updateSecureTokens(Dashboards dashboards, HttpServletRequest
request) throws Exception {
- if (dashboards.getDashboards() != null) {
- for (Dashboard dashboard : dashboards.getDashboards()) {
- if (dashboard.getGadgets() != null) {
- for (Gadget gadget : dashboard.getGadgets()) {
- // Generate a security token and retrieve the title
and height from the spec
- updateSecureToken(gadget, request);
- }
- }
- }
- }
- }
-
-// Generate a new secure token and sets it in the gadget state
- private void updateSecureToken(Gadget gadget, HttpServletRequest request)
throws Exception {
- if (gadget.getSpec() != null) {
- String st = generateSecurityToken(gadget.getSpec().getUrl(),
request);
- gadget.getState().setSecureToken(st);
- }
- }
-
-// Expands the properties (title, height and UserPrefs) of all gadgets on the
dashboard
- private void expandGadgetSpecs(Dashboards dashboards, HttpServletRequest
request) {
- for (Dashboard dashboard : dashboards.getDashboards()) {
- expandGadgetSpecs(dashboard, request);
- }
- }
-
-// Expands the properties (title, height and UserPrefs) of all gadgets on the
dashboard
- private void expandGadgetSpecs(Dashboard dashboard, HttpServletRequest
request) {
- for (Gadget gadget : dashboard.getGadgets()) {
- expandGadgetSpec(gadget, request);
- }
- }
-
-// Expands the properties (title, height and UserPrefs) of the gadget spec
from the gadget spec url
- private void expandGadgetSpec(Gadget gadget, HttpServletRequest request) {
- Spec spec = gadget.getSpec();
- String url = spec.getUrl();
- Map<String, String> gadgetSpec =
getOpenSocialContainer().getGadgetSpec(request, url, null);
- if (gadgetSpec != null) {
- spec.setHeight(getGadgetSpecValue(gadgetSpec, "height", ""));
- spec.setTitle(getGadgetSpecValue(gadgetSpec, "title", ""));
- UserPrefsBean userPrefs =
getOpenSocialContainer().getUserPreferences(url, null);
- gadget.getSpec().setUserprefs(userPrefs.getUserprefs());
- }
- }
-
-// FIXE; currently only /doc/api/dashboards/ works properly, without the last
'.' the CSS is missing
+ // FIXE; currently only /doc/api/dashboards/ works properly, without the
last '.' the CSS is missing
public URL getResource(String name) {
if (name.startsWith(Activator.DASHBOARDS_DOC_ALIAS)) {
String resource =
name.substring(Activator.DASHBOARDS_DOC_ALIAS.length());
Modified:
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/rest/GadgetManagementRESTServiceImpl.java
==============================================================================
---
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/rest/GadgetManagementRESTServiceImpl.java
(original)
+++
trunk/amdatu-opensocial/opensocial-gadgetmanagement/src/main/java/org/amdatu/opensocial/gadgetmanagement/rest/GadgetManagementRESTServiceImpl.java
Wed Feb 22 15:46:30 2012
@@ -149,9 +149,9 @@
Widget widget = getWidget(request,
toAbsoluteUrl(gadgetDefinition.getUrl(), request), null);
if (widget != null) {
// Be sure to add the security token
- String gadgetUrl = widget.getMetadata().getGadgetUrl();
+ String gadgetUrl = widget.getMetadata().getGadgeturl();
String st =
getOpenSocialContainer().getSecurityToken(gadgetUrl, request);
- widget.getMetadata().setSecureToken(st);
+ widget.getMetadata().setSecuretoken(st);
// Assign a unique id
widget.setId(Integer.toString(id++));
@@ -201,7 +201,7 @@
if (widget != null) {
// Be sure to add the security token
String st = getOpenSocialContainer().getSecurityToken(uri,
request);
- widget.getMetadata().setSecureToken(st);
+ widget.getMetadata().setSecuretoken(st);
return Response.ok(widget,
MediaType.APPLICATION_JSON_TYPE).cacheControl(NO_CACHE_CONTROL).build();
}
return
Response.status(Response.Status.NOT_FOUND).cacheControl(NO_CACHE_CONTROL).build();
@@ -318,7 +318,7 @@
if (widget != null) {
// Be sure to add the security token
String st = getOpenSocialContainer().getSecurityToken(uri,
request);
- widget.getMetadata().setSecureToken(st);
+ widget.getMetadata().setSecuretoken(st);
widget.setId(Integer.toString(getStartId(request)));
Modified: trunk/amdatu-opensocial/release-demo/pom.xml
==============================================================================
--- trunk/amdatu-opensocial/release-demo/pom.xml (original)
+++ trunk/amdatu-opensocial/release-demo/pom.xml Wed Feb 22 15:46:30 2012
@@ -445,7 +445,12 @@
<type>jar</type>
<scope>compile</scope>
</dependency>
-
+ <dependency>
+ <groupId>org.amdatu.opensocial</groupId>
+ <artifactId>org.amdatu.opensocial.gadgetpack</artifactId>
+ <type>bundle</type>
+ <scope>compile</scope>
+ </dependency>
</dependencies>
<build>
<defaultGoal>package</defaultGoal>
Modified:
trunk/amdatu-opensocial/test-integration/tests/src/test/java/org/amdatu/opensocial/test/integration/tests/DashboardTest.java
==============================================================================
---
trunk/amdatu-opensocial/test-integration/tests/src/test/java/org/amdatu/opensocial/test/integration/tests/DashboardTest.java
(original)
+++
trunk/amdatu-opensocial/test-integration/tests/src/test/java/org/amdatu/opensocial/test/integration/tests/DashboardTest.java
Wed Feb 22 15:46:30 2012
@@ -69,7 +69,7 @@
// 5a. POST /rest/dashboads (XML). A dashboard should have been
created and returned in JSON format.
JSONObject db1 = createDashboard();
String dashboardXml = toXML(db1);
- response = invokeHTTP("/rest/dashboards?alt=json", POST,
APPLICATION_XML, dashboardXml, SC_OK);
+ response = invokeHTTP("/rest/dashboards?alt=json&expand=true", POST,
APPLICATION_XML, dashboardXml, SC_OK);
// 5b. Remember the id generated for this new dashboard
db1 = new JSONObject(response);
@@ -118,13 +118,13 @@
response = invokeHTTP("/rest/dashboards/" + db1Id +
"/gadgets?alt=json", POST, APPLICATION_JSON, json, SC_OK);
// 8b. GET /rest/dashboards/db1 (JSON). The added gadget should be
returned.
- response = invokeHTTP("/rest/dashboards/" + db1Id + "?alt=json", GET,
SC_OK);
+ response = invokeHTTP("/rest/dashboards/" + db1Id +
"?alt=json&expand=true", GET, SC_OK);
jsonObject = removeGeneratedProperties(new JSONObject(response));
db1.getJSONObject("dashboard").getJSONObject("gadgets").append("gadget",
gadget);
Assert.assertTrue("8b failed: dashboard not updated",
BeanUtil.compareJSON(jsonObject, db1));
// 8c. GET /rest/dashboards/db1/3 (JSON. The added gadget should be
returned.
- response = invokeHTTP("/rest/dashboards/" + db1Id +
"/gadgets/3?alt=json", GET, SC_OK);
+ response = invokeHTTP("/rest/dashboards/" + db1Id +
"/gadgets/3?alt=json&expand=true", GET, SC_OK);
jsonObject = removeGeneratedProperties(new JSONObject(response));
Assert.assertFalse("8c failed: added gadget not returned",
"".equals(response));
@@ -171,7 +171,7 @@
private JSONObject removeSecureTokens(JSONObject jsonObj) throws
JSONException {
List<JSONObject> states = get(jsonObj,
"dashboard.gadgets.gadget.state");
for (JSONObject state : states) {
- state.remove("secureToken");
+ state.remove("securetoken");
}
return jsonObj;
}
_______________________________________________
Amdatu-commits mailing list
[email protected]
http://lists.amdatu.org/mailman/listinfo/amdatu-commits