Можно ли создать URL, указывающий на объект в памяти?
Я пытаюсь расширить мою библиотеку для интеграции Swing
а также JPA
делая JPA
Конфигурация как автоматическая (и переносимая), как это может быть сделано, и это означает программное добавление <class>
элементы. (Я знаю, что это можно сделать через Hibernate's AnnotationConfiguration
или EclipseLInk's ServerSession
, но - мобильность). Я также хотел бы избежать использования Spring
только для этой единственной цели.
Я могу создать persistence.xml
на лету, и заполните его <class>
элементы из указанных пакетов (через библиотеку Reflections). Проблема начинается, когда я пытаюсь накормить это persistence.xml
к JPA
поставщик. Единственный способ, которым я могу думать, - это создать URLClassLoader
, но я не могу придумать способ, который не заставил бы меня сначала записать файл на диск где-нибудь, для единственной возможности получить действительный URL
, Настройка сокета для обслуживания файла через URL
(localhost:xxxx
Кажется... я не знаю, зло?
У кого-нибудь есть идеи, как можно решить эту проблему? Я знаю, что это большая работа, чтобы избежать использования одной библиотеки, но я просто хотел бы знать, можно ли это сделать.
РЕДАКТИРОВАТЬ (попытка быть более ясным):
Динамически генерируется XML
хранится в String
объект. Я не знаю, как сделать это доступным для постоянного поставщика. Кроме того, я хочу избежать записи файла на диск.
В целях моей проблемы поставщик постоянства - это просто класс, который сканирует путь к классам для META-INF/persistence.xml
, Некоторые реализации могут быть сделаны для принятия динамического создания XML
, но нет общего интерфейса (особенно для важной части файла, <class>
теги).
Моя идея состоит в том, чтобы настроить ClassLoader
- если у вас есть другие, я был бы благодарен, я не настроен на это.
Единственный легко расширяемый / настраиваемый, который я мог найти, был URLClassLoader
, Работает на URL
объекты, и я не знаю, смогу ли я создать его, не записав сначала XML на диск.
Вот как я настраиваю вещи, но это работает, написав persistenceXmlFile = new File("META-INF/persistence.xml")
на диск:
Thread.currentThread().setContextClassLoader(
new URLResourceClassLoader(
new URL[] { persistenceXmlFile.toURI().toURL() },
Thread.currentThread().getContextClassLoader()
)
);
URLResourceClassLoader
является URLCLassLoader
подкласс, который позволяет искать ресурсы и классы, переопределяя public Enumeration<URL> findResources(String name)
,
2 ответа
Может быть, немного поздно (через 4 года), но для тех, кто ищет подобное решение, вы можете использовать созданную мной фабрику URL:
public class InMemoryURLFactory {
public static void main(String... args) throws Exception {
URL url = InMemoryURLFactory.getInstance().build("/this/is/a/test.txt", "This is a test!");
byte[] data = IOUtils.toByteArray(url.openConnection().getInputStream());
// Prints out: This is a test!
System.out.println(new String(data));
}
private final Map<URL, byte[]> contents = new WeakHashMap<>();
private final URLStreamHandler handler = new InMemoryStreamHandler();
private static InMemoryURLFactory instance = null;
public static synchronized InMemoryURLFactory getInstance() {
if(instance == null)
instance = new InMemoryURLFactory();
return instance;
}
private InMemoryURLFactory() {
}
public URL build(String path, String data) {
try {
return build(path, data.getBytes("UTF-8"));
} catch (UnsupportedEncodingException ex) {
throw new RuntimeException(ex);
}
}
public URL build(String path, byte[] data) {
try {
URL url = new URL("memory", "", -1, path, handler);
contents.put(url, data);
return url;
} catch (MalformedURLException ex) {
throw new RuntimeException(ex);
}
}
private class InMemoryStreamHandler extends URLStreamHandler {
@Override
protected URLConnection openConnection(URL u) throws IOException {
if(!u.getProtocol().equals("memory")) {
throw new IOException("Cannot handle protocol: " + u.getProtocol());
}
return new URLConnection(u) {
private byte[] data = null;
@Override
public void connect() throws IOException {
initDataIfNeeded();
checkDataAvailability();
// Protected field from superclass
connected = true;
}
@Override
public long getContentLengthLong() {
initDataIfNeeded();
if(data == null)
return 0;
return data.length;
}
@Override
public InputStream getInputStream() throws IOException {
initDataIfNeeded();
checkDataAvailability();
return new ByteArrayInputStream(data);
}
private void initDataIfNeeded() {
if(data == null)
data = contents.get(u);
}
private void checkDataAvailability() throws IOException {
if(data == null)
throw new IOException("In-memory data cannot be found for: " + u.getPath());
}
};
}
}
}
Для этого мы можем использовать библиотеку Google Jimfs .
Во-первых, нам нужно добавить в наш проект зависимость maven :
<dependency>
<groupId>com.google.jimfs</groupId>
<artifactId>jimfs</artifactId>
<version>1.2</version>
</dependency>
После этого нам нужно настроить поведение нашей файловой системы и записать содержимое String в файл в памяти, например:
public static final String INPUT =
"\n"
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<note>\n"
+ " <to>Tove</to>\n"
+ " <from>Jani</from>\n"
+ " <heading>Reminder</heading>\n"
+ " <body>Don't forget me this weekend!</body>\n"
+ "</note>";
@Test
void usingJIMFS() throws IOException {
var fs = Jimfs.newFileSystem(Configuration.unix());
var path = fs.getPath(UUID.randomUUID().toString());
Files.writeString(path, INPUT);
var url = path.toUri().toURL();
assertThat(url.getProtocol()).isEqualTo("jimfs");
assertThat(Resources.asCharSource(url, UTF_8).read()).isEqualTo(INPUT);
}
Мы можем найти больше примеров в официальном репозитории .
Если мы заглянем в исходный код jimfs, мы обнаружим, что реализация похожа на ответ @NSV.