Skip to content

Commit 56b67db

Browse files
committed
Register converter and alias for RemoteConfig class.
This is required since versions 1.1.8 and lower externalized the JGit RemoteConfig class using a custom implementation. The new converter replicates that externalization so that legacy job configurations will be successfully parsed.
1 parent bf5f38d commit 56b67db

3 files changed

Lines changed: 280 additions & 11 deletions

File tree

src/main/java/hudson/plugins/git/GitSCM.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,6 +1285,10 @@ public static final class DescriptorImpl extends SCMDescriptor<GitSCM> {
12851285
public DescriptorImpl() {
12861286
super(GitSCM.class, GitRepositoryBrowser.class);
12871287
Run.XSTREAM.registerConverter(new ObjectIdConverter());
1288+
Items.XSTREAM.registerConverter(new RemoteConfigConverter(
1289+
Items.XSTREAM));
1290+
Items.XSTREAM.alias("org.spearce.jgit.transport.RemoteConfig",
1291+
RemoteConfig.class);
12881292
load();
12891293
}
12901294

src/main/java/hudson/plugins/git/ObjectIdConverter.java

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
*/
1818
public class ObjectIdConverter implements Converter {
1919

20-
private Base64Encoder base64;
20+
private final Base64Encoder base64;
2121

2222
/**
2323
* Create ObjectId converter
@@ -26,7 +26,7 @@ public ObjectIdConverter() {
2626
base64 = new Base64Encoder();
2727
}
2828

29-
public boolean canConvert(Class type) {
29+
public boolean canConvert(@SuppressWarnings("rawtypes") Class type) {
3030
return ObjectId.class == type;
3131
}
3232

@@ -35,15 +35,38 @@ public void marshal(Object source, HierarchicalStreamWriter writer,
3535
writer.setValue(((ObjectId) source).name());
3636
}
3737

38+
/**
39+
* Is the current reader node a legacy node?
40+
*
41+
* @param reader
42+
* @param context
43+
* @return true if legacy, false otherwise
44+
*/
45+
protected boolean isLegacyNode(HierarchicalStreamReader reader,
46+
UnmarshallingContext context) {
47+
return reader.hasMoreChildren()
48+
&& "byte-array".equals(reader.peekNextChild());
49+
}
50+
51+
/**
52+
* Legacy unmarshalling of object id
53+
*
54+
* @param reader
55+
* @param context
56+
* @return object id
57+
*/
58+
protected Object legacyUnmarshal(HierarchicalStreamReader reader,
59+
UnmarshallingContext context) {
60+
reader.moveDown();
61+
ObjectId sha1 = ObjectId.fromRaw(base64.decode(reader.getValue()));
62+
reader.moveUp();
63+
return sha1;
64+
}
65+
3866
public Object unmarshal(HierarchicalStreamReader reader,
39-
final UnmarshallingContext context) {
40-
if (reader.hasMoreChildren()
41-
&& "byte-array".equals(reader.peekNextChild())) {
42-
reader.moveDown();
43-
ObjectId sha1 = ObjectId.fromRaw(base64.decode(reader.getValue()));
44-
reader.moveUp();
45-
return sha1;
46-
} else
47-
return ObjectId.fromString(reader.getValue());
67+
UnmarshallingContext context) {
68+
if (isLegacyNode(reader, context))
69+
return legacyUnmarshal(reader, context);
70+
return ObjectId.fromString(reader.getValue());
4871
}
4972
}
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
package hudson.plugins.git;
2+
3+
import com.thoughtworks.xstream.XStream;
4+
import com.thoughtworks.xstream.converters.ConversionException;
5+
import com.thoughtworks.xstream.converters.Converter;
6+
import com.thoughtworks.xstream.converters.MarshallingContext;
7+
import com.thoughtworks.xstream.converters.UnmarshallingContext;
8+
import com.thoughtworks.xstream.converters.reflection.SerializableConverter;
9+
import com.thoughtworks.xstream.core.util.CustomObjectInputStream;
10+
import com.thoughtworks.xstream.core.util.HierarchicalStreams;
11+
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
12+
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
13+
import com.thoughtworks.xstream.mapper.Mapper;
14+
15+
import java.io.Externalizable;
16+
import java.io.IOException;
17+
import java.io.NotActiveException;
18+
import java.io.ObjectInput;
19+
import java.io.ObjectInputValidation;
20+
import java.io.ObjectOutput;
21+
import java.net.URISyntaxException;
22+
import java.util.ArrayList;
23+
import java.util.Collection;
24+
import java.util.HashMap;
25+
import java.util.Map;
26+
import java.util.Map.Entry;
27+
28+
import org.eclipse.jgit.lib.Config;
29+
import org.eclipse.jgit.transport.RemoteConfig;
30+
31+
/**
32+
* Remote config converter that handles unmarshaling legacy externalization of
33+
* JGit RemoteConfig class.
34+
*/
35+
public class RemoteConfigConverter implements Converter {
36+
37+
/**
38+
* Remote config proxy
39+
*/
40+
private static class RemoteConfigProxy extends Config implements
41+
Externalizable {
42+
43+
private static final String KEY_URL = "url";
44+
45+
private static final String KEY_FETCH = "fetch";
46+
47+
private static final String KEY_PUSH = "push";
48+
49+
private static final String KEY_UPLOADPACK = "uploadpack";
50+
51+
private static final String KEY_RECEIVEPACK = "receivepack";
52+
53+
private static final String KEY_TAGOPT = "tagopt";
54+
55+
private String name;
56+
private String[] uris;
57+
private String[] fetch;
58+
private String[] push;
59+
private String uploadpack;
60+
private String receivepack;
61+
private String tagopt;
62+
63+
/**
64+
* Create remote config proxy
65+
*/
66+
public RemoteConfigProxy() {
67+
uris = new String[0];
68+
fetch = new String[0];
69+
push = new String[0];
70+
uploadpack = "git-upload-pack";
71+
receivepack = "git-receive-pack";
72+
}
73+
74+
public String getString(String section, String subsection, String name) {
75+
if (KEY_UPLOADPACK.equals(name))
76+
return uploadpack;
77+
if (KEY_RECEIVEPACK.equals(name))
78+
return receivepack;
79+
if (KEY_TAGOPT.equals(name))
80+
return tagopt;
81+
return super.getString(section, subsection, name);
82+
}
83+
84+
public String[] getStringList(String section, String subsection,
85+
String name) {
86+
if (KEY_URL.equals(name))
87+
return uris;
88+
if (KEY_FETCH.equals(name))
89+
return fetch;
90+
if (KEY_PUSH.equals(name))
91+
return push;
92+
return super.getStringList(section, subsection, name);
93+
}
94+
95+
private void fromMap(Map<String, Collection<String>> map) {
96+
for (Entry<String, Collection<String>> entry : map.entrySet()) {
97+
String key = entry.getKey();
98+
Collection<String> values = entry.getValue();
99+
if (KEY_URL.equals(key))
100+
uris = values.toArray(new String[values.size()]);
101+
else if (KEY_FETCH.equals(key))
102+
fetch = values.toArray(new String[values.size()]);
103+
else if (KEY_PUSH.equals(key))
104+
push = values.toArray(new String[values.size()]);
105+
else if (KEY_UPLOADPACK.equals(key))
106+
for (String value : values)
107+
uploadpack = value;
108+
else if (KEY_RECEIVEPACK.equals(key))
109+
for (String value : values)
110+
receivepack = value;
111+
else if (KEY_TAGOPT.equals(key))
112+
for (String value : values)
113+
tagopt = value;
114+
}
115+
}
116+
117+
public void readExternal(ObjectInput in) throws IOException,
118+
ClassNotFoundException {
119+
name = in.readUTF();
120+
final int items = in.readInt();
121+
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
122+
for (int i = 0; i < items; i++) {
123+
String key = in.readUTF();
124+
String value = in.readUTF();
125+
Collection<String> values = map.get(key);
126+
if (values == null) {
127+
values = new ArrayList<String>();
128+
map.put(key, values);
129+
}
130+
values.add(value);
131+
}
132+
fromMap(map);
133+
}
134+
135+
public void writeExternal(ObjectOutput out) throws IOException {
136+
throw new IOException("writeExternal not supported");
137+
}
138+
139+
/**
140+
* @return remote config
141+
* @throws URISyntaxException
142+
*/
143+
public RemoteConfig toRemote() throws URISyntaxException {
144+
return new RemoteConfig(this, name);
145+
}
146+
}
147+
148+
private final Mapper mapper;
149+
private final SerializableConverter converter;
150+
151+
/**
152+
* Create remote config converter
153+
*
154+
* @param xStream
155+
*/
156+
public RemoteConfigConverter(XStream xStream) {
157+
mapper = xStream.getMapper();
158+
converter = new SerializableConverter(mapper,
159+
xStream.getReflectionProvider());
160+
}
161+
162+
public boolean canConvert(@SuppressWarnings("rawtypes") Class type) {
163+
return RemoteConfig.class == type;
164+
}
165+
166+
public void marshal(Object source, HierarchicalStreamWriter writer,
167+
MarshallingContext context) {
168+
converter.marshal(source, writer, context);
169+
}
170+
171+
/**
172+
* Is the current reader node a legacy node?
173+
*
174+
* @param reader
175+
* @param context
176+
* @return true if legacy, false otherwise
177+
*/
178+
protected boolean isLegacyNode(HierarchicalStreamReader reader,
179+
final UnmarshallingContext context) {
180+
return reader.getNodeName().startsWith("org.spearce");
181+
}
182+
183+
/**
184+
* Legacy unmarshalling of remote config
185+
*
186+
* @param reader
187+
* @param context
188+
* @return remote config
189+
*/
190+
protected Object legacyUnmarshal(final HierarchicalStreamReader reader,
191+
final UnmarshallingContext context) {
192+
final RemoteConfigProxy proxy = new RemoteConfigProxy();
193+
CustomObjectInputStream.StreamCallback callback = new CustomObjectInputStream.StreamCallback() {
194+
public Object readFromStream() {
195+
reader.moveDown();
196+
@SuppressWarnings("rawtypes")
197+
Class type = HierarchicalStreams.readClassType(reader, mapper);
198+
Object streamItem = context.convertAnother(proxy, type);
199+
reader.moveUp();
200+
return streamItem;
201+
}
202+
203+
@SuppressWarnings("rawtypes")
204+
public Map readFieldsFromStream() {
205+
throw new UnsupportedOperationException();
206+
}
207+
208+
public void defaultReadObject() {
209+
throw new UnsupportedOperationException();
210+
}
211+
212+
public void registerValidation(ObjectInputValidation validation,
213+
int priority) throws NotActiveException {
214+
throw new NotActiveException();
215+
}
216+
217+
public void close() {
218+
throw new UnsupportedOperationException();
219+
}
220+
};
221+
try {
222+
CustomObjectInputStream objectInput = CustomObjectInputStream
223+
.getInstance(context, callback);
224+
proxy.readExternal(objectInput);
225+
objectInput.popCallback();
226+
return proxy.toRemote();
227+
} catch (IOException e) {
228+
throw new ConversionException("Unmarshal failed", e);
229+
} catch (ClassNotFoundException e) {
230+
throw new ConversionException("Unmarshal failed", e);
231+
} catch (URISyntaxException e) {
232+
throw new ConversionException("Unmarshal failed", e);
233+
}
234+
}
235+
236+
public Object unmarshal(final HierarchicalStreamReader reader,
237+
final UnmarshallingContext context) {
238+
if (isLegacyNode(reader, context))
239+
return legacyUnmarshal(reader, context);
240+
return converter.unmarshal(reader, context);
241+
}
242+
}

0 commit comments

Comments
 (0)