You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by dh...@apache.org on 2015/04/06 22:38:09 UTC
[17/23] camel git commit: CAMEL-6568: Initial version of LinkedIn
component
CAMEL-6568: Initial version of LinkedIn component
Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/b490a90c
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/b490a90c
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/b490a90c
Branch: refs/heads/linkedin-component
Commit: b490a90cd2d4ecd2d2382e0c09323db75b9c0918
Parents: 91e19c1
Author: Dhiraj Bokde <dh...@yahoo.com>
Authored: Thu Jul 10 17:46:47 2014 -0700
Committer: Dhiraj Bokde <dh...@yahoo.com>
Committed: Thu Jul 10 17:49:20 2014 -0700
----------------------------------------------------------------------
.../camel-linkedin/camel-linkedin-api/pom.xml | 214 ++
.../component/linkedin/api/DoubleAdapter.java | 35 +
.../linkedin/api/LinkedInException.java | 47 +
.../api/LinkedInExceptionResponseFilter.java | 77 +
.../api/LinkedInOAuthRequestFilter.java | 269 +++
.../component/linkedin/api/LongAdapter.java | 35 +
.../component/linkedin/api/OAuthParams.java | 107 +
.../component/linkedin/api/OAuthScope.java | 64 +
.../linkedin/api/OAuthSecureStorage.java | 36 +
.../component/linkedin/api/OAuthToken.java | 50 +
.../src/main/resources/linkedin-api-schema.xjb | 447 ++++
.../src/main/resources/linkedin-api-schema.xsd | 2255 ++++++++++++++++++
.../src/main/resources/linkedin-api-wadl.xml | 1045 ++++++++
.../src/main/resources/wadl.xsd | 263 ++
.../api/AbstractResourceIntegrationTest.java | 125 +
.../api/PeopleResourceIntegrationTest.java | 99 +
.../api/SearchResourceIntegrationTest.java | 47 +
.../camel-linkedin-component/pom.xml | 280 +++
.../component/linkedin/LinkedInComponent.java | 106 +
.../linkedin/LinkedInConfiguration.java | 155 ++
.../component/linkedin/LinkedInConsumer.java | 58 +
.../component/linkedin/LinkedInEndpoint.java | 174 ++
.../component/linkedin/LinkedInProducer.java | 59 +
.../internal/CachingOAuthSecureStorage.java | 50 +
.../linkedin/internal/LinkedInConstants.java | 29 +
.../internal/LinkedInPropertiesHelper.java | 40 +
.../org/apache/camel/component/linkedin | 1 +
.../linkedin/AbstractLinkedInTestSupport.java | 73 +
.../CommentsResourceIntegrationTest.java | 66 +
.../CompaniesResourceIntegrationTest.java | 353 +++
.../linkedin/GroupsResourceIntegrationTest.java | 65 +
.../linkedin/JobsResourceIntegrationTest.java | 93 +
.../linkedin/PeopleResourceIntegrationTest.java | 636 +++++
.../linkedin/PostsResourceIntegrationTest.java | 162 ++
.../linkedin/SearchResourceIntegrationTest.java | 161 ++
.../src/test/resources/log4j.properties | 36 +
.../src/test/resources/test-options.properties | 28 +
components/camel-linkedin/pom.xml | 66 +
components/pom.xml | 1 +
39 files changed, 7907 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/camel/blob/b490a90c/components/camel-linkedin/camel-linkedin-api/pom.xml
----------------------------------------------------------------------
diff --git a/components/camel-linkedin/camel-linkedin-api/pom.xml b/components/camel-linkedin/camel-linkedin-api/pom.xml
new file mode 100644
index 0000000..012d04f
--- /dev/null
+++ b/components/camel-linkedin/camel-linkedin-api/pom.xml
@@ -0,0 +1,214 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.camel.component.linkedin</groupId>
+ <artifactId>camel-linkedin-parent</artifactId>
+ <version>2.14-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>camel-linkedin-api</artifactId>
+ <name>Camel LinkedIn Component API</name>
+ <description>API for Camel LinkedIn Component</description>
+ <packaging>bundle</packaging>
+
+ <properties>
+ <camel.osgi.export.pkg>org.apache.camel.component.linkedin.api*</camel.osgi.export.pkg>
+ <htmlunit-version>2.15</htmlunit-version>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-core</artifactId>
+ <version>${cxf-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-frontend-jaxrs</artifactId>
+ <version>${cxf-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-rs-security-oauth2</artifactId>
+ <version>${cxf-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-rs-extension-providers</artifactId>
+ <version>${cxf-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-tools-wadlto-jaxrs</artifactId>
+ <version>${cxf-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>net.sourceforge.htmlunit</groupId>
+ <artifactId>htmlunit</artifactId>
+ <version>${htmlunit-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ <version>${jackson2-version}</version>
+ </dependency>
+
+ <!-- logging -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <!-- testing -->
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-rs-client</artifactId>
+ <version>${cxf-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <testResources>
+ <testResource>
+ <directory>${project.basedir}/src/test/resources</directory>
+ </testResource>
+ <testResource>
+ <directory>${project.basedir}/../camel-linkedin-component/src/test/resources</directory>
+ </testResource>
+ </testResources>
+
+ <plugins>
+
+ <!-- uncomment to validate XSD since wadl2java doesn't report line numbers in errors -->
+<!--
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>jaxb2-maven-plugin</artifactId>
+ <version>1.6</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>xjc</goal>
+ </goals>
+ <configuration>
+ <target>2.1</target>
+ <schemaDirectory>src/main/resources</schemaDirectory>
+ <schemaFiles>linkedin-api-schema.xsd</schemaFiles>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+-->
+
+ <!-- Generate API from WADL -->
+ <plugin>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-wadl2java-plugin</artifactId>
+ <version>${cxf-version}</version>
+ <executions>
+ <execution>
+ <id>generate-wadl-sources</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>wadl2java</goal>
+ </goals>
+ <configuration>
+ <sourceRoot>${project.build.directory}/generated-sources/cxf</sourceRoot>
+ <wadlOptions>
+ <wadlOption>
+ <wadl>${project.basedir}/src/main/resources/linkedin-api-wadl.xml</wadl>
+ <packagename>org.apache.camel.component.linkedin.api</packagename>
+ <bindingFiles>
+ <bindingFile>${project.basedir}/src/main/resources/linkedin-api-schema.xjb</bindingFile>
+ </bindingFiles>
+ <extraargs>
+ <extraarg>-verbose</extraarg>
+ <extraarg>-generateEnums</extraarg>
+ <extraarg>-xjc-quiet</extraarg>
+ </extraargs>
+ </wadlOption>
+ </wadlOptions>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ <!-- add generated source to build -->
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>add-generated-sources</id>
+ <goals>
+ <goal>add-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>${project.build.directory}/generated-sources/cxf</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ <!-- to generate API Javadoc -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>add-javadoc</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ <configuration>
+ <attach>true</attach>
+ <source>1.6</source>
+ <quiet>true</quiet>
+ <detectOfflineLinks>false</detectOfflineLinks>
+ <javadocVersion>1.6</javadocVersion>
+ <encoding>UTF-8</encoding>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ </plugins>
+ </build>
+
+</project>
http://git-wip-us.apache.org/repos/asf/camel/blob/b490a90c/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/DoubleAdapter.java
----------------------------------------------------------------------
diff --git a/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/DoubleAdapter.java b/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/DoubleAdapter.java
new file mode 100644
index 0000000..029681a
--- /dev/null
+++ b/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/DoubleAdapter.java
@@ -0,0 +1,35 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.component.linkedin.api;
+
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+
+public class DoubleAdapter extends XmlAdapter<String, Double>
+{
+
+ public Double unmarshal(String value) {
+ return javax.xml.bind.DatatypeConverter.parseDouble(value);
+ }
+
+ public String marshal(Double value) {
+ if (value == null) {
+ return null;
+ }
+ return javax.xml.bind.DatatypeConverter.printDouble(value);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b490a90c/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/LinkedInException.java
----------------------------------------------------------------------
diff --git a/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/LinkedInException.java b/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/LinkedInException.java
new file mode 100644
index 0000000..74b7520
--- /dev/null
+++ b/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/LinkedInException.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.component.linkedin.api;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Response;
+
+import org.apache.camel.component.linkedin.api.model.Error;
+
+/**
+ * Exception wrapper for {@link org.apache.camel.component.linkedin.api.model.Error}
+ */
+public class LinkedInException extends WebApplicationException {
+
+ private static final long serialVersionUID = -6570614972033527197L;
+
+ private final Error error;
+ private final Response response;
+
+ public LinkedInException(Error error, Response response) {
+ super(error.getMessage(), response);
+ this.error = error;
+ this.response = response;
+ }
+
+ public Error getError() {
+ return error;
+ }
+
+ public Response getResponse() {
+ return response;
+ }
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b490a90c/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/LinkedInExceptionResponseFilter.java
----------------------------------------------------------------------
diff --git a/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/LinkedInExceptionResponseFilter.java b/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/LinkedInExceptionResponseFilter.java
new file mode 100644
index 0000000..4b180d9
--- /dev/null
+++ b/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/LinkedInExceptionResponseFilter.java
@@ -0,0 +1,77 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.component.linkedin.api;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.Priority;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientResponseContext;
+import javax.ws.rs.client.ClientResponseFilter;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.Provider;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+
+import org.apache.camel.component.linkedin.api.model.Error;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Response filter for throwing {@link LinkedInException}
+ * when response contains {@link org.apache.camel.component.linkedin.api.model.Error}
+ */
+@Provider
+@Priority(Priorities.USER)
+public class LinkedInExceptionResponseFilter implements ClientResponseFilter {
+
+ private static final Logger LOG = LoggerFactory.getLogger(LinkedInExceptionResponseFilter.class);
+ private final JAXBContext jaxbContext;
+
+ public LinkedInExceptionResponseFilter() {
+ try {
+ jaxbContext = JAXBContext.newInstance(Error.class.getPackage().getName());
+ } catch (JAXBException e) {
+ throw new IllegalArgumentException("Error initializing JAXB: " + e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
+ if (responseContext.getStatus() != Response.Status.OK.getStatusCode() && responseContext.hasEntity()) {
+ try {
+ final Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
+ final Error error = (Error) unmarshaller.unmarshal(responseContext.getEntityStream());
+
+ final Response.ResponseBuilder builder = Response.status(responseContext.getStatusInfo());
+ builder.entity(error);
+ // copy response headers
+ for (Map.Entry<String, List<String>> header : responseContext.getHeaders().entrySet()) {
+ builder.header(header.getKey(), header.getValue());
+ }
+
+ throw new LinkedInException(error, builder.build());
+ } catch (JAXBException e) {
+ // log and ignore
+ LOG.warn("Unable to parse LinkedIn error: " + e.getMessage(), e);
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b490a90c/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/LinkedInOAuthRequestFilter.java
----------------------------------------------------------------------
diff --git a/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/LinkedInOAuthRequestFilter.java b/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/LinkedInOAuthRequestFilter.java
new file mode 100644
index 0000000..4fd7194
--- /dev/null
+++ b/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/LinkedInOAuthRequestFilter.java
@@ -0,0 +1,269 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.component.linkedin.api;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.security.SecureRandom;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.annotation.Priority;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.ext.Provider;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.gargoylesoftware.htmlunit.BrowserVersion;
+import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
+import com.gargoylesoftware.htmlunit.HttpMethod;
+import com.gargoylesoftware.htmlunit.Page;
+import com.gargoylesoftware.htmlunit.ProxyConfig;
+import com.gargoylesoftware.htmlunit.WebClient;
+import com.gargoylesoftware.htmlunit.WebClientOptions;
+import com.gargoylesoftware.htmlunit.WebRequest;
+import com.gargoylesoftware.htmlunit.WebResponse;
+import com.gargoylesoftware.htmlunit.html.HtmlForm;
+import com.gargoylesoftware.htmlunit.html.HtmlPage;
+import com.gargoylesoftware.htmlunit.html.HtmlPasswordInput;
+import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput;
+import com.gargoylesoftware.htmlunit.html.HtmlTextInput;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpStatus;
+import org.apache.http.conn.params.ConnRoutePNames;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * LinkedIn OAuth request filter to handle OAuth token.
+ */
+@Provider
+@Priority(Priorities.AUTHENTICATION)
+public final class LinkedInOAuthRequestFilter implements ClientRequestFilter {
+
+ public static final String BASE_ADDRESS = "https://api.linkedin.com/v1";
+
+ private static final Logger LOG = LoggerFactory.getLogger(LinkedInOAuthRequestFilter.class);
+
+ private static final String AUTHORIZATION_URL = "https://www.linkedin.com/uas/oauth2/authorization?"
+ + "response_type=code&client_id=%s&state=%s&redirect_uri=%s";
+ private static final String AUTHORIZATION_URL_WITH_SCOPE = "https://www.linkedin.com/uas/oauth2/authorization?"
+ + "response_type=code&client_id=%s&state=%s&scope=%s&redirect_uri=%s";
+
+ private static final String ACCESS_TOKEN_URL = "https://www.linkedin.com/uas/oauth2/accessToken?"
+ + "grant_type=authorization_code&code=%s&redirect_uri=%s&client_id=%s&client_secret=%s";
+
+ private static final Pattern QUERY_PARAM_PATTERN = Pattern.compile("&?([^=]+)=([^&]+)");
+
+ private final WebClient webClient;
+
+ private final OAuthParams oAuthParams;
+
+ private OAuthToken oAuthToken;
+
+ @SuppressWarnings("deprecation")
+ public LinkedInOAuthRequestFilter(OAuthParams oAuthParams, Map<String, Object> httpParams,
+ boolean lazyAuth) {
+
+ this.oAuthParams = oAuthParams;
+ this.oAuthToken = null;
+
+ // create HtmlUnit client
+ webClient = new WebClient(BrowserVersion.FIREFOX_24);
+ final WebClientOptions options = webClient.getOptions();
+ options.setRedirectEnabled(true);
+ options.setJavaScriptEnabled(false);
+ options.setThrowExceptionOnFailingStatusCode(true);
+ options.setThrowExceptionOnScriptError(true);
+ options.setPrintContentOnFailingStatusCode(LOG.isDebugEnabled());
+
+ // add HTTP proxy if set
+ if (httpParams != null && httpParams.get(ConnRoutePNames.DEFAULT_PROXY) != null) {
+ final HttpHost proxyHost = (HttpHost) httpParams.get(ConnRoutePNames.DEFAULT_PROXY);
+ final Boolean socksProxy = (Boolean) httpParams.get("http.route.socks-proxy");
+ final ProxyConfig proxyConfig = new ProxyConfig(proxyHost.getHostName(), proxyHost.getPort(),
+ socksProxy != null ? socksProxy : false);
+ options.setProxyConfig(proxyConfig);
+ }
+
+ if (!lazyAuth) {
+ try {
+ updateOAuthToken();
+ } catch (IOException e) {
+ throw new IllegalArgumentException(
+ String.format("Error authorizing user %s: %s", oAuthParams.getUserName(), e.getMessage()), e);
+ }
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private String getRefreshToken() {
+ // authorize application on user's behalf
+ webClient.getOptions().setRedirectEnabled(true);
+
+ try {
+ final String csrfId = String.valueOf(new SecureRandom().nextLong());
+
+ final String encodedRedirectUri = URLEncoder.encode(oAuthParams.getRedirectUri(), "UTF-8");
+ final OAuthScope[] scopes = oAuthParams.getScopes();
+
+ final String url;
+ if (scopes == null || scopes.length == 0) {
+ url = String.format(AUTHORIZATION_URL, oAuthParams.getClientId(),
+ csrfId, encodedRedirectUri);
+ } else {
+ final int nScopes = scopes.length;
+ final StringBuilder builder = new StringBuilder();
+ int i = 0;
+ for (OAuthScope scope : scopes) {
+ builder.append(scope.getValue());
+ if (++i < nScopes) {
+ builder.append("%20");
+ }
+ }
+ url = String.format(AUTHORIZATION_URL_WITH_SCOPE, oAuthParams.getClientId(), csrfId,
+ builder.toString(), encodedRedirectUri);
+ }
+ final HtmlPage authPage = webClient.getPage(url);
+
+ // submit login credentials
+ final HtmlForm loginForm = authPage.getFormByName("oauth2SAuthorizeForm");
+ final HtmlTextInput login = loginForm.getInputByName("session_key");
+ login.setText(oAuthParams.getUserName());
+ final HtmlPasswordInput password = loginForm.getInputByName("session_password");
+ password.setText(oAuthParams.getUserPassword());
+ final HtmlSubmitInput submitInput = loginForm.getInputByName("authorize");
+
+ // disable redirect to avoid loading redirect URL
+ webClient.getOptions().setRedirectEnabled(false);
+
+ // validate CSRF and get authorization code
+ String redirectQuery;
+ try {
+ final Page redirectPage = submitInput.click();
+ redirectQuery = redirectPage.getUrl().getQuery();
+ } catch (FailingHttpStatusCodeException e) {
+ // escalate non redirect errors
+ if (e.getStatusCode() != HttpStatus.SC_MOVED_TEMPORARILY) {
+ throw e;
+ }
+ final String location = e.getResponse().getResponseHeaderValue("Location");
+ redirectQuery = location.substring(location.indexOf('?') + 1);
+ }
+ final Map<String, String> params = new HashMap<String, String>();
+ final Matcher matcher = QUERY_PARAM_PATTERN.matcher(redirectQuery);
+ while (matcher.find()) {
+ params.put(matcher.group(1), matcher.group(2));
+ }
+ final String state = params.get("state");
+ if (!csrfId.equals(state)) {
+ throw new SecurityException("Invalid CSRF code!");
+ } else {
+ // return authorization code
+ // TODO check results??
+ return params.get("code");
+ }
+
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Error authorizing application: " + e.getMessage(), e);
+ }
+ }
+
+ public void close() {
+ webClient.closeAllWindows();
+ }
+
+ private OAuthToken getAccessToken(String refreshToken) throws IOException {
+ final String tokenUrl = String.format(ACCESS_TOKEN_URL, refreshToken,
+ oAuthParams.getRedirectUri(), oAuthParams.getClientId(), oAuthParams.getClientSecret());
+ final WebRequest webRequest = new WebRequest(new URL(tokenUrl), HttpMethod.POST);
+
+ final WebResponse webResponse = webClient.loadWebResponse(webRequest);
+ if (webResponse.getStatusCode() != HttpStatus.SC_OK) {
+ throw new IOException(String.format("Error getting access token: [%s: %s]",
+ webResponse.getStatusCode(), webResponse.getStatusMessage()));
+ }
+ final long currentTime = System.currentTimeMillis();
+ final ObjectMapper mapper = new ObjectMapper();
+ final Map map = mapper.readValue(webResponse.getContentAsStream(), Map.class);
+ final String accessToken = map.get("access_token").toString();
+ final Integer expiresIn = Integer.valueOf(map.get("expires_in").toString());
+ return new OAuthToken(refreshToken, accessToken,
+ currentTime + TimeUnit.MILLISECONDS.convert(expiresIn, TimeUnit.SECONDS));
+ }
+
+ public synchronized OAuthToken getOAuthToken() {
+ return oAuthToken;
+ }
+
+ @Override
+ public void filter(ClientRequestContext requestContext) throws IOException {
+ updateOAuthToken();
+
+ // add OAuth query param
+ final String requestUri = requestContext.getUri().toString();
+ final StringBuilder builder = new StringBuilder(requestUri);
+ if (requestUri.contains("?")) {
+ builder.append('&');
+ } else {
+ builder.append('?');
+ }
+ builder.append("oauth2_access_token=").append(oAuthToken.getAccessToken());
+ requestContext.setUri(URI.create(builder.toString()));
+ }
+
+ private synchronized void updateOAuthToken() throws IOException {
+
+ // check whether an update is needed
+ final long currentTime = System.currentTimeMillis();
+ if (oAuthToken == null || oAuthToken.getExpiryTime() < currentTime) {
+ LOG.info("OAuth token doesn't exist or has expired");
+
+ // check whether a secure store is provided
+ final OAuthSecureStorage secureStorage = oAuthParams.getSecureStorage();
+ if (secureStorage != null) {
+
+ oAuthToken = secureStorage.getOAuthToken();
+ // if it returned a valid token, we are done, otherwise fall through and generate a new token
+ if (oAuthToken != null && oAuthToken.getExpiryTime() > currentTime) {
+ return;
+ }
+ LOG.info("OAuth secure storage returned a null or expired token, creating a new token...");
+
+ // throw an exception if a user password is not set for authorization
+ if (oAuthParams.getUserPassword() == null || oAuthParams.getUserPassword().isEmpty()) {
+ throw new IllegalArgumentException("Missing password for LinkedIn authorization");
+ }
+ }
+
+ // need new OAuth token, authorize user, LinkedIn does not support OAuth2 grant_type=refresh_token
+ final String refreshToken = getRefreshToken();
+ this.oAuthToken = getAccessToken(refreshToken);
+ LOG.info("OAuth token created!");
+
+ // notify secure storage
+ if (secureStorage != null) {
+ secureStorage.saveOAuthToken(this.oAuthToken);
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b490a90c/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/LongAdapter.java
----------------------------------------------------------------------
diff --git a/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/LongAdapter.java b/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/LongAdapter.java
new file mode 100644
index 0000000..3fe63cc
--- /dev/null
+++ b/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/LongAdapter.java
@@ -0,0 +1,35 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.component.linkedin.api;
+
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+
+public class LongAdapter extends XmlAdapter<String, Long>
+{
+
+ public Long unmarshal(String value) {
+ return new Long(value);
+ }
+
+ public String marshal(Long value) {
+ if (value == null) {
+ return null;
+ }
+ return value.toString();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b490a90c/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/OAuthParams.java
----------------------------------------------------------------------
diff --git a/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/OAuthParams.java b/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/OAuthParams.java
new file mode 100644
index 0000000..0e73a13
--- /dev/null
+++ b/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/OAuthParams.java
@@ -0,0 +1,107 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.component.linkedin.api;
+
+import java.util.Arrays;
+
+/**
+ * Parameters for OAuth 2.0 flow used by {@link LinkedInOAuthRequestFilter}.
+*/
+public class OAuthParams {
+
+ private String userName;
+ private String userPassword;
+
+ private OAuthSecureStorage secureStorage;
+
+ private String clientId;
+ private String clientSecret;
+
+ private OAuthScope[] scopes;
+ private String redirectUri;
+
+ public OAuthParams() {
+ }
+
+ public OAuthParams(String userName, String userPassword, OAuthSecureStorage secureStorage,
+ String clientId, String clientSecret,
+ String redirectUri, OAuthScope... scopes) {
+ this.userName = userName;
+ this.userPassword = userPassword;
+ this.secureStorage = secureStorage;
+ this.clientId = clientId;
+ this.clientSecret = clientSecret;
+ this.scopes = scopes != null ? Arrays.copyOf(scopes, scopes.length) : null;
+ this.redirectUri = redirectUri;
+ }
+
+ public String getUserName() {
+ return userName;
+ }
+
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+
+ public String getUserPassword() {
+ return userPassword;
+ }
+
+ public void setUserPassword(String userPassword) {
+ this.userPassword = userPassword;
+ }
+
+ public String getClientId() {
+ return clientId;
+ }
+
+ public void setClientId(String clientId) {
+ this.clientId = clientId;
+ }
+
+ public OAuthScope[] getScopes() {
+ return scopes;
+ }
+
+ public void setScopes(OAuthScope[] scopes) {
+ this.scopes = scopes;
+ }
+
+ public String getRedirectUri() {
+ return redirectUri;
+ }
+
+ public void setRedirectUri(String redirectUri) {
+ this.redirectUri = redirectUri;
+ }
+
+ public String getClientSecret() {
+ return clientSecret;
+ }
+
+ public void setClientSecret(String clientSecret) {
+ this.clientSecret = clientSecret;
+ }
+
+ public OAuthSecureStorage getSecureStorage() {
+ return secureStorage;
+ }
+
+ public void setSecureStorage(OAuthSecureStorage secureStorage) {
+ this.secureStorage = secureStorage;
+ }
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b490a90c/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/OAuthScope.java
----------------------------------------------------------------------
diff --git a/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/OAuthScope.java b/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/OAuthScope.java
new file mode 100644
index 0000000..aadf353
--- /dev/null
+++ b/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/OAuthScope.java
@@ -0,0 +1,64 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.component.linkedin.api;
+
+/**
+ * OAuth scope for use in {@link LinkedInOAuthRequestFilter}
+ */
+public enum OAuthScope {
+
+ R_BASICPROFILE("r_basicprofile"),
+ R_FULLPROFILE("r_fullprofile"),
+ R_EMAILADDRESS("r_emailaddress"),
+ R_NETWORK("r_network"),
+ R_CONTACTINFO("r_contactinfo"),
+ RW_NUS("rw_nus"),
+ RW_COMPANY_ADMIN("rw_company_admin"),
+ RW_GROUPS("rw_groups"),
+ W_MESSAGES("w_messages");
+
+ private final String value;
+
+ private OAuthScope(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public static OAuthScope fromValue(String value) {
+ for (OAuthScope scope : values()) {
+ if (scope.value.equals(value)) {
+ return scope;
+ }
+ }
+ throw new IllegalArgumentException(value);
+ }
+
+ public static OAuthScope[] fromValues(String... values) {
+ if (values == null || values.length == 0) {
+ return new OAuthScope[0];
+ }
+ final OAuthScope[] result = new OAuthScope[values.length];
+ int i = 0;
+ for (String value : values) {
+ result[i++] = fromValue(value);
+ }
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b490a90c/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/OAuthSecureStorage.java
----------------------------------------------------------------------
diff --git a/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/OAuthSecureStorage.java b/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/OAuthSecureStorage.java
new file mode 100644
index 0000000..cf5542e
--- /dev/null
+++ b/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/OAuthSecureStorage.java
@@ -0,0 +1,36 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.component.linkedin.api;
+
+/**
+ * Secure token storage for {@link OAuthToken}
+ */
+public interface OAuthSecureStorage {
+
+ /**
+ * Get token from secure storage.
+ * @return null if a secure token doesn't exist and {@link LinkedInOAuthRequestFilter} must create one.
+ */
+ OAuthToken getOAuthToken();
+
+ /**
+ * Save token to secure storage.
+ * Only called when {@link LinkedInOAuthRequestFilter} creates one.
+ * @param newToken
+ */
+ void saveOAuthToken(OAuthToken newToken);
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b490a90c/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/OAuthToken.java
----------------------------------------------------------------------
diff --git a/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/OAuthToken.java b/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/OAuthToken.java
new file mode 100644
index 0000000..cf04380
--- /dev/null
+++ b/components/camel-linkedin/camel-linkedin-api/src/main/java/org/apache/camel/component/linkedin/api/OAuthToken.java
@@ -0,0 +1,50 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.component.linkedin.api;
+
+/**
+* LinkedIn OAuth Token
+*/
+public final class OAuthToken {
+
+ private final String refreshToken;
+ private final String accessToken;
+ private long expiryTime;
+
+ public OAuthToken(String refreshToken, String accessToken, long expiryTime) {
+ this.refreshToken = refreshToken;
+ this.accessToken = accessToken;
+ this.expiryTime = expiryTime;
+ }
+
+ public String getRefreshToken() {
+ return refreshToken;
+ }
+
+ public String getAccessToken() {
+ return accessToken;
+ }
+
+ public long getExpiryTime() {
+ return expiryTime;
+ }
+
+ // package method for testing only
+ void setExpiryTime(long expiryTime) {
+ this.expiryTime = expiryTime;
+ }
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b490a90c/components/camel-linkedin/camel-linkedin-api/src/main/resources/linkedin-api-schema.xjb
----------------------------------------------------------------------
diff --git a/components/camel-linkedin/camel-linkedin-api/src/main/resources/linkedin-api-schema.xjb b/components/camel-linkedin/camel-linkedin-api/src/main/resources/linkedin-api-schema.xjb
new file mode 100644
index 0000000..778a4f5
--- /dev/null
+++ b/components/camel-linkedin/camel-linkedin-api/src/main/resources/linkedin-api-schema.xjb
@@ -0,0 +1,447 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+ -->
+<bindings version="2.0" xmlns="http://java.sun.com/xml/ns/jaxb"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
+ extensionBindingPrefixes="xjc"
+ schemaLocation="linkedin-api-schema.xsd" node="/xs:schema">
+
+ <!-- Copied with permission from the linkedin-j library https://code.google.com/p/linkedin-j/ -->
+ <globalBindings localScoping="toplevel" generateValueClass="true">
+ <xjc:javaType name="Double" xmlType="xs:double"
+ adapter="org.apache.camel.component.linkedin.api.DoubleAdapter"/>
+ <xjc:javaType name="Long" xmlType="xs:integer"
+ adapter="org.apache.camel.component.linkedin.api.LongAdapter"/>
+ <!-- <xjc:superInterface name="SchemaEntity"/> -->
+ <!-- <xjc:superClass name="BaseSchemaEntity"/> -->
+ <!-- <xjc:serializable uid="1L"/> -->
+ <!-- <xjc:serializable uid="6877416375268387499L"/> -->
+ </globalBindings>
+
+ <schemaBindings>
+ <package name="org.apache.camel.component.linkedin.api.model" />
+ </schemaBindings>
+
+ <bindings node="//xs:element[@name='content-type']/xs:simpleType">
+ <typesafeEnumClass name="NetworkUpdateContentType">
+ <typesafeEnumMember name="LINKED_IN_HTML" value="linkedin-html"/>
+ </typesafeEnumClass>
+ </bindings>
+ <bindings node="//xs:element[@name='connect-type']/xs:simpleType">
+ <typesafeEnumClass name="InviteConnectType" >
+ <typesafeEnumMember name="FRIEND" value="friend"/>
+ </typesafeEnumClass>
+ </bindings>
+ <bindings node="//xs:element[@name='update-type']/xs:simpleType">
+ <typesafeEnumClass name="NetworkUpdateReturnType">
+ <typesafeEnumMember name="ANSWER_UPDATE" value="ANSW"/>
+ <typesafeEnumMember name="APPLICATION_CONNECTION_UPDATE" value="APPM"/>
+ <typesafeEnumMember name="APPLICATION_TO_MEMBER_UPDATE" value="APPS"/>
+ <typesafeEnumMember name="CONNECTION_ADDED_CONNECTIONS" value="CONN"/>
+ <typesafeEnumMember name="NEW_CONNECTIONS" value="NCON"/>
+ <typesafeEnumMember name="CONTACT_JOINED" value="CCEM"/>
+ <typesafeEnumMember name="JOB_POSTED" value="JOBP"/>
+ <typesafeEnumMember name="CONNECTION_JOINED_GROUP" value="JGRP"/>
+ <typesafeEnumMember name="CONNECTION_UPDATED_PICTURE" value="PICU"/>
+ <typesafeEnumMember name="CONNECTION_RECOMMENDED" value="PREC"/>
+ <typesafeEnumMember name="CONNECTION_UPDATED_PROFILE" value="PROF"/>
+ <typesafeEnumMember name="QUESTION_UPDATED" value="QSTN"/>
+ <typesafeEnumMember name="STATUS_UPDATED" value="STAT"/>
+ <typesafeEnumMember name="SHARED_ITEM" value="SHAR"/>
+ <typesafeEnumMember name="EXTENDED_PROFILE_UPDATED" value="PRFX"/>
+ <typesafeEnumMember name="COMPANY_UPDATED" value="CMPY"/>
+ <typesafeEnumMember name="VIRAL_UPDATE" value="VIRL"/>
+ </typesafeEnumClass>
+ </bindings>
+ <bindings node="//xs:element[@name='recommendation-type']/xs:complexType/xs:sequence/xs:element[@name='code']/xs:simpleType">
+ <typesafeEnumClass name="RecommendationCode">
+ <typesafeEnumMember name="COLLEAGUE" value="colleague"/>
+ <typesafeEnumMember name="BUSINESS_PARTNER" value="business-partner"/>
+ <typesafeEnumMember name="SERVICE_PROVIDER" value="service-provider"/>
+ <typesafeEnumMember name="EDUCATION" value="education"/>
+ </typesafeEnumClass>
+ </bindings>
+ <bindings node="//xs:element[@name='im-account-type']/xs:simpleType">
+ <typesafeEnumClass name="ImAccountType">
+ <typesafeEnumMember name="AIM" value="aim"/>
+ <typesafeEnumMember name="GTALK" value="gtalk"/>
+ <typesafeEnumMember name="ICQ" value="icq"/>
+ <typesafeEnumMember name="MSN" value="msn"/>
+ <typesafeEnumMember name="SKYPE" value="skype"/>
+ <typesafeEnumMember name="YAHOO" value="yahoo"/>
+ </typesafeEnumClass>
+ </bindings>
+ <bindings node="//xs:element[@name='phone-type']/xs:simpleType">
+ <typesafeEnumClass name="PhoneType">
+ <typesafeEnumMember name="HOME" value="home"/>
+ <typesafeEnumMember name="WORK" value="work"/>
+ <typesafeEnumMember name="MOBILE" value="mobile"/>
+ </typesafeEnumClass>
+ </bindings>
+ <bindings node="//xs:element[@name='level']/xs:simpleType">
+ <typesafeEnumClass name="ProficiencyLevelType">
+ <typesafeEnumMember name="ELEMENTARY" value="elementary"/>
+ <typesafeEnumMember name="LIMITED_WORKING" value="limited_working"/>
+ <typesafeEnumMember name="PROFESSIONAL_WORKING" value="professional_working"/>
+ <typesafeEnumMember name="FULL_PROFESSIONAL" value="full_professional"/>
+ <typesafeEnumMember name="NATIVE_BILINGUAL" value="native_or_bilingual"/>
+ <typesafeEnumMember name="BEGINNER" value="beginner"/>
+ <typesafeEnumMember name="INTERMEDIATE" value="intermediate"/>
+ <typesafeEnumMember name="ADVANCED" value="advanced"/>
+ <typesafeEnumMember name="EXPERT" value="expert"/>
+ </typesafeEnumClass>
+ </bindings>
+ <bindings node="//xs:element[@name='facet']/xs:complexType/xs:sequence/xs:element[@name='code']/xs:simpleType">
+ <typesafeEnumClass name="FacetType">
+ <typesafeEnumMember name="LOCATION" value="location"/>
+ <typesafeEnumMember name="INDUSTRY" value="industry"/>
+ <typesafeEnumMember name="NETWORK" value="network"/>
+ <typesafeEnumMember name="LANGUAGE" value="language"/>
+ <typesafeEnumMember name="CURRENT_COMPANY" value="current-company"/>
+ <typesafeEnumMember name="PAST_COMPANY" value="past-company"/>
+ <typesafeEnumMember name="SCHOOL" value="school"/>
+ <typesafeEnumMember name="COMPANY_SIZE" value="company-size"/>
+ <typesafeEnumMember name="NUM_FOLLOWERS_RANGE" value="num-followers-range"/>
+ <typesafeEnumMember name="FORTUNE" value="fortune"/>
+ <typesafeEnumMember name="COMPANY" value="company"/>
+ <typesafeEnumMember name="DATE_POSTED" value="date-posted"/>
+ <typesafeEnumMember name="JOB_FUNCTION" value="job-function"/>
+ <typesafeEnumMember name="EXPERIENCE_LEVEL" value="experience-level"/>
+ <typesafeEnumMember name="SALARY" value="salary"/>
+ </typesafeEnumClass>
+ </bindings>
+ <bindings node="//xs:element[@name='visibility']/xs:complexType/xs:sequence/xs:element[@name='code']/xs:simpleType">
+ <typesafeEnumClass name="VisibilityType">
+ <typesafeEnumMember name="ANYONE" value="anyone"/>
+ <typesafeEnumMember name="ALL_MEMBERS" value="all-members"/>
+ <typesafeEnumMember name="CONNECTIONS_ONLY" value="connections-only"/>
+ </typesafeEnumClass>
+ </bindings>
+ <bindings node="//xs:element[@name='role']/xs:complexType/xs:sequence/xs:element[@name='code']/xs:simpleType">
+ <typesafeEnumClass name="RoleCode">
+ <typesafeEnumMember name="HIRING_MANAGER" value="H"/>
+ <typesafeEnumMember name="COMPANY_RECRUITER" value="R"/>
+ <typesafeEnumMember name="STAFFING_FIRM" value="S"/>
+ <typesafeEnumMember name="COMPANY_EMPLOYEE" value="W"/>
+ </typesafeEnumClass>
+ </bindings>
+ <bindings node="//xs:element[@name='profile-field']/xs:complexType/xs:sequence/xs:element[@name='code']/xs:simpleType">
+ <typesafeEnumClass name="ProfileFieldCode">
+ <typesafeEnumMember name="DESCRIPTION" value="description"/>
+ <typesafeEnumMember name="SPECIALITY" value="speciality"/>
+ <typesafeEnumMember name="LOGO" value="logo"/>
+ </typesafeEnumClass>
+ </bindings>
+ <bindings node="//xs:element[@name='job-type']/xs:complexType/xs:sequence/xs:element[@name='code']/xs:simpleType">
+ <typesafeEnumClass name="JobTypeCode">
+ <typesafeEnumMember name="FULL_TIME" value="F"/>
+ <typesafeEnumMember name="PART_TIME" value="P"/>
+ <typesafeEnumMember name="CONTRACT" value="C"/>
+ <typesafeEnumMember name="TEMPORARY" value="T"/>
+ <typesafeEnumMember name="OTHER" value="O"/>
+ </typesafeEnumClass>
+ </bindings>
+ <bindings node="//xs:element[@name='experience-level']/xs:complexType/xs:sequence/xs:element[@name='code']/xs:simpleType">
+ <typesafeEnumClass name="ExperienceLevelCode">
+ <typesafeEnumMember name="NOT_APPLICABLE" value="0"/>
+ <typesafeEnumMember name="INTERNSHIP" value="1"/>
+ <typesafeEnumMember name="ENTRY_LEVEL" value="2"/>
+ <typesafeEnumMember name="ASSOCIATE" value="3"/>
+ <typesafeEnumMember name="MID_SENIOR_LEVEL" value="4"/>
+ <typesafeEnumMember name="DIRECTOR" value="5"/>
+ <typesafeEnumMember name="EXECUTIVE" value="6"/>
+ </typesafeEnumClass>
+ </bindings>
+ <bindings node="//xs:complexType[@name='company-status']/xs:sequence/xs:element[@name='code']/xs:simpleType">
+ <typesafeEnumClass name="CompanyStatusCode">
+ <typesafeEnumMember name="OPERATING" value="OPR"/>
+ <typesafeEnumMember name="OPERATING_SUBSIDIARY" value="OPS"/>
+ <typesafeEnumMember name="REORGANIZING" value="RRG"/>
+ <typesafeEnumMember name="OUT_OF_BUSINESS" value="OOB"/>
+ <typesafeEnumMember name="ACQUIRED" value="ACQ"/>
+ </typesafeEnumClass>
+ </bindings>
+ <bindings node="//xs:element[@name='company-type']/xs:complexType/xs:sequence/xs:element[@name='code']/xs:simpleType">
+ <typesafeEnumClass name="CompanyTypeCode">
+ <typesafeEnumMember name="PUBLIC_COMPANY" value="C"/>
+ <typesafeEnumMember name="EDUCATIONAL" value="D"/>
+ <typesafeEnumMember name="SELF_EMPLOYED" value="E"/>
+ <typesafeEnumMember name="GOVT_AGENCY" value="G"/>
+ <typesafeEnumMember name="NON_PROFIT" value="N"/>
+ <typesafeEnumMember name="SELF_OWNED" value="O"/>
+ <typesafeEnumMember name="PRIVATELY_HELD" value="P"/>
+ <typesafeEnumMember name="PARTNERSHIP" value="S"/>
+ </typesafeEnumClass>
+ </bindings>
+ <bindings node="//xs:element[@name='stock-exchange']/xs:complexType/xs:sequence/xs:element[@name='code']/xs:simpleType">
+ <typesafeEnumClass name="StockExchangeCode">
+ <typesafeEnumMember name="AMERICAN_STOCK_EXCHANGE" value="ASE"/>
+ <typesafeEnumMember name="NEWYORK_STOCK_EXCHANGE" value="NYS"/>
+ <typesafeEnumMember name="NASDAQ" value="NMS"/>
+ <typesafeEnumMember name="LONDON_STOCK_EXCHANGE" value="LSE"/>
+ <typesafeEnumMember name="FRANKFURT_STOCK_EXCHANGE" value="FRA"/>
+ <typesafeEnumMember name="XETRA_TRADING_PLATFORM" value="GER"/>
+ <typesafeEnumMember name="EURONEXT_PARIS" value="PAR"/>
+ </typesafeEnumClass>
+ </bindings>
+ <bindings node="//xs:element[@name='job-function']/xs:complexType/xs:sequence/xs:element[@name='code']/xs:simpleType">
+ <typesafeEnumClass name="JobFunctionCode">
+ <typesafeEnumMember name="ACCOUNTING_AUDITING" value="acct"/>
+ <typesafeEnumMember name="ADMINISTRATIVE" value="adm"/>
+ <typesafeEnumMember name="ADVERTISING" value="advr"/>
+ <typesafeEnumMember name="ANALYST" value="anls"/>
+ <typesafeEnumMember name="ART_CREATIVE" value="art"/>
+ <typesafeEnumMember name="BUSINESS_DEVELOPMENT" value="bd"/>
+ <typesafeEnumMember name="CONSULTING" value="cnsl"/>
+ <typesafeEnumMember name="CUSTOMER_SERVICE" value="cust"/>
+ <typesafeEnumMember name="DISTRIBUTION" value="dist"/>
+ <typesafeEnumMember name="DESIGN" value="dsgn"/>
+ <typesafeEnumMember name="EDUCATION" value="edu"/>
+ <typesafeEnumMember name="ENGINEERING" value="eng"/>
+ <typesafeEnumMember name="FINANCE" value="fin"/>
+ <typesafeEnumMember name="GENERAL_BUSINESS" value="genb"/>
+ <typesafeEnumMember name="HUMAN_RESOURCES" value="hr"/>
+ <typesafeEnumMember name="INFORMATION_TECHNOLOGY" value="it"/>
+ <typesafeEnumMember name="LEGAL" value="lgl"/>
+ <typesafeEnumMember name="MANAGEMENT" value="mgmt"/>
+ <typesafeEnumMember name="MANUFACTURING" value="mnfc"/>
+ <typesafeEnumMember name="MARKETING" value="mrkt"/>
+ <typesafeEnumMember name="OTHER" value="othr"/>
+ <typesafeEnumMember name="PUBLIC_RELATIONS" value="pr"/>
+ <typesafeEnumMember name="PURCHASING" value="prch"/>
+ <typesafeEnumMember name="PRODUCT_MANAGEMENT" value="prdm"/>
+ <typesafeEnumMember name="PROJECT_MANAGEMENT" value="prjm"/>
+ <typesafeEnumMember name="PRODUCTION" value="prod"/>
+ <typesafeEnumMember name="QUALITY_ASSURANCE" value="qa"/>
+ <typesafeEnumMember name="RESEARCH" value="rsch"/>
+ <typesafeEnumMember name="SALES" value="sale"/>
+ <typesafeEnumMember name="SCIENCE" value="sci"/>
+ <typesafeEnumMember name="STRATEGY_PLANNING" value="stra"/>
+ <typesafeEnumMember name="SUPPLY_CHAIN" value="supl"/>
+ <typesafeEnumMember name="TRAINING" value="trng"/>
+ <typesafeEnumMember name="WRITING_EDITING" value="wrt"/>
+ </typesafeEnumClass>
+ </bindings>
+ <bindings node="//xs:element[@name='membership-state']/xs:complexType/xs:sequence/xs:element[@name='code']/xs:simpleType">
+ <typesafeEnumClass name="MembershipStateCode">
+ <typesafeEnumMember name="BLOCKED" value="blocked"/>
+ <typesafeEnumMember name="NON_MEMBER" value="non-member"/>
+ <typesafeEnumMember name="AWAITING_CONFIRMATION" value="awaiting-confirmation"/>
+ <typesafeEnumMember name="AWAITING_PARENT_GROUP_CONFIRMATION" value="awaiting-parent-group-confirmation"/>
+ <typesafeEnumMember name="MEMBER" value="member"/>
+ <typesafeEnumMember name="MODERATOR" value="moderator"/>
+ <typesafeEnumMember name="MANAGER" value="manager"/>
+ <typesafeEnumMember name="OWNER" value="owner"/>
+ </typesafeEnumClass>
+ </bindings>
+ <bindings node="//xs:element[@name='email-digest-frequency']/xs:complexType/xs:sequence/xs:element[@name='code']/xs:simpleType">
+ <typesafeEnumClass name="EmailDigestFrequencyCode">
+ <typesafeEnumMember name="NONE" value="none"/>
+ <typesafeEnumMember name="DAILY" value="daily"/>
+ <typesafeEnumMember name="WEEKLY" value="weekly"/>
+ </typesafeEnumClass>
+ </bindings>
+ <bindings node="//xs:element[@name='category']/xs:complexType/xs:sequence/xs:element[@name='code']/xs:simpleType">
+ <typesafeEnumClass name="PostCategoryCode">
+ <typesafeEnumMember name="DISCUSSION" value="discussion"/>
+ <typesafeEnumMember name="JOB" value="job"/>
+ <typesafeEnumMember name="PROMOTION" value="promotion"/>
+ <typesafeEnumMember name="LINKEDIN_JOB" value="linkedin-job"/>
+ </typesafeEnumClass>
+ </bindings>
+ <bindings node="//xs:complexType[@name='GroupCategory']/xs:sequence/xs:element[@name='code']/xs:simpleType">
+ <typesafeEnumClass name="GroupCategoryCode">
+ <typesafeEnumMember name="ALUMNI" value="alumni"/>
+ <typesafeEnumMember name="CORPORATE" value="corporate"/>
+ <typesafeEnumMember name="CONFERENCE" value="conference"/>
+ <typesafeEnumMember name="NETWORK" value="network"/>
+ <typesafeEnumMember name="PHILANTHROPIC" value="philanthropic"/>
+ <typesafeEnumMember name="PROFESSIONAL" value="professional"/>
+ <typesafeEnumMember name="OTHER" value="other"/>
+ </typesafeEnumClass>
+ </bindings>
+ <bindings node="//xs:element[@name='post']/xs:complexType/xs:sequence/xs:element[@name='type']/xs:complexType/xs:sequence/xs:element[@name='code']/xs:simpleType">
+ <typesafeEnumClass name="PostTypeCode">
+ <typesafeEnumMember name="STANDARD" value="standard"/>
+ <typesafeEnumMember name="NEWS" value="news"/>
+ </typesafeEnumClass>
+ </bindings>
+
+ <bindings node="//xs:element[@name='updates']/xs:complexType/xs:sequence/xs:element[@ref='update']">
+ <property name="updateList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='recipients']/xs:complexType/xs:sequence/xs:element[@ref='recipient']">
+ <property name="recipientList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='network-stats']/xs:complexType/xs:sequence/xs:element[@ref='property']">
+ <property name="propertyList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='question-categories']/xs:complexType/xs:sequence/xs:element[@ref='question-category']">
+ <property name="questionCategoryList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='answers']/xs:complexType/xs:sequence/xs:element[@ref='answer']">
+ <property name="answerList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='update-comments']/xs:complexType/xs:sequence/xs:element[@ref='update-comment']">
+ <property name="updateCommentList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='people']/xs:complexType/xs:sequence/xs:element[@ref='person']">
+ <property name="personList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='positions']/xs:complexType/xs:sequence/xs:element[@ref='position']">
+ <property name="positionList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='three-current-positions']/xs:complexType/xs:sequence/xs:element[@ref='position']">
+ <property name="positionList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='three-past-positions']/xs:complexType/xs:sequence/xs:element[@ref='position']">
+ <property name="positionList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='educations']/xs:complexType/xs:sequence/xs:element[@ref='education']">
+ <property name="educationList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='member-groups']/xs:complexType/xs:sequence/xs:element[@ref='member-group']">
+ <property name="memberGroupList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='person-activities']/xs:complexType/xs:sequence/xs:element[@ref='activity']">
+ <property name="activityList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='recommendations-given']/xs:complexType/xs:sequence/xs:element[@ref='recommendation']">
+ <property name="recommendationList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='recommendations-received']/xs:complexType/xs:sequence/xs:element[@ref='recommendation']">
+ <property name="recommendationList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='connections']/xs:complexType/xs:sequence/xs:element[@ref='person']">
+ <property name="personList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='headers']/xs:complexType/xs:sequence/xs:element[@ref='http-header']">
+ <property name="httpHeaderList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='im-accounts']/xs:complexType/xs:sequence/xs:element[@ref='im-account']">
+ <property name="imAccountList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='twitter-accounts']/xs:complexType/xs:sequence/xs:element[@ref='twitter-account']">
+ <property name="twitterAccountList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='phone-numbers']/xs:complexType/xs:sequence/xs:element[@ref='phone-number']">
+ <property name="phoneNumberList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='member-url-resources']/xs:complexType/xs:sequence/xs:element[@ref='member-url']">
+ <property name="memberUrlList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='facets']/xs:complexType/xs:sequence/xs:element[@ref='facet']">
+ <property name="facetList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='buckets']/xs:complexType/xs:sequence/xs:element[@ref='bucket']">
+ <property name="bucketList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='likes']/xs:complexType/xs:sequence/xs:element[@ref='like']">
+ <property name="likeList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='certifications']/xs:complexType/xs:sequence/xs:element[@ref='certification']">
+ <property name="certificationList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='patents']/xs:complexType/xs:sequence/xs:element[@ref='patent']">
+ <property name="patentList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='publications']/xs:complexType/xs:sequence/xs:element[@ref='publication']">
+ <property name="publicationList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='skills']/xs:complexType/xs:sequence/xs:element[@ref='skill']">
+ <property name="skillList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='languages']/xs:complexType/xs:sequence/xs:element[@ref='language']">
+ <property name="languageList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='inventors']/xs:complexType/xs:sequence/xs:element[@ref='inventor']">
+ <property name="inventorList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='authors']/xs:complexType/xs:sequence/xs:element[@name='author']">
+ <property name="authorList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='related-connections']/xs:complexType/xs:sequence/xs:element[@ref='person']">
+ <property name="personList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='companies']/xs:complexType/xs:sequence/xs:element[@ref='company']">
+ <property name="companyList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='products']/xs:complexType/xs:sequence/xs:element[@ref='product']">
+ <property name="productList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='job-bookmarks']/xs:complexType/xs:sequence/xs:element[@ref='job-bookmark']">
+ <property name="jobBookmarkList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='jobs']/xs:complexType/xs:sequence/xs:element[@ref='job']">
+ <property name="jobList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='email-domains']/xs:complexType/xs:sequence/xs:element[@ref='email-domain']">
+ <property name="emailDomainList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='locations']/xs:complexType/xs:sequence/xs:element[@ref='location']">
+ <property name="locationList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='recommendations']/xs:complexType/xs:sequence/xs:element[@ref='recommendation']">
+ <property name="recommendationList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='job-functions']/xs:complexType/xs:sequence/xs:element[@ref='job-function']">
+ <property name="jobFunctionList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='industries']/xs:complexType/xs:sequence/xs:element[@ref='industry']">
+ <property name="industryList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='specialties']/xs:complexType/xs:sequence/xs:element[@ref='specialty']">
+ <property name="specialtyList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='features']/xs:complexType/xs:sequence/xs:element[@ref='feature']">
+ <property name="featureList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='sales-persons']/xs:complexType/xs:sequence/xs:element[@ref='person']">
+ <property name="personList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='posts']/xs:complexType/xs:sequence/xs:element[@ref='post']">
+ <property name="postList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='comments']/xs:complexType/xs:sequence/xs:element[@ref='comment']">
+ <property name="commentList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='available-actions']/xs:complexType/xs:sequence/xs:element[@ref='action']">
+ <property name="actionList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='group-memberships']/xs:complexType/xs:sequence/xs:element[@ref='group-membership']">
+ <property name="groupMembershipList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='groups']/xs:complexType/xs:sequence/xs:element[@ref='group']">
+ <property name="groupList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='historical-follow-statistics']/xs:complexType/xs:sequence/xs:element[@name='statistic']">
+ <property name="statisticList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='historical-status-update-statistics']/xs:complexType/xs:sequence/xs:element[@name='statistic']">
+ <property name="statisticList"/>
+ </bindings>
+ <bindings node="//xs:element[@name='historical-follow-statistics']/xs:complexType/xs:sequence/xs:element[@name='statistic']/xs:complexType">
+ <class name="HistoricalFollowStatistic"/>
+ </bindings>
+ <bindings node="//xs:element[@name='historical-status-update-statistics']/xs:complexType/xs:sequence/xs:element[@name='statistic']/xs:complexType">
+ <class name="HistoricalStatusUpdateStatistic"/>
+ </bindings>
+ <bindings node="//xs:element[@name='share-target-reach']/xs:complexType/xs:sequence/xs:element[@name='share-targets']/xs:complexType/xs:sequence/xs:element[@name='share-target']">
+ <property name="shareTargetList"/>
+ </bindings>
+</bindings>
\ No newline at end of file