/* Copyright 2008 Google Inc.
*
* 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 com.google.feedserver.client;
import com.google.gdata.client.GoogleService;
import com.google.gdata.util.ServiceException;
import com.google.inject.Inject;
import com.google.feedserver.util.FeedServerClientException;
import org.apache.commons.beanutils.BeanMap;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
/**
* Implements a parameterized Gdata feed client for FeedServer "payload-in-content" feeds employing
* java beans to represent the data. Create a java bean that represents the entity data and
* use it as the parameterized type for the feed client.
*
* @author r@kuci.org (Ray Colline)
*/
public class FeedServerClient<T> {
private static final String ID_ELEMENT = "id";
private static final String NAME_ELEMENT = "name";
// Logging instance
private static final Logger LOG = Logger.getLogger(FeedServerClient.class.getName());
// Dependencies
private GoogleService service;
private Class<T> entityClass; // Java bean
/**
* Creates client using supplied service and entityClass
*
* @param service the configured Gdata service.
*/
@Inject
public FeedServerClient(GoogleService service, Class<T> entityClass) {
this.service = service;
this.entityClass = entityClass;
}
/**
* Fetches generic "payload-in-content" entry into a {@link FeedServerEntry}. The
* FeedServerEntry allows you to return the content of the entry as a java bean.
*
* @param entryUrl the entry URL which can contain any valid ATOM "query"
* @return the populated entry.
* @throws FeedServerClientException if we cannot contact the feedserver, fetch the URL, or
* parse the XML.
* @throws RuntimeException if the bean is not constructed properly and is missing fields.
*/
public FeedServerEntry getEntry(URL entryUrl) throws FeedServerClientException {
try {
return service.getEntry(entryUrl, FeedServerEntry.class);
} catch (IOException e) {
throw new FeedServerClientException("Error while fetching " + entryUrl, e);
} catch (ServiceException e) {
throw new FeedServerClientException(e);
}
}
/**
* Fetches generic "payload-in-content" entity into a predefined java bean. This bean should
* have fields necessary to receive all the elements of the feed. Using a bean, requires you
* know the schema of your feed ahead of time, but gives you the convenience of having first
* class object access.
*
* @param feedUrl the feed URL which can contain any valid ATOM "query"
* @return the populated bean.
* @throws FeedServerClientException if we cannot contact the feedserver, fetch the URL, or
* parse the XML.
* @throws RuntimeException if the bean is not constructed properly and is missing fields.
*/
public T getEntity(URL feedUrl) throws FeedServerClientException {
return getEntry(feedUrl).getEntity(entityClass);
}
/**
* Fetches generic "payload-in-content" entry into a list of {@link FeedServerEntry}. The
* FeedServerEntry allows you to return the content of the entry as a java bean.
*
* @param feedUrl the feed URL which can contain any valid ATOM "query"
* @return the list of populated entries.
* @throws FeedServerClientException if we cannot contact the feedserver, fetch the URL, or
* parse the XML.
* @throws RuntimeException if the bean is not constructed properly and is missing fields.
*/
public List<FeedServerEntry> getEntries(URL feedUrl) throws FeedServerClientException {
try {
FeedServerFeed feed = service.getFeed(feedUrl, FeedServerFeed.class);
return feed.getEntries();
} catch (IOException e) {
throw new FeedServerClientException("Error while fetching " + feedUrl, e);
} catch (ServiceException e) {
throw new FeedServerClientException(e);
} catch (IllegalArgumentException e) {
throw new RuntimeException("Invalid bean " + entityClass.getName(), e);
}
}
/**
* Fetches generic "payload-in-content" feed .
*
* @param feedUrl the feed URL which can contain any valid ATOM "query"
* @return the populated feed.
* @throws FeedServerClientException if we cannot contact the feedserver, fetch the URL, or
* parse the XML.
* @throws RuntimeException if the bean is not constructed properly and is missing fields.
*/
public FeedServerFeed getFeed(URL feedUrl) throws FeedServerClientException {
try {
return service.getFeed(feedUrl, FeedServerFeed.class);
} catch (IOException e) {
throw new FeedServerClientException("Error while fetching " + feedUrl, e);
} catch (ServiceException e) {
throw new FeedServerClientException(e);
} catch (IllegalArgumentException e) {
throw new RuntimeException("Invalid bean " + entityClass.getName(), e);
}
}
/**
* Fetches generic "payload-in-content" entity into a list of predefined java bean. This
* bean should have fields necessary to receive all the elements of the feed. Using a bean,
* requires you know the schema of your feed ahead of time, but gives you the convenience of
* having first class object access.
*
* @param feedUrl the feed URL which can contain any valid ATOM "query"
* @return a list of populated beans representing an entry's entity.
* @throws FeedServerClientException if we cannot contact the feedserver, fetch the URL, or
* parse the XML.
* @throws RuntimeException if the bean is not constructed properly and is missing fields.
*/
public List<T> getEntities(URL feedUrl) throws FeedServerClientException {
List<FeedServerEntry> entries = getEntries(feedUrl);
List<T> entities = new ArrayList<T>();
for (FeedServerEntry entry : entries) {
entities.add(entry.getEntity(entityClass));
}
return entities;
}
/**
* Deletes entry specified by supplied URL. This URL must include the ID.
*
* @param entryUrl the full URL to the entry in this feed.
* @throws FeedServerClientException if any communication issues occur with the feed or the
* feed ID is invalid or malformed..
*/
public void deleteEntry(URL entryUrl) throws FeedServerClientException {
try {
service.delete(entryUrl);
} catch (IOException e) {
throw new FeedServerClientException("Error while deleting " + entryUrl, e);
} catch (ServiceException e) {
throw new FeedServerClientException(e);
}
}
/**
* Deletes specified entry using "name" property contained in the entry's entity.
*
* @param baseUrl Feed url not including ID
* @param entry valid entry containing an entity bean.
* @throws FeedServerClientException if any communication issues occur with the feed or the
* feed ID is invalid or malformed.
*/
public void deleteEntry(URL baseUrl, FeedServerEntry entry) throws FeedServerClientException {
Object id = getBeanProperty(NAME_ELEMENT, entry.getEntity(entityClass));
if (id == null) {
id = getBeanProperty(ID_ELEMENT, entry.getEntity(entityClass));
}
if (id == null) {
throw new FeedServerClientException("name or id is required in the entry to update");
}
try {
URL feedUrl = new URL(baseUrl.toString() + "/" + id);
LOG.info("deleting entry at feed " + feedUrl);
deleteEntry(feedUrl);
} catch (MalformedURLException e) {
throw new FeedServerClientException("invalid base URL", e);
}
}
/**
* Deletes specified entry using "name" property contained in the supplied entity bean.
*
* @param baseUrl Feed url not including ID
* @param entity valid entry bean.
* @throws FeedServerClientException if any communication issues occur with the feed or the
* feed ID is invalid or malformed.
*/
public void deleteEntity(URL baseUrl, T entity) throws FeedServerClientException {
Object id = getBeanProperty(NAME_ELEMENT, entity);
if (id == null) {
id = getBeanProperty(ID_ELEMENT, entity);
}
if (id == null) {
throw new FeedServerClientException("name or id is required in the entry to update");
}
try {
URL feedUrl = new URL(baseUrl.toString() + "/" + id);
LOG.info("deleting entry at feed " + feedUrl);
deleteEntry(feedUrl);
} catch (MalformedURLException e) {
throw new FeedServerClientException("invalid base URL", e);
}
}
/**
* Deletes specified entries using "name" property contained in the entry's entity. This
* makes one request per entry.
*
* @param baseUrl Feed url not including ID
* @param entries list of valid populated entries.
* @throws FeedServerClientException if any communication issues occur with the feed or the
* feed ID is invalid or malformed.
*/
public void deleteEntries(URL baseUrl, List<FeedServerEntry> entries)
throws FeedServerClientException {
for (FeedServerEntry entry : entries) {
deleteEntry(baseUrl, entry);
}
}
/**
* Deletes specified entries using "name" property contained in the entity bean. This makes one
* request per entry.
*
* @param baseUrl Feed url not including ID
* @param entities list of valid entity beans.
* @throws FeedServerClientException if any communication issues occur with the feed or the
* feed ID is invalid or malformed.
*/
public void deleteEntities(URL baseUrl, List<T> entities) throws FeedServerClientException {
for (T entity : entities) {
deleteEntity(baseUrl, entity);
}
}
/**
* Updates the entry using the baseUrl plus the ID contained in the entry's entity.
*
* @param baseUrl fully qualified feed URL.
* @param entry populated entry object.
* @throws FeedServerClientException if any feed communication errors occur.
*/
public void updateEntry(URL baseUrl, FeedServerEntry entry) throws FeedServerClientException {
Object id = getBeanProperty(NAME_ELEMENT, entry.getEntity(entityClass));
if (id == null) {
id = getBeanProperty(ID_ELEMENT, entry.getEntity(entityClass));
}
if (id == null) {
throw new FeedServerClientException("name or id is required in the entry to update");
}
try {
LOG.info("updating entry at feed " + baseUrl + "/" + id);
URL url = new URL(baseUrl + "/" + id);
service.update(url, entry);
} catch (IOException e) {
throw new FeedServerClientException(e);
} catch (ServiceException e) {
throw new FeedServerClientException(e);
}
}
/**
* Updates the entry using the baseUrl plus the ID contained in the entity.
*
* @param baseUrl feed URL without an ID.
* @param entity a bean representing a feed entry.
* @throws FeedServerClientException if any feed communication issues occur or the URL is
* malformed.
*/
public void updateEntity(URL baseUrl, T entity) throws FeedServerClientException {
FeedServerEntry entry = new FeedServerEntry(entity);
updateEntry(baseUrl, entry);
}
/**
* Updates the entries using the baseUrl plus the ID contained in each entry's entity.
*
* @param baseUrl feed URL without an ID.
* @param entries a list of entries
* @throws FeedServerClientException if any feed communication issues occur or the URL is
* malformed.
*/
public void updateEntries(URL baseUrl, List<FeedServerEntry> entries) throws FeedServerClientException {
for (FeedServerEntry entry : entries) {
updateEntry(baseUrl, entry);
}
}
/**
* Updates the entries using the baseUrl plus the ID contained in each entity.
*
* @param baseUrl feed URL without an ID.
* @param entities a list of beans representing feed entries.
* @throws FeedServerClientException if any feed communication issues occur or the URL is
* malformed.
*/
public void updateEntities(URL baseUrl, List<T> entities) throws FeedServerClientException {
for (T entity : entities) {
updateEntity(baseUrl, entity);
}
}
/**
* Creates a new entry from the given entity and inserts it.
*
* @param baseUrl feed URL without an ID.
* @param entity a bean representing a feed entry.
* @throws FeedServerClientException if any feed communication issues occur or the URL is
* malformed.
*/
@SuppressWarnings("unchecked")
public T insertEntity(URL baseUrl, T entity) throws FeedServerClientException {
FeedServerEntry entry = new FeedServerEntry(entity);
try {
LOG.info("inserting entry at feed " + baseUrl);
entry = service.insert(baseUrl, entry);
return (T) entry.getEntity(entity.getClass());
} catch (IOException e) {
throw new FeedServerClientException(e);
} catch (ServiceException e) {
throw new FeedServerClientException(e);
}
}
/**
* Inserts the entry using the baseUrl provided.
*
* @param baseUrl feed URL without an ID.
* @param entry a populated feed entry.
* @throws FeedServerClientException if any feed communication issues occur or the URL is
* malformed.
*/
public void insertEntry(URL baseUrl, FeedServerEntry entry) throws FeedServerClientException {
try {
LOG.info("inserting entry at feed " + baseUrl);
service.insert(baseUrl, entry);
} catch (IOException e) {
throw new FeedServerClientException(e);
} catch (ServiceException e) {
throw new FeedServerClientException(e);
}
}
/**
* Creates an entry for each provided entity and inserts this. This results in one request
* for each given entity.
*
* @param baseUrl feed URL without an ID.
* @param entities a list of entity beans each representing a feed entry.
* @throws FeedServerClientException if any feed communication issues occur or the URL is
* malformed.
*/
public void insertEntities(URL baseUrl, List<T> entities) throws FeedServerClientException {
for (T entity : entities) {
insertEntity(baseUrl, entity);
}
}
/**
* Inserts the entries provided using the baseUrl provided. This results in one request for
* each given entry.
*
* @param baseUrl feed URL without an ID.
* @param entries a list of feed entries.
* @throws FeedServerClientException if any feed communication issues occur or the URL is
* malformed.
*/
public void insertEntries(URL baseUrl, List<FeedServerEntry> entries)
throws FeedServerClientException {
for (FeedServerEntry entry : entries) {
insertEntry(baseUrl, entry);
}
}
/**
* Helper method to retrieve a property from the provided bean.
*
* @param propertyName the property to read from the bean.
* @param bean the bean to read from.
* @return the container supplied.
* @throws FeedServerClientException if any problems exist with the bean.
*/
private Object getBeanProperty(String propertyName, T bean) throws
FeedServerClientException {
try {
BeanMap beanMap = new BeanMap(bean);
return beanMap.get(propertyName);
} catch (IllegalArgumentException e) {
throw new RuntimeException("Invalid bean " + bean.getClass().getName(), e);
} catch (SecurityException e) {
throw new RuntimeException("Invalid bean " + bean.getClass().getName(), e);
}
}
public GoogleService getService() {
return service;
}
}