/** * */ package org.gcube.portlets.user.uriresolvermanager; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import org.gcube.common.scope.api.ScopeProvider; import org.gcube.portlets.user.uriresolvermanager.entity.Resolver; import org.gcube.portlets.user.uriresolvermanager.entity.ServiceAccessPoint; import org.gcube.portlets.user.uriresolvermanager.entity.ServiceParameter; import org.gcube.portlets.user.uriresolvermanager.exception.IllegalArgumentException; import org.gcube.portlets.user.uriresolvermanager.exception.NotImplementedException; import org.gcube.portlets.user.uriresolvermanager.exception.UriResolverMapException; import org.gcube.portlets.user.uriresolvermanager.readers.RuntimeResourceReader; import org.gcube.portlets.user.uriresolvermanager.readers.UriResolverMapReader; import org.gcube.portlets.user.uriresolvermanager.util.UrlEncoderUtil; import org.gcube.portlets.user.uriresolvermanager.util.UrlEncoderUtil.URI_PART; import org.gcube.portlets.user.urlshortener.UrlShortener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The Class UriResolverManager. * * @author Francesco Mangiacrapa francesco.mangiacrapa@isti.cnr.it Sep 6, 2016 */ public class UriResolverManager { private UriResolverMapReader uriResolverMapReader; private Map applicationTypes; private String applicationType; private RuntimeResourceReader reader; /** * A lock to prevent reader = null; */ private int usingReader = 0; private ServiceAccessPoint serviceAccessPoint; private Timer timer; /** * Lock reader. */ public synchronized void lockReader() { usingReader++; } /** * Release reader. */ public synchronized void releaseReader() { usingReader--; } /** * Count readers. * * @return the int */ public synchronized int countReaders() { return usingReader; } public static final Logger LOG = LoggerFactory.getLogger(UriResolverManager.class); /** * Instantiates a new uri resolver manager. Precondition: set the scope into * ScopeProvider {@link ScopeProvider#get()} The scope is used to look up the * generic resource with name: * {@link UriResolverMapReader#URI_RESOLVER_MAP_RESOURCE_NAME}, secondary type: * {@link UriResolverMapReader#URIRESOLVERMAP_SECONDARY_TYPE} from IS to map * ApplicationType with its Resolver * * @throws UriResolverMapException the uri resolver map exception * @throws IllegalArgumentException the illegal argument exception */ public UriResolverManager() throws UriResolverMapException, IllegalArgumentException { try { String scope = ScopeProvider.instance.get(); LOG.info("UriResolverManager is using scope: " + scope + ", read from ScopeProvider"); if (scope == null) throw new UriResolverMapException("Scope is null, set scope into ScopeProvider!"); this.uriResolverMapReader = new UriResolverMapReader(); this.applicationTypes = uriResolverMapReader.getApplicationTypes(); // this.setTimerUriResolverReader(RESET_DELAY, RESET_TIME); } catch (UriResolverMapException e) { LOG.error("UriResolverMapException: ", e); throw e; } catch (Exception e) { LOG.error("UriResolverManager: ", e); throw new UriResolverMapException("Map Application Type - Resources not found in IS"); } } /** * Sets the application type. * * @param applicationType the applicationType to set * @throws IllegalArgumentException the illegal argument exception */ public void setApplicationType(String applicationType) throws IllegalArgumentException { if (!this.applicationTypes.containsKey(applicationType)) { throw new IllegalArgumentException("Application type '" + applicationType + "' not found in Application Types: " + getApplicationTypes()); } this.applicationType = applicationType; } /** * Instance a UriResolverManager Precondition: set the scope provider * {@link ScopeProvider.instance.get()} The scope is used to look up the generic * resource {@link UriResolverMapReader#URI_RESOLVER_MAP} available in the * infrastructure to map ApplicationType with its Resolver * * @param applicationType a (valid) key Application Type * {@link UriResolverManager#getApplicationTypes()} * @throws UriResolverMapException the uri resolver map exception * @throws IllegalArgumentException the illegal argument exception */ public UriResolverManager(String applicationType) throws UriResolverMapException, IllegalArgumentException { this(); setApplicationType(applicationType); } /** * Gets the link. * * @param applicationType the application type * @param parameters the map of the parameters sent as HTTP query string * @param shortLink if true the link is shorted otherwise none * @return the link * @throws IllegalArgumentException the illegal argument exception * @throws UriResolverMapException the uri resolver map exception */ public String getLink(String applicationType, Map parameters, boolean shortLink) throws IllegalArgumentException, UriResolverMapException { this.applicationType = applicationType; return getLink(parameters, shortLink); } /** * Gets the link. * * @param parameters the map of the parameters sent as HTTP query * string * @param queryStringParameters the query string parameters * @param shortLink if true the link is shortened otherwise none * @return the link * @throws IllegalArgumentException the illegal argument exception * @throws UriResolverMapException the uri resolver map exception */ public String getLink(Map parameters, boolean shortLink) throws IllegalArgumentException, UriResolverMapException { if (applicationType == null) throw new IllegalArgumentException("Application type is null"); Resolver resolver = this.applicationTypes.get(applicationType); LOG.debug("The resolver found is of kind: "+ resolver.getClass().getSimpleName()); String link = null; if (parameters == null) throw new IllegalArgumentException("Input Map parameters is null"); try { // lockReader(); if (reader == null) { LOG.info("Runtime Resource Reader is null, istancing..."); reader = new RuntimeResourceReader(resolver.getResourceName()); } if (resolver.getEntryName() == null || resolver.getEntryName().isEmpty()) { LOG.warn("The entryname to " + resolver.getResourceName() + " is null or empty, reading first Access Point!!"); serviceAccessPoint = reader.getServiceAccessPoints().get(0); } else { LOG.warn("Reading Access Point for Entry Name: " + resolver.getEntryName()); serviceAccessPoint = reader.getServiceAccessPointForEntryName(resolver.getEntryName()); if (serviceAccessPoint == null) throw new UriResolverMapException("Entry Name " + resolver.getEntryName() + " not found in Resource name: " + resolver.getResourceName()); } List resourceParameters = serviceAccessPoint.getServiceParameters(); LOG.debug("Service parameters are: " + resourceParameters); // CHECK PARAMETERS for (ServiceParameter serviceParameter : resourceParameters) { if (serviceParameter.isMandatory()) { if (!parameters.containsKey(serviceParameter.getKey())) { throw new IllegalArgumentException("Mandatory service key (parameter) '" + serviceParameter.getKey() + "' not found into input map"); } } } String baseURI = serviceAccessPoint.getServiceUrl(); // SPECIALIZED IMPLEMENTATION OF RESOLVER try { link = resolver.getLink(baseURI, parameters); LOG.debug("Read specialized getLink: " + link); if (shortLink) { link = resolver.shortLink(link, parameters); } return link; } catch (NotImplementedException e) { LOG.info("Specialized getLink not implemented, going to default implementation"); } // GENERIC IMPLEMENTATION OF RESOLVER. DEFAULT IMPLEMENTATION APPLYING... if (link == null) { // not shortening so returning the link with the query string with only the // parameters encoded LOG.info("Specialized getLink is null, applying DEFAULT implementation via GET request on base URI and encoded query-string"); String queryString = UrlEncoderUtil.encodeQuery(parameters); link = String.format("%s?%s", baseURI, queryString); // LOG.info("returning base URI with encoded parameters in the query string: " + // linkEncoded); // return toReturn; } else { LOG.info("Specialized getLink is not null"); } String linkNotShort = link; LOG.info("Created HTTP link: " + link); // Short link required if (shortLink) { try { LOG.info("Short link requested, so encoding query string is required..."); URI_PART uriParts = UrlEncoderUtil.getURIParts(link); if (uriParts.getQueryString() != null && !uriParts.getQueryString().isEmpty()) { LOG.info("QueryString part " + uriParts.getQueryString() + " is not null, encoding it"); String queryStringEncoded = UrlEncoderUtil.encodeString(uriParts.getQueryString()); link = String.format("%s?%s", uriParts.getBaseURI(), queryStringEncoded); } LOG.info("Encoded link is: " + link); LOG.info("Shortner starts.."); String shortenedLink = shortTheLink(link); if (shortenedLink != null && shortenedLink.equals(link)) { // here the short link and the input link are identical // so the shortening did not work // I'm returning the decoded link because it is directly consumable via browser LOG.info("Short link is equal to long link, returning long link: " + linkNotShort); link = linkNotShort; } else { // here the link is really shortened LOG.debug("The link is really short, returning it"); link = shortenedLink; } } catch (Exception e) { LOG.warn("An error occurred during link shortening: ", e); // here I'm returning the decoded link in case of error on shortening it link = linkNotShort; } } } catch (IllegalArgumentException e) { LOG.error("Uri Resolver IllegalArgumentException: ", e); throw e; } catch (Exception e) { LOG.error("Uri Resolver Exception: ", e); throw new UriResolverMapException("Uri Resolver error: " + e.getMessage()); } LOG.info("Returning HTTP(s) link: " + link); return link; } private String shortTheLink(String sourceLink) { String toReturnLink = sourceLink; try { UrlShortener shortener = new UrlShortener(); String shortLink = shortener.shorten(sourceLink); LOG.info("Short link is: " + shortLink); toReturnLink = shortLink; } catch (Exception e) { LOG.warn("Returning source link, an error occurred during link shortening: ", e); } return toReturnLink; } /** * Gets the application types. * * @return the Application Types available */ public Set getApplicationTypes() { return this.applicationTypes.keySet(); } /** * Discovery service parameters. * * @param resolver the resolver * @return the list * @throws IllegalArgumentException the illegal argument exception * @throws Exception the exception */ public List discoveryServiceParameters(Resolver resolver) throws IllegalArgumentException, Exception { try { String scope = ScopeProvider.instance.get(); LOG.info("DiscoveryServiceParameters is using scope: " + scope + ", read from ScopeProvider"); if (scope == null) throw new UriResolverMapException("Scope is null, set scope into ScopeProvider!"); if (resolver == null) throw new IllegalArgumentException("Resolver is null, set Resolver"); RuntimeResourceReader reader = new RuntimeResourceReader(resolver.getResourceName()); ServiceAccessPoint serviceAccessPoint = null; if (resolver.getEntryName() == null || resolver.getEntryName().isEmpty()) { LOG.warn("The entryname to " + resolver.getResourceName() + " is null or empty, reading first Access Point!!"); serviceAccessPoint = reader.getServiceAccessPoints().get(0); } else { LOG.info("Reading Access Point for entryname: " + resolver.getEntryName()); serviceAccessPoint = reader.getServiceAccessPointForEntryName(resolver.getEntryName()); if (serviceAccessPoint == null) throw new UriResolverMapException("Entry Name " + resolver.getEntryName() + " not found in Resource name: " + resolver.getResourceName()); } return serviceAccessPoint.getServiceParameters(); } catch (Exception e) { LOG.error("Uri Resolver error: ", e); throw new UriResolverMapException("Uri Resolver error: " + e.getMessage()); } } /** * Gets the resolver. * * @param applicationType the application type * @return the resolver */ public Resolver getResolver(String applicationType) { return this.applicationTypes.get(applicationType); } /** * Gets the capabilities. * * @return a map Application Type - Resolver */ public Map getCapabilities() { return this.applicationTypes; } /** * Sets the timer uri resolver reader. * * @param delay the delay * @param period the period */ public void setTimerUriResolverReader(long delay, long period) { cancelTimerUriResolverReader(); timer = new Timer(true); timer.schedule(new TimerTask() { @Override public void run() { LOG.info("Timer Reset Runtime Resource running.."); int counters = countReaders(); if (counters == 0) { LOG.info("Reader not locked, resetting"); reader = null; } else LOG.info("Reader locked, counters is/are:" + counters + ", skipping"); } }, delay, period); } /** * Cancel timer uri resolver reader. */ public void cancelTimerUriResolverReader() { if (timer != null) timer.cancel(); } /** * Invalid uri resolver reader. */ public void invalidUriResolverReader() { reader = null; } }