View Javadoc

1   /*
2    * Copyright James Leigh (c) 2007.
3    *
4    * Licensed under the Aduna BSD-style license.
5    */
6   package org.openrdf.repository.loader;
7   
8   import java.io.IOException;
9   import java.net.URL;
10  import java.net.URLConnection;
11  import java.util.Enumeration;
12  import java.util.Map;
13  import java.util.Properties;
14  import java.util.concurrent.ConcurrentHashMap;
15  
16  import org.openrdf.model.URI;
17  import org.openrdf.model.ValueFactory;
18  import org.openrdf.repository.Repository;
19  import org.openrdf.repository.RepositoryConnection;
20  import org.openrdf.repository.RepositoryException;
21  import org.openrdf.repository.base.RepositoryWrapper;
22  import org.openrdf.rio.RDFFormat;
23  import org.openrdf.rio.RDFParseException;
24  import org.slf4j.Logger;
25  import org.slf4j.LoggerFactory;
26  
27  /**
28   * This repository monitors external datasets and loads then into a context in
29   * the repository.
30   * 
31   * @author James Leigh
32   */
33  public class LoaderRepository extends RepositoryWrapper {
34  	private final Logger logger = LoggerFactory
35  			.getLogger(LoaderRepository.class);
36  	private Map<URL, Map<URL, URI>> index = new ConcurrentHashMap<URL, Map<URL, URI>>();
37  	private ClassLoader cl = Thread.currentThread().getContextClassLoader();
38  	private long lastModified;
39  
40  	public LoaderRepository() {
41  		super();
42  	}
43  
44  	public LoaderRepository(Repository delegate) {
45  		super(delegate);
46  	}
47  
48  	public synchronized void loadResources(String path) throws IOException,
49  			RepositoryException, RDFParseException {
50  		refresh();
51  		Enumeration<URL> resources = cl.getResources(path);
52  		if (!resources.hasMoreElements()) {
53  			ClassLoader classLoader = LoaderRepository.class.getClassLoader();
54  			resources = classLoader.getResources(path);
55  		}
56  		if (!resources.hasMoreElements())
57  			throw new IllegalArgumentException(path + " not found");
58  		while (resources.hasMoreElements()) {
59  			URL resource = resources.nextElement();
60  			Map<URL, URI> map = new ConcurrentHashMap<URL, URI>();
61  			loadIndex(resource, resource.openConnection(), map);
62  			index.put(resource, map);
63  			for (Map.Entry<URL, URI> e : map.entrySet()) {
64  				reload(e.getKey(), e.getKey().openConnection(), e.getValue());
65  			}
66  		}
67  		lastModified = System.currentTimeMillis();
68  	}
69  
70  	public synchronized void loadContext(URL dataset, URI context)
71  			throws IOException, RepositoryException, RDFParseException {
72  		Map<URL, URI> map = new ConcurrentHashMap<URL, URI>();
73  		map.put(dataset, context);
74  		index.put(dataset, map);
75  		reload(dataset, dataset.openConnection(), context);
76  	}
77  
78  	@Override
79  	public RepositoryConnection getConnection() throws RepositoryException {
80  		try {
81  			refresh();
82  		} catch (IOException e) {
83  			throw new RepositoryException(e);
84  		} catch (RDFParseException e) {
85  			throw new RepositoryException(e);
86  		}
87  		return super.getConnection();
88  	}
89  
90  	@Override
91  	public void shutDown() throws RepositoryException {
92  		clearLoadedContexts();
93  		super.shutDown();
94  	}
95  
96  	public void clearLoadedContexts() throws RepositoryException {
97  		RepositoryConnection conn = super.getConnection();
98  		try {
99  			conn.setAutoCommit(false);
100 			for (Map.Entry<URL, Map<URL, URI>> e : index.entrySet()) {
101 				for (Map.Entry<URL, URI> f : e.getValue().entrySet()) {
102 					conn.clear(f.getValue());
103 				}
104 			}
105 			conn.commit();
106 		} finally {
107 			conn.close();
108 		}
109 	}
110 
111 	private void refresh() throws IOException, RepositoryException,
112 			RDFParseException {
113 		URLConnection open;
114 		long modified = lastModified;
115 		for (Map.Entry<URL, Map<URL, URI>> e : index.entrySet()) {
116 			open = getUrlIfNeeded(e);
117 			if (open != null) {
118 				long m = loadIndex(e.getKey(), open, e.getValue());
119 				if (m > modified) {
120 					modified = m;
121 				}
122 			}
123 			for (Map.Entry<URL, URI> f : e.getValue().entrySet()) {
124 				open = getUrlIfNeeded(f.getKey());
125 				if (open != null) {
126 					long m = reload(f.getKey(), open, f.getValue());
127 					if (m > modified) {
128 						modified = m;
129 					}
130 				}
131 			}
132 		}
133 		lastModified = modified;
134 	}
135 
136 	private URI createURI(String context, URL dataset) {
137 		ValueFactory vf = getValueFactory();
138 		if (context == null || context.length() == 0)
139 			return vf.createURI(dataset.toExternalForm());
140 		return vf.createURI(context);
141 	}
142 
143 	private URLConnection getUrlIfNeeded(Map.Entry<URL, ?> e)
144 			throws IOException, RepositoryException {
145 		if (e.getValue() == null)
146 			return e.getKey().openConnection();
147 		return getUrlIfNeeded(e.getKey());
148 	}
149 
150 	private URLConnection getUrlIfNeeded(URL url) throws IOException,
151 			RepositoryException {
152 		URLConnection open = url.openConnection();
153 		if (lastModified == 0)
154 			return open;
155 		open.setIfModifiedSince(lastModified);
156 		if (open.getLastModified() > lastModified)
157 			return open;
158 		return null;
159 	}
160 
161 	private synchronized long loadIndex(URL url, URLConnection conn,
162 			Map<URL, URI> map) throws IOException {
163 		long modified = conn.getLastModified();
164 		RDFFormat format = findRdfFormat(url, conn, null);
165 		if (format == null) {
166 			Properties p = new Properties();
167 			p.load(conn.getInputStream());
168 			map.clear();
169 			for (Map.Entry e : p.entrySet()) {
170 				String path = e.getKey().toString();
171 				String context = e.getValue().toString();
172 				Enumeration<URL> resources = cl.getResources(path);
173 				while (resources.hasMoreElements()) {
174 					URL dataset = resources.nextElement();
175 					map.put(dataset, createURI(context, dataset));
176 				}
177 			}
178 		} else if (!map.containsKey(url)) {
179 			map.put(url, createURI(null, url));
180 		}
181 		return modified;
182 	}
183 
184 	private synchronized long reload(URL dataset, URLConnection url, URI context)
185 			throws RepositoryException, IOException, RDFParseException {
186 		RDFFormat format = findRdfFormat(dataset, url, RDFFormat.RDFXML);
187 		logger.info("Loading {}", dataset);
188 		long modified = url.getLastModified();
189 		RepositoryConnection conn = super.getConnection();
190 		try {
191 			conn.setAutoCommit(false);
192 			conn.clear(context);
193 			conn.add(url.getInputStream(), dataset.toExternalForm(), format,
194 					context);
195 			conn.commit();
196 		} finally {
197 			conn.close();
198 		}
199 		return modified;
200 	}
201 
202 	private RDFFormat findRdfFormat(URL url, URLConnection conn,
203 			RDFFormat defaultFormat) {
204 		RDFFormat format = RDFFormat.forMIMEType(conn.getContentType());
205 		if (format == null) {
206 			format = RDFFormat.forFileName(url.getFile(), defaultFormat);
207 		}
208 		return format;
209 	}
210 }