0
# Model Framework
1
2
Configuration model system that provides hierarchical representation of logback components with processing, state tracking, serialization support, and integration with the Joran configuration framework. The Model framework serves as the bridge between XML configuration and component instantiation.
3
4
## Capabilities
5
6
### Core Model Architecture
7
8
#### Model Base Class
9
10
The foundational class representing configuration components in a hierarchical structure.
11
12
```java { .api }
13
/**
14
* Base class for all configuration models in the logback framework.
15
* Provides hierarchical structure, processing state, and serialization support.
16
*/
17
public class Model implements Serializable {
18
/**
19
* Get the line number where this model was defined in configuration.
20
* @return line number in source configuration
21
*/
22
public int getLineNumber();
23
24
/**
25
* Set the line number for this model.
26
* @param lineNumber line number in source configuration
27
*/
28
public void setLineNumber(int lineNumber);
29
30
/**
31
* Get the parent model in the hierarchy.
32
* @return parent model or null if this is root
33
*/
34
public Model getParent();
35
36
/**
37
* Set the parent model.
38
* @param parent parent model
39
*/
40
public void setParent(Model parent);
41
42
/**
43
* Add a child model to this model.
44
* @param child child model to add
45
*/
46
public void addSubModel(Model child);
47
48
/**
49
* Get all child models.
50
* @return list of child models
51
*/
52
public List<Model> getSubModels();
53
54
/**
55
* Get processing state for this model.
56
* @return true if model has been processed
57
*/
58
public boolean isProcessed();
59
60
/**
61
* Mark this model as processed.
62
* @param processed processing state
63
*/
64
public void setProcessed(boolean processed);
65
66
/**
67
* Get the tag name associated with this model.
68
* @return XML tag name
69
*/
70
public String getTag();
71
72
/**
73
* Set the tag name for this model.
74
* @param tag XML tag name
75
*/
76
public void setTag(String tag);
77
78
/**
79
* Get the body text content of this model.
80
* @return body text or null
81
*/
82
public String getBodyText();
83
84
/**
85
* Set the body text content.
86
* @param bodyText text content
87
*/
88
public void setBodyText(String bodyText);
89
90
/**
91
* Check if this model has been marked.
92
* Used for processing control and cycle detection.
93
* @return true if marked
94
*/
95
public boolean isMarked();
96
97
/**
98
* Mark this model for processing control.
99
* @param marked marking state
100
*/
101
public void setMarked(boolean marked);
102
}
103
```
104
105
### Component Model Hierarchy
106
107
#### ComponentModel
108
109
Base class for models representing configurable components.
110
111
```java { .api }
112
/**
113
* Model for configurable components with class name and property support.
114
*/
115
public class ComponentModel extends Model {
116
/**
117
* Get the component class name.
118
* @return fully qualified class name
119
*/
120
public String getClassName();
121
122
/**
123
* Set the component class name.
124
* @param className fully qualified class name
125
*/
126
public void setClassName(String className);
127
128
/**
129
* Get the component class object.
130
* @return Class object or null
131
*/
132
public Class<?> getClazz();
133
134
/**
135
* Set the component class object.
136
* @param clazz Class object
137
*/
138
public void setClazz(Class<?> clazz);
139
}
140
```
141
142
#### NamedComponentModel
143
144
Model for components that have names (like appenders, filters).
145
146
```java { .api }
147
/**
148
* Model for named components like appenders and filters.
149
*/
150
public class NamedComponentModel extends ComponentModel implements INamedModel {
151
/**
152
* Get the component name.
153
* @return component name
154
*/
155
public String getName();
156
157
/**
158
* Set the component name.
159
* @param name component name
160
*/
161
public void setName(String name);
162
}
163
```
164
165
#### INamedModel Interface
166
167
Interface for models that have names.
168
169
```java { .api }
170
/**
171
* Interface for models that can be identified by name.
172
*/
173
public interface INamedModel {
174
/**
175
* Get the model name.
176
* @return model name
177
*/
178
String getName();
179
180
/**
181
* Set the model name.
182
* @param name model name
183
*/
184
void setName(String name);
185
}
186
```
187
188
### Configuration Models
189
190
#### AppenderModel
191
192
Model representing appender configuration.
193
194
```java { .api }
195
/**
196
* Model for appender configuration with encoder and filter support.
197
*/
198
public class AppenderModel extends NamedComponentModel {
199
/**
200
* Get the encoder model.
201
* @return encoder model or null
202
*/
203
public EncoderModel getEncoder();
204
205
/**
206
* Set the encoder model.
207
* @param encoder encoder model
208
*/
209
public void setEncoder(EncoderModel encoder);
210
211
/**
212
* Get all filter models.
213
* @return list of filter models
214
*/
215
public List<FilterModel> getFilterModels();
216
217
/**
218
* Add a filter model.
219
* @param filterModel filter model to add
220
*/
221
public void addFilterModel(FilterModel filterModel);
222
}
223
```
224
225
#### PropertyModel
226
227
Model for property definitions and references.
228
229
```java { .api }
230
/**
231
* Model for property definitions with name, value, and scope.
232
*/
233
public class PropertyModel extends Model {
234
/**
235
* Get the property name.
236
* @return property name
237
*/
238
public String getName();
239
240
/**
241
* Set the property name.
242
* @param name property name
243
*/
244
public void setName(String name);
245
246
/**
247
* Get the property value.
248
* @return property value
249
*/
250
public String getValue();
251
252
/**
253
* Set the property value.
254
* @param value property value
255
*/
256
public void setValue(String value);
257
258
/**
259
* Get the property scope.
260
* @return property scope (context, system, local)
261
*/
262
public String getScope();
263
264
/**
265
* Set the property scope.
266
* @param scope property scope
267
*/
268
public void setScope(String scope);
269
270
/**
271
* Get the source for property value.
272
* @return source reference (file, resource, url)
273
*/
274
public String getSource();
275
276
/**
277
* Set the source for property value.
278
* @param source source reference
279
*/
280
public void setSource(String source);
281
}
282
```
283
284
#### ConditionalModel
285
286
Model for conditional configuration logic.
287
288
```java { .api }
289
/**
290
* Model for conditional configuration based on properties or expressions.
291
*/
292
public class ConditionalModel extends Model {
293
/**
294
* Get the condition expression.
295
* @return condition expression
296
*/
297
public String getCondition();
298
299
/**
300
* Set the condition expression.
301
* @param condition condition to evaluate
302
*/
303
public void setCondition(String condition);
304
305
/**
306
* Get the then models (executed if condition is true).
307
* @return list of then models
308
*/
309
public List<Model> getThenModels();
310
311
/**
312
* Add a model to the then branch.
313
* @param model model to add
314
*/
315
public void addThenModel(Model model);
316
317
/**
318
* Get the else models (executed if condition is false).
319
* @return list of else models
320
*/
321
public List<Model> getElseModels();
322
323
/**
324
* Add a model to the else branch.
325
* @param model model to add
326
*/
327
public void addElseModel(Model model);
328
}
329
```
330
331
### Model Processing Framework
332
333
#### ModelHandlerBase
334
335
Base class for model handlers that process specific model types.
336
337
```java { .api }
338
/**
339
* Base class for handlers that process specific model types.
340
* Provides context access and common processing utilities.
341
*/
342
public abstract class ModelHandlerBase extends ContextAwareBase {
343
protected ModelInterpretationContext mic;
344
345
/**
346
* Check if this handler can handle the given model.
347
* @param model model to check
348
* @return true if this handler can process the model
349
*/
350
public abstract boolean isSuitableForModel(Model model);
351
352
/**
353
* Handle the model processing.
354
* @param mic model interpretation context
355
* @param model model to process
356
* @throws ModelHandlerException if processing fails
357
*/
358
public abstract void handle(ModelInterpretationContext mic, Model model)
359
throws ModelHandlerException;
360
361
/**
362
* Post-handle processing after all children are processed.
363
* @param mic model interpretation context
364
* @param model model being processed
365
* @throws ModelHandlerException if post-processing fails
366
*/
367
public void postHandle(ModelInterpretationContext mic, Model model)
368
throws ModelHandlerException;
369
}
370
```
371
372
#### ModelInterpretationContext
373
374
Context for model processing providing access to objects, properties, and processing state.
375
376
```java { .api }
377
/**
378
* Context for model interpretation providing access to processing state.
379
*/
380
public class ModelInterpretationContext extends ContextAwareBase {
381
/**
382
* Get an object by key.
383
* @param key object key
384
* @return stored object or null
385
*/
386
public Object getObject(String key);
387
388
/**
389
* Store an object by key.
390
* @param key object key
391
* @param value object to store
392
*/
393
public void putObject(String key, Object value);
394
395
/**
396
* Push an object onto the processing stack.
397
* @param obj object to push
398
*/
399
public void pushObject(Object obj);
400
401
/**
402
* Pop an object from the processing stack.
403
* @return popped object
404
*/
405
public Object popObject();
406
407
/**
408
* Peek at the top object on the stack.
409
* @return top object without removing it
410
*/
411
public Object peekObject();
412
413
/**
414
* Check if the stack is empty.
415
* @return true if stack is empty
416
*/
417
public boolean isObjectStackEmpty();
418
419
/**
420
* Get property value with variable substitution.
421
* @param key property key
422
* @return property value with substitutions applied
423
*/
424
public String getProperty(String key);
425
426
/**
427
* Substitute variables in the given value.
428
* @param value value with potential variables
429
* @return value with variables substituted
430
*/
431
public String subst(String value);
432
433
/**
434
* Get the dependency analyzer for tracking component dependencies.
435
* @return dependency analyzer
436
*/
437
public DependencyAnalyzer getDependencyAnalyzer();
438
}
439
```
440
441
#### ModelHandlerException
442
443
Exception thrown during model processing.
444
445
```java { .api }
446
/**
447
* Exception thrown when model processing fails.
448
*/
449
public class ModelHandlerException extends Exception {
450
/**
451
* Create exception with message.
452
* @param msg error message
453
*/
454
public ModelHandlerException(String msg);
455
456
/**
457
* Create exception with message and cause.
458
* @param msg error message
459
* @param cause underlying cause
460
*/
461
public ModelHandlerException(String msg, Throwable cause);
462
}
463
```
464
465
#### DefaultProcessor
466
467
Main processor that orchestrates model processing through registered handlers.
468
469
```java { .api }
470
/**
471
* Default processor that handles model processing through registered handlers.
472
*/
473
public class DefaultProcessor extends ContextAwareBase {
474
/**
475
* Add a model handler.
476
* @param modelHandler handler to add
477
*/
478
public void addHandler(ModelHandlerBase modelHandler);
479
480
/**
481
* Process a model and its children.
482
* @param mic model interpretation context
483
* @param model model to process
484
* @throws ModelHandlerException if processing fails
485
*/
486
public void process(ModelInterpretationContext mic, Model model)
487
throws ModelHandlerException;
488
489
/**
490
* Find a suitable handler for the given model.
491
* @param model model to find handler for
492
* @return suitable handler or null
493
*/
494
protected ModelHandlerBase getSuitableModelHandler(Model model);
495
}
496
```
497
498
### Integration with Joran
499
500
#### BaseModelAction
501
502
Base action class that bridges SAX parsing and model creation.
503
504
```java { .api }
505
/**
506
* Base action for creating models from SAX events.
507
*/
508
public abstract class BaseModelAction extends Action {
509
/**
510
* Create a model instance for the current element.
511
* @param ic interpretation context
512
* @param localName element local name
513
* @param attributes element attributes
514
* @return created model instance
515
*/
516
protected abstract Model buildCurrentModel(InterpretationContext ic,
517
String localName, Attributes attributes);
518
519
/**
520
* Build model from SAX start element.
521
* @param ic interpretation context
522
* @param localName element local name
523
* @param attributes element attributes
524
*/
525
@Override
526
public void begin(InterpretationContext ic, String localName,
527
Attributes attributes) throws ActionException;
528
529
/**
530
* Set body text on the model.
531
* @param ic interpretation context
532
* @param body element body text
533
*/
534
@Override
535
public void body(InterpretationContext ic, String body)
536
throws ActionException;
537
538
/**
539
* Finalize model creation.
540
* @param ic interpretation context
541
* @param localName element local name
542
*/
543
@Override
544
public void end(InterpretationContext ic, String localName)
545
throws ActionException;
546
}
547
```
548
549
### Utility Classes
550
551
#### ModelUtil
552
553
Utility methods for model processing and manipulation.
554
555
```java { .api }
556
/**
557
* Utility methods for model processing.
558
*/
559
public class ModelUtil {
560
/**
561
* Deep clone a model hierarchy.
562
* @param model model to clone
563
* @return cloned model
564
*/
565
public static Model deepClone(Model model);
566
567
/**
568
* Find models of a specific type in the hierarchy.
569
* @param root root model to search from
570
* @param modelClass class of models to find
571
* @return list of matching models
572
*/
573
public static <T extends Model> List<T> findModelsOfType(Model root,
574
Class<T> modelClass);
575
576
/**
577
* Substitute variables in all string properties of a model.
578
* @param mic model interpretation context
579
* @param model model to process
580
*/
581
public static void substituteVariables(ModelInterpretationContext mic,
582
Model model);
583
584
/**
585
* Reset processing state for a model hierarchy.
586
* @param model root model to reset
587
*/
588
public static void resetProcessingState(Model model);
589
}
590
```
591
592
## Usage Examples
593
594
### Creating a Custom Model
595
596
```java
597
import ch.qos.logback.core.model.ComponentModel;
598
import ch.qos.logback.core.model.INamedModel;
599
600
public class CustomFilterModel extends ComponentModel implements INamedModel {
601
private String name;
602
private String pattern;
603
private boolean caseSensitive = true;
604
605
@Override
606
public String getName() {
607
return name;
608
}
609
610
@Override
611
public void setName(String name) {
612
this.name = name;
613
}
614
615
public String getPattern() {
616
return pattern;
617
}
618
619
public void setPattern(String pattern) {
620
this.pattern = pattern;
621
}
622
623
public boolean isCaseSensitive() {
624
return caseSensitive;
625
}
626
627
public void setCaseSensitive(boolean caseSensitive) {
628
this.caseSensitive = caseSensitive;
629
}
630
}
631
```
632
633
### Creating a Custom Model Handler
634
635
```java
636
import ch.qos.logback.core.model.processor.ModelHandlerBase;
637
import ch.qos.logback.core.model.processor.ModelInterpretationContext;
638
import ch.qos.logback.core.model.processor.ModelHandlerException;
639
640
public class CustomFilterModelHandler extends ModelHandlerBase {
641
642
@Override
643
public boolean isSuitableForModel(Model model) {
644
return model instanceof CustomFilterModel;
645
}
646
647
@Override
648
public void handle(ModelInterpretationContext mic, Model model)
649
throws ModelHandlerException {
650
CustomFilterModel cfm = (CustomFilterModel) model;
651
652
// Create the actual filter instance
653
CustomFilter filter = new CustomFilter();
654
filter.setContext(context);
655
filter.setName(cfm.getName());
656
filter.setPattern(cfm.getPattern());
657
filter.setCaseSensitive(cfm.isCaseSensitive());
658
659
// Validate configuration
660
if (cfm.getPattern() == null) {
661
addError("Pattern is required for CustomFilter");
662
return;
663
}
664
665
// Store in context for reference by other components
666
if (cfm.getName() != null) {
667
mic.putObject(cfm.getName(), filter);
668
}
669
670
// Add to parent component if applicable
671
Object parent = mic.peekObject();
672
if (parent instanceof FilterAttachable) {
673
((FilterAttachable<?>) parent).addFilter(filter);
674
}
675
676
filter.start();
677
}
678
}
679
```
680
681
### Using the Default Processor
682
683
```java
684
import ch.qos.logback.core.model.processor.DefaultProcessor;
685
import ch.qos.logback.core.model.processor.ModelInterpretationContext;
686
687
// Create processor and register handlers
688
DefaultProcessor processor = new DefaultProcessor();
689
processor.setContext(context);
690
691
// Add standard handlers
692
processor.addHandler(new AppenderModelHandler());
693
processor.addHandler(new FilterModelHandler());
694
processor.addHandler(new CustomFilterModelHandler());
695
696
// Create interpretation context
697
ModelInterpretationContext mic = new ModelInterpretationContext(context);
698
699
// Process model hierarchy
700
try {
701
processor.process(mic, rootModel);
702
} catch (ModelHandlerException e) {
703
addError("Failed to process model", e);
704
}
705
```
706
707
### Model-based Configuration Action
708
709
```java
710
import ch.qos.logback.core.joran.action.BaseModelAction;
711
import ch.qos.logback.core.joran.spi.InterpretationContext;
712
713
public class CustomFilterAction extends BaseModelAction {
714
715
@Override
716
protected Model buildCurrentModel(InterpretationContext ic,
717
String localName, Attributes attributes) {
718
CustomFilterModel model = new CustomFilterModel();
719
model.setTag(localName);
720
721
// Set properties from attributes
722
String name = attributes.getValue("name");
723
if (name != null) {
724
model.setName(name);
725
}
726
727
String className = attributes.getValue("class");
728
if (className != null) {
729
model.setClassName(className);
730
}
731
732
String pattern = attributes.getValue("pattern");
733
if (pattern != null) {
734
model.setPattern(pattern);
735
}
736
737
String caseSensitive = attributes.getValue("caseSensitive");
738
if (caseSensitive != null) {
739
model.setCaseSensitive(Boolean.parseBoolean(caseSensitive));
740
}
741
742
return model;
743
}
744
}
745
```
746
747
## Model Processing Phases
748
749
The Model framework processes configuration in distinct phases:
750
751
1. **Model Creation**: SAX events are converted to model objects through actions
752
2. **Model Assembly**: Models are arranged in hierarchical structure
753
3. **Model Processing**: Handlers process models to create actual component instances
754
4. **Component Configuration**: Created components are configured and started
755
5. **Dependency Resolution**: Component dependencies are resolved and injected
756
757
This separation enables sophisticated configuration processing, validation, and component lifecycle management while maintaining clean separation between parsing and instantiation concerns.
758
759
## Integration Patterns
760
761
The Model framework integrates with other logback-core components:
762
763
- **Joran Configuration**: Actions create models from XML elements
764
- **Component Factory**: Handlers instantiate and configure components
765
- **Context Management**: Models store component references in context
766
- **Property Substitution**: Variable substitution occurs during model processing
767
- **Conditional Logic**: Models support conditional configuration sections
768
- **Error Handling**: Processing errors are reported through context status system