Java tutorial
/* ======================================================================== * Copyright (c) 2010-2013 The University of Washington * * 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.washington.iam.registry.rp; import java.util.ArrayList; import java.util.List; import java.util.Vector; import java.io.File; import java.io.FileWriter; import java.io.BufferedWriter; import java.net.URI; import java.net.URISyntaxException; import java.io.IOException; import java.lang.InterruptedException; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.Properties; import org.springframework.beans.factory.annotation.Autowired; import org.w3c.dom.Document; import org.w3c.dom.Element; import javax.annotation.PostConstruct; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.ParserConfigurationException; import org.xml.sax.SAXException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import edu.washington.iam.tools.XMLHelper; import edu.washington.iam.registry.exception.RelyingPartyException; public class XMLMetadata implements MetadataDAO { private final Logger log = LoggerFactory.getLogger(getClass()); private final ReentrantReadWriteLock locker = new ReentrantReadWriteLock(); public void setId(String id) { this.id = id; } public void setDescription(String description) { this.description = description; } public void setEditable(boolean editable) { this.editable = editable; } public void setUri(String uri) { this.uri = uri; } public void setRefresh(int refresh) { this.refresh = refresh; } private String id; private String description; private boolean editable; private String uri; private String sourceName; private String tempUri; private int refresh = 0; private List<RelyingParty> relyingParties; Thread reloader = null; private String xmlStart = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<EntitiesDescriptor Name=\"urn:washington.edu:rpedit\"\n" + " xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\"\n" + " xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"\n" + " xmlns:shibmd=\"urn:mace:shibboleth:metadata:1.0\"\n" + " xmlns:xml=\"http://www.w3.org/XML/1998/namespace\"\n" + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n"; private String xmlEnd = "</EntitiesDescriptor>"; private String xmlNotice = "\n <!-- DO NOT EDIT: This is a binary, created by sp-registry -->\n\n"; private long modifyTime = 0; private void refreshMetadataIfNeeded() { log.debug("reloader checking..."); File f = new File(sourceName); if (f.lastModified() > modifyTime) { // reload the metadata log.debug("reloading metadata for " + id + " from " + uri); locker.writeLock().lock(); try { loadMetadata(); } catch (Exception e) { log.error("reload errro: " + e); } locker.writeLock().unlock(); log.debug("reload completed, time now " + modifyTime); } } // thread to sometimes reload the metadata class MetadataReloader extends Thread { public void run() { log.debug("reloader running: interval = " + refresh); // loop on checking the source while (true) { refreshMetadataIfNeeded(); try { if (isInterrupted()) { log.info("interrupted during processing"); break; } Thread.sleep(refresh * 1000); } catch (InterruptedException e) { log.info("sleep interrupted"); break; } } } } @PostConstruct private void init() throws RelyingPartyException { sourceName = uri.replaceFirst("file:", ""); loadMetadata(); if (refresh > 0) { reloader = new Thread(new MetadataReloader()); reloader.start(); } } // load metadata from the url private void loadMetadata() throws RelyingPartyException { log.info("load relyingParties for " + id + " from " + uri); relyingParties = new Vector(); Document doc; try { DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); builderFactory.setNamespaceAware(true); DocumentBuilder builder = builderFactory.newDocumentBuilder(); doc = builder.parse(uri); } catch (Exception e) { log.error("parse issue: " + e); throw new RelyingPartyException("bad xml"); } // update the timestamp File f = new File(sourceName); modifyTime = f.lastModified(); log.debug("rp load " + f.getName() + ": time = " + modifyTime); List<Element> list = XMLHelper.getElementsByName(doc.getDocumentElement(), "EntityDescriptor"); log.info("found " + list.size()); for (int i = 0; i < list.size(); i++) { Element rpe = list.get(i); if (XMLHelper.getElementByName(rpe, "SPSSODescriptor") == null) continue; try { RelyingParty rp = new RelyingParty(rpe, id, editable); relyingParties.add(rp); } catch (RelyingPartyException e) { log.error("load of element failed: " + e); } } } @Override public void updateRelyingParty(RelyingParty rp) { if (!editable) return; refreshMetadataIfNeeded(); locker.readLock().lock(); // remove any existing one for (int i = 0; i < relyingParties.size(); i++) { RelyingParty r = relyingParties.get(i); if (r.getEntityId().equals(rp.getEntityId())) { relyingParties.remove(i); break; } } // add the new relyingParties.add(rp); locker.readLock().unlock(); writeMetadata(); } // remove a single rp @Override public void removeRelyingParty(String id) { if (!editable) return; refreshMetadataIfNeeded(); locker.readLock().lock(); for (int i = 0; i < relyingParties.size(); i++) { RelyingParty r = relyingParties.get(i); if (r.getEntityId().equals(id)) { relyingParties.remove(i); break; } } locker.readLock().unlock(); writeMetadata(); } // get rp by id @Override public RelyingParty getRelyingPartyById(String rpid) throws RelyingPartyException { log.debug("md " + id + " looking for " + rpid); refreshMetadataIfNeeded(); RelyingParty ret = null; locker.readLock().lock(); for (int i = 0; i < relyingParties.size(); i++) { if (relyingParties.get(i).getEntityId().equals(rpid)) ret = relyingParties.get(i); } locker.readLock().unlock(); if (ret != null) return ret; log.debug(" ..nope"); throw new RelyingPartyException("not found"); } @Override public List<String> searchRelyingPartyIds(String searchStr) { refreshMetadataIfNeeded(); List<String> list = new ArrayList<>(); locker.readLock().lock(); try { for (RelyingParty rp : relyingParties) { if (searchStr == null || rp.getEntityId().matches(String.format(".*{0}.*", searchStr))) { list.add(rp.getEntityId()); } } } finally { locker.readLock().unlock(); } return list; } // write the metadata public int writeMetadata() { locker.readLock().lock(); try { URI xUri = new URI(tempUri); File xfile = new File(xUri); FileWriter xstream = new FileWriter(xfile); BufferedWriter xout = new BufferedWriter(xstream); // write header xout.write(xmlStart); xout.write(xmlNotice); // write rps for (int i = 0; i < relyingParties.size(); i++) { RelyingParty rp = relyingParties.get(i); rp.writeXml(xout); xout.write("\n"); } // write trailer xout.write(xmlEnd); xout.close(); } catch (IOException e) { log.error("write io error: " + e); locker.readLock().unlock(); return 1; } catch (URISyntaxException e) { log.error("bad uri error: " + e); locker.readLock().unlock(); return 1; } catch (Exception e) { log.error("write error: " + e); locker.readLock().unlock(); return 1; } // move the temp file to live try { File live = new File(new URI(uri)); File temp = new File(new URI(tempUri)); temp.renameTo(live); } catch (Exception e) { log.info("rename: " + e); } locker.readLock().unlock(); return 0; } public String getId() { return (id); } public String getDescription() { return (description); } public boolean isEditable() { return editable; } public List<RelyingParty> getRelyingParties() { return relyingParties; } public void setTempUri(String v) { tempUri = v; } public String getTempUri() { return tempUri; } public String getXmlStart() { return (xmlStart); } public String getXmlEnd() { return (xmlEnd); } public String getXmlNotice() { return (xmlNotice); } public void cleanup() { log.info("Metadata got signal to cleanup"); if (reloader != null) reloader.interrupt(); } }