Java tutorial
/** * Copyright 2013 The Trustees of Indiana University * * 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 edu.indiana.d2i.htrc.portal; import edu.illinois.i3.htrc.registry.entities.workset.Volume; import edu.illinois.i3.htrc.registry.entities.workset.Workset; import edu.illinois.i3.htrc.registry.entities.workset.WorksetContent; import edu.illinois.i3.htrc.registry.entities.workset.Worksets; import edu.indiana.d2i.htrc.portal.bean.AlgorithmDetailsBean; import edu.indiana.d2i.htrc.portal.bean.VolumeDetailsBean; import org.apache.amber.oauth2.client.OAuthClient; import org.apache.amber.oauth2.client.URLConnectionClient; import org.apache.amber.oauth2.client.request.OAuthClientRequest; import org.apache.amber.oauth2.client.response.OAuthClientResponse; import org.apache.amber.oauth2.common.message.types.GrantType; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.StringRequestEntity; import org.w3c.dom.*; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import play.Logger; import play.mvc.Http; import javax.xml.bind.*; import javax.xml.bind.Element; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URLEncoder; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; public class HTRCPersistenceAPIClient { private static Logger.ALogger log = play.Logger.of("application"); private String accessToken; private String refreshToken; private static Http.Session session; private HttpClient client = new HttpClient(); public int responseCode; /** * xml parsing for algorithm info */ private XMLInputFactory factory = XMLInputFactory.newInstance(); /** * access token renew time */ private int renew = 0; private final int MAX_RENEW = 1; public HTRCPersistenceAPIClient(Http.Session session) { HTRCPersistenceAPIClient.session = session; accessToken = session.get(PortalConstants.SESSION_TOKEN); refreshToken = session.get(PortalConstants.SESSION_REFRESH_TOKEN); } private static class RSLValidationEventHandler implements ValidationEventHandler { public boolean handleEvent(ValidationEvent ve) { if (ve.getSeverity() == ValidationEvent.FATAL_ERROR || ve.getSeverity() == ValidationEvent.ERROR) return false; return true; } } private Object parseXML(String xmlStr) throws JAXBException { // if (log.isDebugEnabled()) { // Logger.debug("Workset Response: \n" + xmlStr); // } JAXBContext jaxbContext = JAXBContext.newInstance("edu.illinois.i3.htrc.registry.entities.workset"); Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); unmarshaller.setEventHandler(new RSLValidationEventHandler()); JAXBElement<Object> worksets = (JAXBElement<Object>) unmarshaller .unmarshal(new ByteArrayInputStream(xmlStr.getBytes())); return worksets.getValue(); } /** * This method renew the access token given a refresh token. * * @param refreshToken * @return access token * @throws IOException * @throws org.apache.amber.oauth2.common.exception.OAuthSystemException * @throws org.apache.amber.oauth2.common.exception.OAuthProblemException * @throws Exception */ public static String renewToken(String refreshToken) throws Exception { String oauthClientId = PlayConfWrapper.getOauthClientID(); String oauthClientSecret = PlayConfWrapper.getOauthClientSecrete(); String tokenLocation = PlayConfWrapper.tokenEndpoint(); if (oauthClientId != null && oauthClientSecret != null && tokenLocation != null && refreshToken != null) { OAuthClientRequest refreshTokenRequest = OAuthClientRequest.tokenLocation(tokenLocation) .setGrantType(GrantType.REFRESH_TOKEN).setRefreshToken(refreshToken).setClientId(oauthClientId) .setClientSecret(oauthClientSecret).buildBodyMessage(); OAuthClient refreshTokenClient = new OAuthClient(new URLConnectionClient()); OAuthClientResponse refreshTokenResponse = refreshTokenClient.accessToken(refreshTokenRequest); String refreshedAccessToken = refreshTokenResponse.getParam("access_token"); session.put(PortalConstants.SESSION_TOKEN, refreshedAccessToken); session.put(PortalConstants.SESSION_REFRESH_TOKEN, refreshTokenResponse.getParam("refresh_token")); log.info("Access token has been renewed to " + refreshedAccessToken); return refreshedAccessToken; } else { log.error("One or more variables are null."); return null; } } public List<Workset> getAllWorksets() throws IOException, JAXBException { String worksetUrl = PlayConfWrapper.registryEPR() + "/worksets" + "?public=" + true; log.debug("getAllWorksets Url: " + worksetUrl); GetMethod get = new GetMethod(worksetUrl); get.addRequestHeader("Authorization", "Bearer " + accessToken); get.addRequestHeader("Accept", "application/vnd.htrc-workset+xml"); int response = client.executeMethod(get); this.responseCode = response; if (response == 200) { String xmlStr = get.getResponseBodyAsString(); Worksets worksets = (Worksets) parseXML(xmlStr); renew = 0; return worksets.getWorkset(); } else if (response == 401 && (renew < MAX_RENEW)) { try { accessToken = renewToken(refreshToken); renew++; return getAllWorksets(); } catch (Exception e) { throw new IOException(e); } } else if (response == 404) { log.info("No workset is found for request " + worksetUrl); return Collections.emptyList(); } else { log.error("Response code is " + response + "\n" + get.getResponseBodyAsString()); throw new IOException("Response code is " + response + " for request " + worksetUrl); } } public List<Workset> getUserWorksets() throws IOException, JAXBException { String worksetUrl = PlayConfWrapper.registryEPR() + "/worksets"; log.debug("getUserWorksets Url: " + worksetUrl); GetMethod get = new GetMethod(worksetUrl); get.addRequestHeader("Authorization", "Bearer " + accessToken); get.addRequestHeader("Accept", "application/vnd.htrc-workset+xml"); int response = client.executeMethod(get); this.responseCode = response; if (response == 200) { String xmlStr = get.getResponseBodyAsString(); Worksets worksets = (Worksets) parseXML(xmlStr); renew = 0; return worksets.getWorkset(); } else if (response == 401 && (renew < MAX_RENEW)) { try { accessToken = renewToken(refreshToken); renew++; return getUserWorksets(); } catch (Exception e) { throw new IOException(e); } } else if (response == 404) { log.info("No workset is found for request " + worksetUrl); return Collections.emptyList(); } else { log.error("Response code is " + response + "\n" + get.getResponseBodyAsString()); throw new IOException("Response code is " + response + " for request " + worksetUrl); } } /** Get all the other allWorksets other than user's allWorksets * */ public List<Workset> getPublicWorksets() throws IOException, JAXBException { List<Workset> allWorksets = getAllWorksets(); allWorksets.removeAll(getUserWorksets()); return allWorksets; } public Workset getWorkset(String worksetId, String worksetAuthor) throws IOException, JAXBException { String worksetUrl = PlayConfWrapper.registryEPR() + "/worksets/" + worksetId + "?author=" + worksetAuthor; log.debug("getWorkset Url: " + worksetUrl); GetMethod get = new GetMethod(worksetUrl); get.addRequestHeader("Authorization", "Bearer " + accessToken); get.addRequestHeader("Accept", "application/vnd.htrc-workset+xml"); int response = client.executeMethod(get); this.responseCode = response; if (response == 200) { String xmlStr = get.getResponseBodyAsString(); Workset workset = (Workset) parseXML(xmlStr); renew = 0; return workset; } else if (response == 401 && (renew < MAX_RENEW)) { try { accessToken = renewToken(refreshToken); renew++; return getWorkset(worksetId, worksetAuthor); } catch (Exception e) { throw new IOException(e); } } else if (response == 404) { log.info("No workset is found for request " + worksetUrl); return null; } else { log.error("Response code is " + response + "\n" + get.getResponseBodyAsString()); throw new IOException("Response code is " + response + " for request " + worksetUrl); } } public List<Volume> getWorksetVolumes(String worksetName, String worksetAuthor) throws IOException, JAXBException { String worksetUrl = PlayConfWrapper.registryEPR() + "/worksets/" + worksetName + "?author=" + worksetAuthor; log.debug("getWorkset Url: " + worksetUrl); GetMethod get = new GetMethod(worksetUrl); get.addRequestHeader("Authorization", "Bearer " + accessToken); get.addRequestHeader("Accept", "application/vnd.htrc-workset+xml"); int response = client.executeMethod(get); this.responseCode = response; if (response == 200) { String xmlStr = get.getResponseBodyAsString(); Workset workset = (Workset) parseXML(xmlStr); WorksetContent worksetContent = workset.getContent(); renew = 0; if (worksetContent != null) { return worksetContent.getVolumes().getVolume(); } else { log.info("Workset content null."); return null; } } else if (response == 401 && (renew < MAX_RENEW)) { try { accessToken = renewToken(refreshToken); renew++; return getWorksetVolumes(worksetName, worksetAuthor); } catch (Exception e) { throw new IOException(e); } } else if (response == 404) { log.info("No workset is found for request " + worksetUrl); return null; } else { log.error("Response code is " + response + "\n" + get.getResponseBodyAsString()); throw new IOException("Response code is " + response + " for request " + worksetUrl); } } /** * Return all algorithm details. * * @return a hash map with algorithm id as key and algorithm detail as value * @throws IOException * @throws IllegalStateException * @throws JAXBException * @throws javax.xml.stream.XMLStreamException */ public Map<String, AlgorithmDetailsBean> getAllAlgorithmDetails() throws IOException, IllegalStateException, JAXBException, XMLStreamException { Map<String, AlgorithmDetailsBean> res = new TreeMap<String, AlgorithmDetailsBean>(); String algoFolder = PlayConfWrapper.registryAlgFolder(); log.debug("setting repo path to algofolder"); String str = getFilesAsString(algoFolder, ".*.xml", null, true); while (true) { int start = str.indexOf("<algorithm>"); int end = str.indexOf("</algorithm>"); if (start == -1 || end == -1) break; String sub = str.substring(start, end + "</algorithm>".length()); AlgorithmDetailsBean algoDetail = parseAlgorithmDetailBean( new ByteArrayInputStream(sub.getBytes("UTF-8"))); res.put(algoDetail.getName(), algoDetail); str = str.substring(end + "</algorithm>".length()); } return res; } public String getFilesAsString(String repoPath, String nameReg, String typeReg, boolean shared) throws HttpException, IOException { String url = PlayConfWrapper.registryEPR() + "/files" + repoPath; if (nameReg != null) { url += "?name=" + nameReg + "&"; } if (typeReg != null) { url += "?type=" + typeReg + "&"; } if (url.lastIndexOf("&") == url.length() - 1) { url += "public=" + String.valueOf(shared); } else { url += "?public=" + String.valueOf(shared); } Logger.info("getFilesAsString url: " + url); GetMethod get = new GetMethod(url); get.addRequestHeader("Authorization", "Bearer " + accessToken); int response = client.executeMethod(get); this.responseCode = response; if (response == 200) { renew = 0; return get.getResponseBodyAsString(); } else if (response == 404) { play.Logger.error("getFilesAsString failed: \n" + get.getResponseBodyAsString()); return ""; // empty string } else if (response == 401 && (renew < MAX_RENEW)) { play.Logger.info("getFilesAsString got access token expired error."); try { accessToken = renewToken(refreshToken); renew++; return getFilesAsString(repoPath, nameReg, typeReg, shared); } catch (Exception e) { throw new IOException(e); } } else { this.responseCode = response; throw new IOException("Response code " + response + " for " + url); } } private AlgorithmDetailsBean parseAlgorithmDetailBean(InputStream stream) throws XMLStreamException { AlgorithmDetailsBean res = new AlgorithmDetailsBean(); XMLStreamReader parser = factory.createXMLStreamReader(stream); List<AlgorithmDetailsBean.Parameter> parameters = new ArrayList<AlgorithmDetailsBean.Parameter>(); List<String> authors = new ArrayList<String>(); while (parser.hasNext()) { int event = parser.next(); if (event == XMLStreamConstants.START_ELEMENT) { if (parser.hasName()) { // only parse the info tag! if (parser.getLocalName().equals(AlgorithmDetailsBean.NAME)) { res.setName(parser.getElementText()); } else if (parser.getLocalName().equals(AlgorithmDetailsBean.VERSION)) { res.setVersion(parser.getElementText()); } else if (parser.getLocalName().equals(AlgorithmDetailsBean.DESCRIPTION)) { res.setDescription(parser.getElementText()); } else if (parser.getLocalName().equals(AlgorithmDetailsBean.SUPPORTURL)) { res.setSupportUrl(parser.getElementText()); } else if (parser.getLocalName().equals(AlgorithmDetailsBean.PARAMETER)) { AlgorithmDetailsBean.Parameter parameter = new AlgorithmDetailsBean.Parameter(); int count = parser.getAttributeCount(); for (int i = 0; i < count; i++) { if (parser.getAttributeLocalName(i).equals("required")) parameter.setRequired(Boolean.valueOf(parser.getAttributeValue(i))); if (parser.getAttributeLocalName(i).equals("type")) parameter.setType(parser.getAttributeValue(i)); if (parser.getAttributeLocalName(i).equals("name")) parameter.setName(parser.getAttributeValue(i)); if (parser.getAttributeLocalName(i).equals("defaultValue")) parameter.setDefaultValue(parser.getAttributeValue(i)); if (parser.getAttributeLocalName(i).equals("validation")) parameter.setValidation(parser.getAttributeValue(i)); if (parser.getAttributeLocalName(i).equals("validationError")) parameter.setValidationError(parser.getAttributeValue(i)); if (parser.getAttributeLocalName(i).equals("readOnly")) parameter.setReadOnly(Boolean.parseBoolean(parser.getAttributeValue(i))); } parser.nextTag(); if (parser.getLocalName().equals("label")) parameter.setLabel(parser.getElementText()); parser.nextTag(); if (parser.getLocalName().equals("description")) parameter.setDescription(parser.getElementText()); parameters.add(parameter); } else if (parser.getLocalName().equals(AlgorithmDetailsBean.AUTHOR)) { int count = parser.getAttributeCount(); for (int i = 0; i < count; i++) { if (parser.getAttributeLocalName(i).equals("name")) authors.add(parser.getAttributeValue(i)); } } } } } res.setParameters(parameters); res.setAuthors(authors); return res; } /** * Create workset * * @param worksetContent , Give workset content as a String * @param isPublic */ public void createWorkset(String worksetContent, boolean isPublic) throws IOException { String url; if (isPublic) { url = PlayConfWrapper.registryEPR() + "/worksets" + "?public=true"; } else { url = PlayConfWrapper.registryEPR() + "/worksets"; } PostMethod post = new PostMethod(url); post.addRequestHeader("Authorization", "Bearer " + accessToken); post.setRequestEntity(new StringRequestEntity(worksetContent, "application/vnd.htrc-workset+xml", "UTF-8")); int response = client.executeMethod(post); if (response == 201) { // nothing } else { this.responseCode = response; throw new IOException( "Response code " + response + " for " + url + " message: \n " + post.getResponseBodyAsString()); } } /** * Get Volum details from solr meta data instance. Here it adds '\' character before the ':'. * @param volId * @return VolumeDetailsBean * @throws IOException */ public VolumeDetailsBean getVolumeDetails(String volId) throws IOException { String volumeId; if (volId.contains(":")) { volumeId = volId.substring(0, volId.indexOf(":")) + "\\" + volId.substring(volId.indexOf(":")); } else { volumeId = volId; } String volumeDetailsQueryUrl = PlayConfWrapper.solrMetaQueryUrl() + "id:" + URLEncoder.encode(volumeId, "UTF-8") + "&fl=title,author,htrc_genderMale,htrc_genderFemale,htrc_genderUnknown,htrc_pageCount,htrc_wordCount"; VolumeDetailsBean volDetails = new VolumeDetailsBean(); if (log.isDebugEnabled()) { log.debug(volumeDetailsQueryUrl); } HttpClient httpClient = new HttpClient(); HttpMethod method = new GetMethod(volumeDetailsQueryUrl); method.setFollowRedirects(true); try { httpClient.executeMethod(method); volDetails.setVolumeId(volId); if (method.getStatusCode() == 200 && !method.getResponseBodyAsString().contains("<warn>RESPONSE CODE: 400</warn>")) { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = dbf.newDocumentBuilder(); Document dom = documentBuilder.parse(method.getResponseBodyAsStream()); NodeList result = dom.getElementsByTagName("result"); NodeList arrays = ((org.w3c.dom.Element) result.item(0)).getElementsByTagName("arr"); NodeList integers = ((org.w3c.dom.Element) result.item(0)).getElementsByTagName("int"); NodeList longIntegers = ((org.w3c.dom.Element) result.item(0)).getElementsByTagName("long"); for (int i = 0; i < arrays.getLength(); i++) { org.w3c.dom.Element arr = (org.w3c.dom.Element) arrays.item(i); if (arr.hasAttribute("name") && arr.getAttribute("name").equals("title")) { NodeList strElements = arr.getElementsByTagName("str"); volDetails.setTitle((strElements.item(0)).getTextContent()); } else if (arr.hasAttribute("name") && arr.getAttribute("name").equals("htrc_genderMale")) { NodeList strElements = arr.getElementsByTagName("str"); String maleAuthor = ""; for (int j = 0; j < strElements.getLength(); j++) { org.w3c.dom.Element str = (org.w3c.dom.Element) strElements.item(j); if (j != strElements.getLength() - 1) { maleAuthor += str.getTextContent(); } else { maleAuthor += str.getTextContent(); } } volDetails.setMaleAuthor(maleAuthor); } else if (arr.hasAttribute("name") && arr.getAttribute("name").equals("htrc_genderFemale")) { NodeList strElements = arr.getElementsByTagName("str"); String femaleAuthor = ""; for (int j = 0; j < strElements.getLength(); j++) { org.w3c.dom.Element str = (org.w3c.dom.Element) strElements.item(j); if (j != strElements.getLength() - 1) { femaleAuthor += str.getTextContent(); } else { femaleAuthor += str.getTextContent(); } } volDetails.setFemaleAuthor(femaleAuthor); } else if (arr.hasAttribute("name") && arr.getAttribute("name").equals("htrc_genderUnknown")) { NodeList strElements = arr.getElementsByTagName("str"); String genderUnknownAuthor = ""; for (int j = 0; j < strElements.getLength(); j++) { org.w3c.dom.Element str = (org.w3c.dom.Element) strElements.item(j); if (j != strElements.getLength() - 1) { genderUnknownAuthor += str.getTextContent(); } else { genderUnknownAuthor += str.getTextContent(); } } volDetails.setGenderUnkownAuthor(genderUnknownAuthor); } } for (int i = 0; i < integers.getLength(); i++) { org.w3c.dom.Element integer = (org.w3c.dom.Element) integers.item(i); if (integer.hasAttribute("name") && integer.getAttribute("name").equals("htrc_pageCount")) { String pageCount = integer.getTextContent(); volDetails.setPageCount(pageCount); } } for (int i = 0; i < longIntegers.getLength(); i++) { org.w3c.dom.Element longInteger = (org.w3c.dom.Element) longIntegers.item(0); if (longInteger.hasAttribute("name") && longInteger.getAttribute("name").equals("htrc_wordCount")) { String wordCount = longInteger.getTextContent(); volDetails.setWordCount(wordCount); } } } else { volDetails.setTitle("Cannot retrieve volume details."); log.warn("Cannot retrieve details for volume id: " + volId + " Response body: \n" + method.getResponseBodyAsString()); } } catch (SAXParseException e) { log.error("Error while parsing volume details for volume: " + volId + " query url: " + volumeDetailsQueryUrl + " status code: " + method.getStatusCode(), e); volDetails.setTitle("Cannot parse volume details."); } catch (ParserConfigurationException e) { log.error("Unrecoverable error while parsing volume details.", e); log.error("Error while parsing volume details for volume: " + volId + " query url: " + volumeDetailsQueryUrl + " status code: " + method.getStatusCode(), e); throw new RuntimeException("Unrecoverable error while parsing volume details.", e); } catch (SAXException e) { log.error("Error while parsing volume details for volume: " + volId + " query url: " + volumeDetailsQueryUrl + " status code: " + method.getStatusCode(), e); volDetails.setTitle("Cannot parse volume details."); } return volDetails; } }