39
39
40
40
import java .io .IOException ;
41
41
import java .net .URISyntaxException ;
42
- import java .util .Map ;
43
42
import java .util .Optional ;
44
43
import java .util .concurrent .TimeUnit ;
45
44
import java .util .regex .Matcher ;
48
47
import javax .inject .Inject ;
49
48
import javax .inject .Provider ;
50
49
51
- import org .openjdk .jmc .common .unit .IConstrainedMap ;
52
50
import org .openjdk .jmc .common .unit .QuantityConversionException ;
53
- import org .openjdk .jmc .flightrecorder .configuration .events .EventOptionID ;
54
51
import org .openjdk .jmc .flightrecorder .configuration .recording .RecordingOptionsBuilder ;
55
- import org .openjdk .jmc .rjmx .services .jfr .IEventTypeInfo ;
56
52
import org .openjdk .jmc .rjmx .services .jfr .IRecordingDescriptor ;
57
53
58
- import io .cryostat .commands .internal .EventOptionsBuilder ;
59
54
import io .cryostat .commands .internal .RecordingOptionsBuilderFactory ;
60
55
import io .cryostat .core .net .JFRConnection ;
61
- import io .cryostat .core .templates .Template ;
62
56
import io .cryostat .core .templates .TemplateType ;
63
57
import io .cryostat .jmc .serialization .HyperlinkedSerializableRecordingDescriptor ;
64
- import io .cryostat .messaging .notifications .NotificationFactory ;
65
58
import io .cryostat .net .AuthManager ;
59
+ import io .cryostat .net .ConnectionDescriptor ;
66
60
import io .cryostat .net .TargetConnectionManager ;
67
61
import io .cryostat .net .web .WebServer ;
68
62
import io .cryostat .net .web .http .AbstractAuthenticatedRequestHandler ;
69
63
import io .cryostat .net .web .http .HttpMimeType ;
70
64
import io .cryostat .net .web .http .api .ApiVersion ;
65
+ import io .cryostat .recordings .RecordingCreationHelper ;
71
66
72
67
import com .google .gson .Gson ;
73
68
import io .vertx .core .MultiMap ;
76
71
import io .vertx .ext .web .RoutingContext ;
77
72
import io .vertx .ext .web .handler .impl .HttpStatusException ;
78
73
import org .apache .commons .lang3 .StringUtils ;
74
+ import org .apache .commons .lang3 .tuple .Pair ;
79
75
80
76
public class TargetRecordingsPostHandler extends AbstractAuthenticatedRequestHandler {
81
77
82
- // TODO extract this somewhere more appropriate
83
- public static final Template ALL_EVENTS_TEMPLATE =
84
- new Template (
85
- "ALL" ,
86
- "Enable all available events in the target JVM, with default option values. This will be very expensive and is intended primarily for testing Cryostat's own capabilities." ,
87
- "Cryostat" ,
88
- TemplateType .TARGET );
89
-
90
- private static final Pattern TEMPLATE_PATTERN =
91
- Pattern .compile ("^template=([\\ w]+)(?:,type=([\\ w]+))?$" );
92
-
93
78
static final String PATH = "targets/:targetId/recordings" ;
94
79
private final TargetConnectionManager targetConnectionManager ;
80
+ private final RecordingCreationHelper recordingCreationHelper ;
95
81
private final RecordingOptionsBuilderFactory recordingOptionsBuilderFactory ;
96
- private final EventOptionsBuilder .Factory eventOptionsBuilderFactory ;
97
82
private final Provider <WebServer > webServerProvider ;
98
83
private final Gson gson ;
99
- private final NotificationFactory notificationFactory ;
100
- private static final String NOTIFICATION_CATEGORY = "RecordingCreated" ;
101
84
102
85
@ Inject
103
86
TargetRecordingsPostHandler (
104
87
AuthManager auth ,
105
88
TargetConnectionManager targetConnectionManager ,
89
+ RecordingCreationHelper recordingCreationHelper ,
106
90
RecordingOptionsBuilderFactory recordingOptionsBuilderFactory ,
107
- EventOptionsBuilder .Factory eventOptionsBuilderFactory ,
108
91
Provider <WebServer > webServerProvider ,
109
- Gson gson ,
110
- NotificationFactory notificationFactory ) {
92
+ Gson gson ) {
111
93
super (auth );
112
94
this .targetConnectionManager = targetConnectionManager ;
95
+ this .recordingCreationHelper = recordingCreationHelper ;
113
96
this .recordingOptionsBuilderFactory = recordingOptionsBuilderFactory ;
114
- this .eventOptionsBuilderFactory = eventOptionsBuilderFactory ;
115
97
this .webServerProvider = webServerProvider ;
116
98
this .gson = gson ;
117
- this .notificationFactory = notificationFactory ;
118
99
}
119
100
120
101
@ Override
@@ -150,18 +131,11 @@ public void handleAuthenticated(RoutingContext ctx) throws Exception {
150
131
}
151
132
152
133
try {
153
- Optional <HyperlinkedSerializableRecordingDescriptor > descriptor =
134
+ ConnectionDescriptor connectionDescriptor = getConnectionDescriptorFromContext (ctx );
135
+ HyperlinkedSerializableRecordingDescriptor linkedDescriptor =
154
136
targetConnectionManager .executeConnectedTask (
155
- getConnectionDescriptorFromContext ( ctx ) ,
137
+ connectionDescriptor ,
156
138
connection -> {
157
- if (getDescriptorByName (connection , recordingName ).isPresent ()) {
158
- throw new HttpStatusException (
159
- 400 ,
160
- String .format (
161
- "Recording with name \" %s\" already exists" ,
162
- recordingName ));
163
- }
164
-
165
139
RecordingOptionsBuilder builder =
166
140
recordingOptionsBuilderFactory
167
141
.create (connection .getService ())
@@ -185,57 +159,34 @@ public void handleAuthenticated(RoutingContext ctx) throws Exception {
185
159
if (attrs .contains ("maxSize" )) {
186
160
builder = builder .maxSize (Long .parseLong (attrs .get ("maxSize" )));
187
161
}
188
- IConstrainedMap <String > recordingOptions = builder .build ();
189
- connection
190
- .getService ()
191
- .start (
192
- recordingOptions ,
193
- enableEvents (connection , eventSpecifier ));
194
- notificationFactory
195
- .createBuilder ()
196
- .metaCategory (NOTIFICATION_CATEGORY )
197
- .metaType (HttpMimeType .JSON )
198
- .message (
199
- Map .of (
200
- "recording" ,
201
- recordingName ,
202
- "target" ,
203
- getConnectionDescriptorFromContext (ctx )
204
- .getTargetId ()))
205
- .build ()
206
- .send ();
207
- return getDescriptorByName (connection , recordingName )
208
- .map (
209
- d -> {
210
- try {
211
- WebServer webServer =
212
- webServerProvider .get ();
213
- return new HyperlinkedSerializableRecordingDescriptor (
214
- d ,
215
- webServer .getDownloadURL (
216
- connection , d .getName ()),
217
- webServer .getReportURL (
218
- connection , d .getName ()));
219
- } catch (QuantityConversionException
220
- | URISyntaxException
221
- | IOException e ) {
222
- throw new HttpStatusException (500 , e );
223
- }
224
- });
162
+ Pair <String , TemplateType > template =
163
+ RecordingCreationHelper .parseEventSpecifierToTemplate (
164
+ eventSpecifier );
165
+ IRecordingDescriptor descriptor =
166
+ recordingCreationHelper .startRecording (
167
+ connectionDescriptor ,
168
+ builder .build (),
169
+ template .getLeft (),
170
+ template .getRight ());
171
+ try {
172
+ WebServer webServer = webServerProvider .get ();
173
+ return new HyperlinkedSerializableRecordingDescriptor (
174
+ descriptor ,
175
+ webServer .getDownloadURL (
176
+ connection , descriptor .getName ()),
177
+ webServer .getReportURL (
178
+ connection , descriptor .getName ()));
179
+ } catch (QuantityConversionException
180
+ | URISyntaxException
181
+ | IOException e ) {
182
+ throw new HttpStatusException (500 , e );
183
+ }
225
184
});
226
185
227
- descriptor .ifPresentOrElse (
228
- linkedDescriptor -> {
229
- ctx .response ().setStatusCode (201 );
230
- ctx .response ().putHeader (HttpHeaders .LOCATION , "/" + recordingName );
231
- ctx .response ()
232
- .putHeader (HttpHeaders .CONTENT_TYPE , HttpMimeType .JSON .mime ());
233
- ctx .response ().end (gson .toJson (linkedDescriptor ));
234
- },
235
- () -> {
236
- throw new HttpStatusException (
237
- 500 , "Unexpected failure to create recording" );
238
- });
186
+ ctx .response ().setStatusCode (201 );
187
+ ctx .response ().putHeader (HttpHeaders .LOCATION , "/" + recordingName );
188
+ ctx .response ().putHeader (HttpHeaders .CONTENT_TYPE , HttpMimeType .JSON .mime ());
189
+ ctx .response ().end (gson .toJson (linkedDescriptor ));
239
190
} catch (NumberFormatException nfe ) {
240
191
throw new HttpStatusException (
241
192
400 , String .format ("Invalid argument: %s" , nfe .getMessage ()), nfe );
@@ -250,59 +201,4 @@ protected Optional<IRecordingDescriptor> getDescriptorByName(
250
201
.filter (recording -> recording .getName ().equals (recordingName ))
251
202
.findFirst ();
252
203
}
253
-
254
- protected IConstrainedMap <EventOptionID > enableEvents (JFRConnection connection , String events )
255
- throws Exception {
256
- Matcher m = TEMPLATE_PATTERN .matcher (events );
257
- m .find ();
258
- String templateName = m .group (1 );
259
- String typeName = m .group (2 );
260
- if (ALL_EVENTS_TEMPLATE .getName ().equals (templateName )) {
261
- return enableAllEvents (connection );
262
- }
263
- if (typeName != null ) {
264
- return connection
265
- .getTemplateService ()
266
- .getEvents (templateName , TemplateType .valueOf (typeName ))
267
- .orElseThrow (
268
- () ->
269
- new IllegalArgumentException (
270
- String .format (
271
- "No template \" %s\" found with type %s" ,
272
- templateName , typeName )));
273
- }
274
- // if template type not specified, try to find a Custom template by that name. If none,
275
- // fall back on finding a Target built-in template by the name. If not, throw an
276
- // exception and bail out.
277
- return connection
278
- .getTemplateService ()
279
- .getEvents (templateName , TemplateType .CUSTOM )
280
- .or (
281
- () -> {
282
- try {
283
- return connection
284
- .getTemplateService ()
285
- .getEvents (templateName , TemplateType .TARGET );
286
- } catch (Exception e ) {
287
- return Optional .empty ();
288
- }
289
- })
290
- .orElseThrow (
291
- () ->
292
- new IllegalArgumentException (
293
- String .format (
294
- "Invalid/unknown event template %s" ,
295
- templateName )));
296
- }
297
-
298
- protected IConstrainedMap <EventOptionID > enableAllEvents (JFRConnection connection )
299
- throws Exception {
300
- EventOptionsBuilder builder = eventOptionsBuilderFactory .create (connection );
301
-
302
- for (IEventTypeInfo eventTypeInfo : connection .getService ().getAvailableEventTypes ()) {
303
- builder .addEvent (eventTypeInfo .getEventTypeID ().getFullKey (), "enabled" , "true" );
304
- }
305
-
306
- return builder .build ();
307
- }
308
204
}
0 commit comments