/* ==================================================================
* WebServiceLocationService.java - Feb 19, 2011 2:43:42 PM
*
* Copyright 2007-2011 SolarNetwork.net Dev Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
* ==================================================================
*/
package net.solarnetwork.node.location.ws;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import net.solarnetwork.domain.GeneralLocationSourceMetadata;
import net.solarnetwork.node.LocationService;
import net.solarnetwork.node.domain.BasicGeneralLocation;
import net.solarnetwork.node.domain.BasicLocation;
import net.solarnetwork.node.domain.GeneralLocation;
import net.solarnetwork.node.domain.Location;
import net.solarnetwork.node.domain.PriceLocation;
import net.solarnetwork.node.domain.WeatherLocation;
import net.solarnetwork.node.support.JsonHttpClientSupport;
/**
* Web service implementation of {@link WebServiceLocationService}.
*
* @author matt
* @version 1.1
*/
public class WebServiceLocationService extends JsonHttpClientSupport implements LocationService {
/** Default value for the <code>cacheTtl</code> property. */
public static final Long DEFAULT_CACHE_TTL = 1000L * 60 * 60 * 24 * 7;
private String url = "/api/v1/sec/location";
private Long cacheTtl = DEFAULT_CACHE_TTL;
private final ConcurrentHashMap<String, CachedLocation> cache = new ConcurrentHashMap<String, CachedLocation>(
2);
@Override
public <T extends Location> T getLocation(Class<T> locationType, Long locationId) {
Set<String> tags = new HashSet<String>(1);
if ( PriceLocation.class.isAssignableFrom(locationType) ) {
tags.add(Location.PRICE_TYPE);
} else if ( WeatherLocation.class.isAssignableFrom(locationType) ) {
tags.add(Location.WEATHER_TYPE);
} else {
throw new IllegalArgumentException("The locationType " + locationType.getName()
+ " is not supported");
}
final String url = locationSourceMetadataUrl(null, locationId, null, tags);
try {
final InputStream in = jsonGET(url);
Collection<GeneralLocationSourceMetadata> results = extractCollectionResponseData(in,
GeneralLocationSourceMetadata.class);
if ( results != null && results.size() > 0 ) {
GeneralLocationSourceMetadata meta = results.iterator().next();
return convertLocationSourceMetadata(locationType, meta);
}
return null;
} catch ( IOException e ) {
if ( log.isTraceEnabled() ) {
log.trace("IOException querying for location source metadata at " + url, e);
} else if ( log.isDebugEnabled() ) {
log.debug("Unable to post data: " + e.getMessage());
}
throw new RuntimeException(e);
}
}
private <T extends Location> T convertLocationSourceMetadata(Class<T> locationType,
GeneralLocationSourceMetadata meta) {
T result;
try {
result = locationType.newInstance();
} catch ( InstantiationException e ) {
throw new RuntimeException(e);
} catch ( IllegalAccessException e ) {
throw new RuntimeException(e);
}
if ( result instanceof BasicLocation ) {
BasicLocation loc = (BasicLocation) result;
loc.setLocationId(meta.getLocationId());
loc.setSourceId(meta.getSourceId());
if ( meta.getMeta() != null ) {
loc.setLocationName(meta.getMeta().getInfoString("name"));
}
}
return result;
}
@Override
public <T extends Location> Collection<T> findLocations(final Class<T> locationType,
final String sourceName, final String locationName) {
Set<String> tags = new HashSet<String>(1);
if ( PriceLocation.class.isAssignableFrom(locationType) ) {
tags.add(Location.PRICE_TYPE);
} else if ( WeatherLocation.class.isAssignableFrom(locationType) ) {
tags.add(Location.WEATHER_TYPE);
} else {
throw new IllegalArgumentException("The locationType " + locationType.getName()
+ " is not supported");
}
final String url = locationSourceMetadataUrl(sourceName + ' ' + locationName, null, null, tags);
try {
final InputStream in = jsonGET(url);
Collection<GeneralLocationSourceMetadata> results = extractCollectionResponseData(in,
GeneralLocationSourceMetadata.class);
Collection<T> col = new ArrayList<T>();
if ( results != null ) {
for ( GeneralLocationSourceMetadata meta : results ) {
col.add(convertLocationSourceMetadata(locationType, meta));
}
}
return col;
} catch ( IOException e ) {
if ( log.isTraceEnabled() ) {
log.trace("IOException querying for location source metadata at " + url, e);
} else if ( log.isDebugEnabled() ) {
log.debug("Unable to post data: " + e.getMessage());
}
throw new RuntimeException(e);
}
}
private String locationSourceMetadataUrl(String query, Long locationId, String sourceId,
Set<String> tags) {
StringBuilder buf = new StringBuilder(getIdentityService().getSolarInBaseUrl());
buf.append(url);
StringBuilder q = new StringBuilder();
if ( query != null ) {
appendXWWWFormURLEncodedValue(q, "query", query);
}
if ( locationId != null ) {
appendXWWWFormURLEncodedValue(q, "locationId", locationId);
}
if ( sourceId != null ) {
appendXWWWFormURLEncodedValue(q, "sourceId", sourceId);
}
if ( tags != null ) {
for ( String tag : tags ) {
appendXWWWFormURLEncodedValue(q, "tags", tag);
}
}
if ( q.length() > 0 ) {
buf.append('?').append(q);
}
return buf.toString();
}
private CachedLocation getCachedLocation(String key) {
CachedLocation cachedLocation = cache.get(key);
if ( cachedLocation != null ) {
if ( cachedLocation.expires > System.currentTimeMillis() ) {
log.debug("Found cached location {} (expires in {}ms)", cachedLocation,
(System.currentTimeMillis() - cachedLocation.expires));
return cachedLocation;
}
}
return null;
}
@Override
public Collection<GeneralLocationSourceMetadata> findLocationMetadata(String query, String sourceId,
Set<String> tags) {
final String url = locationSourceMetadataUrl(query, null, sourceId, tags);
try {
final InputStream in = jsonGET(url);
return extractCollectionResponseData(in, GeneralLocationSourceMetadata.class);
} catch ( IOException e ) {
if ( log.isTraceEnabled() ) {
log.trace("IOException querying for location source metadata at " + url, e);
} else if ( log.isDebugEnabled() ) {
log.debug("Unable to post data: " + e.getMessage());
}
throw new RuntimeException(e);
}
}
private String locationSourceMetadataUrl(Long locationId, String sourceId) {
StringBuilder buf = new StringBuilder(getIdentityService().getSolarInBaseUrl());
buf.append(url);
if ( locationId != null ) {
buf.append('/').append(locationId);
}
StringBuilder q = new StringBuilder();
if ( sourceId != null ) {
appendXWWWFormURLEncodedValue(q, "sourceId", sourceId);
}
if ( q.length() > 0 ) {
buf.append('?').append(q);
}
return buf.toString();
}
@Override
public GeneralLocationSourceMetadata getLocationMetadata(Long locationId, String sourceId) {
final String url = locationSourceMetadataUrl(locationId, sourceId);
final String cacheKey = url;
CachedLocation cached = getCachedLocation(cacheKey);
if ( cached != null && cached instanceof GeneralLocation ) {
return cached.asGeneralLocationSourceMetadata();
}
try {
final InputStream in = jsonGET(url);
GeneralLocationSourceMetadata meta = extractResponseData(in,
GeneralLocationSourceMetadata.class);
CachedLocation cachedLocation = new CachedLocation(meta);
cachedLocation.expires = Long.valueOf(System.currentTimeMillis() + cacheTtl);
cache.put(cacheKey, cachedLocation);
return cachedLocation.asGeneralLocationSourceMetadata();
} catch ( IOException e ) {
if ( log.isTraceEnabled() ) {
log.trace("IOException querying for location source metadata at " + url, e);
} else if ( log.isDebugEnabled() ) {
log.debug("Unable to post data: " + e.getMessage());
}
throw new RuntimeException(e);
}
}
private static class CachedLocation {
private Long expires;
private final BasicGeneralLocation location;
private CachedLocation(GeneralLocationSourceMetadata meta) {
super();
BasicGeneralLocation loc = new BasicGeneralLocation();
loc.setLocationId(meta.getLocationId());
loc.setSourceId(meta.getSourceId());
loc.setSourceMetadata(meta);
this.location = loc;
}
public GeneralLocationSourceMetadata asGeneralLocationSourceMetadata() {
if ( location != null && location.getSourceMetadata() != null ) {
return location.getSourceMetadata();
}
return new GeneralLocationSourceMetadata();
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder("CachedLocation{locationId=");
if ( location != null ) {
buf.append(location.getLocationId());
}
buf.append(",sourceId=");
if ( location != null ) {
buf.append(location.getSourceId());
}
buf.append('}');
return buf.toString();
}
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Long getCacheTtl() {
return cacheTtl;
}
public void setCacheTtl(Long cacheTtl) {
this.cacheTtl = cacheTtl;
}
}