К счастью, программа позволяет на ходу подключать модули, содержащие произвольный код. А значит — в нашем распоряжении вся мощь reflection!
Напоминаю, что это proof-of-concept, поэтому для конкретной задачи придется как минимум установить нужный тип для accessor, и вообще при выдирании кода из контекста некоторые блоки могут потерять смысл. Например в приведенном примере всё будет работать и без замены accessor вообще, достаточно будет снять final. Следует учитывать, что это будет гарантированно работать только для объектов. Примитивы обычно инлайнятся компилятором.
package main;
class PublicMorozov
{
// заготавливаем инстанс, для которого мы будем создавать экземпляр inner класса
private static final PublicMorozov INSTANCE = new PublicMorozov();
// и поле, содержимое которого и будем заменять
private static final java.lang.ref.WeakReference<Inner> targetField = new java.lang.ref.WeakReference<Inner>(null);
private class Inner
{}
public PublicMorozov()
{}
public static void makeReplace() throws Exception
{
// получаем через рефлект поле, которое предстоит заменить
java.lang.reflect.Field targetAsField = Class.forName("main.PublicMorozov").getDeclaredField("targetField");
// снимаем с него private
targetAsField.setAccessible(true);
// получаем адрес поля модификаторов в целевом поле
java.lang.reflect.Field modifiers = Class.forName("java.lang.reflect.Field").getDeclaredField("modifiers");
// снимаем с него private
modifiers.setAccessible(true);
// снимаем с целевого поля private и final, а вместо них ставим public
modifiers.setInt(targetAsField, targetAsField.getModifiers() & ~java.lang.reflect.Modifier.FINAL);
modifiers.setInt(targetAsField, targetAsField.getModifiers() & ~java.lang.reflect.Modifier.PRIVATE);
modifiers.setInt(targetAsField, targetAsField.getModifiers() | java.lang.reflect.Modifier.PUBLIC);
// но всё не так просто... если попытаться применить изменения то нас может отправить куда подальше с IllegalAccessException
// поэтому мы заменяем accessor на свой, которому будет пофиг на финал
// мы используем именно overrideFieldAccessor поскольку поле изначально было private, в противном случае следует использовать fieldAccessor
java.lang.reflect.Field accessorField = Class.forName("java.lang.reflect.Field").getDeclaredField("overrideFieldAccessor");
// как обычно снимаем с него private
accessorField.setAccessible(true);
// поскольку мы заменяем статический Object нам нужен именно этот тип, их много под разные типы полей и данных
java.lang.reflect.Constructor accessorConstructor = Class.forName("sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl").getDeclaredConstructor(java.lang.reflect.Field.class, boolean.class);
// конструктор тоже сокрыт... но разве нас этим испугаешь?
accessorConstructor.setAccessible(true);
// вот теперь всё нормально - новый accessor на поле final и смотреть не будет
accessorField.set(targetAsField, accessorConstructor.newInstance(targetAsField, false));
// и на десерт - доступ к inner классу
java.lang.reflect.Constructor innerConstructor = Class.forName("main.PublicMorozov$Inner").getDeclaredConstructor(Class.forName("main.PublicMorozov"));
innerConstructor.setAccessible(true);
// заменяем таки содержимое нужного поля
targetAsField.set(null, new java.lang.ref.WeakReference<Inner>((Inner) innerConstructor.newInstance(INSTANCE)));
}
}
* This source code was highlighted with Source Code Highlighter.
Комментарии (25)
wwarlock
19.01.2010 04:28Я бы хотел уточнить насколько временное это решение?
На мой взгляд, лучше просто заменить код на правильный (без хаков) во время плановой остановки сервера на обслуживание. Или с помощью средств виртуализации, если таковые используются.Oblitus Автор
19.01.2010 04:28Код писался для разового использования, ибо останавливать сервер было нельзя, но и работать дальше без исправления было невозможно. Естественно, при плановой остановке было проведено нормальное обновление.
А еще таким способом можно работать с либами, для которых нет исходников. Изжоп, но всякое случается.
kivsiak
19.01.2010 04:28А можно уточнить зачем потребовалось менять аттрубут который разве что не заминирован, настолько его автор был против смены?
Oblitus Автор
19.01.2010 04:28Потому что модуль заглючил. Я уже сам забыл детали. А private final это вполне обычное состояние для внутренних объектов. ООП же, видимость ставится минимально возможной, во избежание. Иногда это выходит боком.
Gorthauer87
19.01.2010 04:28Главное потом не забыть связаться с автором, уговорить его сделать публичный метод, а потом не забыть убрать этот антипаттерн, а то ведь в один прекрасный день ружье возьмет да и выстрелит
naryl
19.01.2010 04:28Если вы знали заранее, что сервер нельзя ни в коем случае останавливать, а фиксить придётся, то Java — не лучший выбор. Конечно, я не могу знать всех причин, но Common Lisp или (если у вас панический страх перед скобочками) erlang был бы лучшим выбором.
Oblitus Автор
19.01.2010 04:28Причин много, и все веские. Пожалуй, самая веская та, что переписывать уже имеющегося монстра из >200000 строк кода на совершенно другой язык силами трех человек, ни один из которых этот язык не знает — дохлое дело.
SerGold
Павлик Морозов — это не паттерн, а Антипаттерн!
tagline: кармапоклонники — это безобидный коммент… в карму можете не смотреть :)
SwampRunner
что все так трясутся, жополизы.
SerGold
Вы мои комменты изучите, а потом подумайте жополиз ли я? :)
tagline: кармадрочеры старайтесь лучше "-10" за сегодня, темпы падения кармы падают… :)
SerGold
Думаю пора опубликовать предварительные итоги эксперимента с таглайнами:
Карма: -20,00
52 голоса
Сила: 26,50
выводы:
1. адекватных людей с кармой позволяющей ставить "+" только комментам, на хабре большинство;
2. не раз наблюдал «задротов» пробегающихся не только по карме, но и по всем комментам...;
3. выбраться из минуса невозможно!..
ну и главный вывод: система кармы прогнила!
Всем спасибо, если у кого смелости хватит — буду благодарен за размещение этого в виде топика!
SilenceAndy
Вы бредите. Хабр это сообщество и как в любом сообществе есть абсолютно разные люди. Кому-то ваш комментарий нравится, кому-то нет, кого-то он вообще бесит и он ставит минус в карму. Конечно есть опредленный процент людей которые в любом случае «срут» в карму, но их не так много.
Вы заработали минусы в карму не потому что она прогнила, а потому что устраивали какие-то непонятные эксперементы. В комментариях нужно комментировать, спрашивать или скажем уточнять, а не ставить эксперементы над системой кармы.
Поверте, все ваши минусы заслужены, ну по крайней мере те, что были поставленны с начала «эксперемента».
corristo
лично выбирался из -15 точно :)
maeris
Инопланетяне для этого слишком разумны.
Имхо это дело парсера. Вспоминается эпическое «Я идиот! Убейте меня!»