1515import org .apache .maven .plugins .annotations .Parameter ;
1616import org .apache .maven .plugins .annotations .ResolutionScope ;
1717import org .apache .maven .project .MavenProject ;
18+ import org .codehaus .plexus .util .FileUtils ;
1819import org .codehaus .plexus .util .StringUtils ;
1920
2021import java .io .File ;
2324import java .nio .file .Files ;
2425import java .nio .file .Path ;
2526import java .nio .file .Paths ;
26- import java .util .Collection ;
27- import java .util .LinkedHashSet ;
28- import java .util .Set ;
27+ import java .util .*;
28+ import java .util .function .BiFunction ;
29+
30+ import static java .lang .String .format ;
2931
3032@ Mojo (
3133 name = "resolve" ,
3638)
3739public class SwaggerMojo extends AbstractMojo {
3840
39- public enum Format {JSON , YAML , JSONANDYAML };
41+ public enum Format {JSON , YAML , JSONANDYAML }
4042
41- public void execute () throws MojoExecutionException , MojoFailureException
42- {
43+ @ Override
44+ public void execute () throws MojoExecutionException , MojoFailureException {
4345 if (skip ) {
4446 getLog ().info ( "Skipping OpenAPI specification resolution" );
4547 return ;
4648 }
4749 getLog ().info ( "Resolving OpenAPI specification.." );
4850
49- if (project !=null ) {
51+ if (project != null ) {
5052 String pEnc = project .getProperties ().getProperty ("project.build.sourceEncoding" );
5153 if (StringUtils .isNotBlank (pEnc )) {
5254 projectEncoding = pEnc ;
@@ -56,43 +58,18 @@ public void execute() throws MojoExecutionException, MojoFailureException
5658 encoding = projectEncoding ;
5759 }
5860
59- OpenAPI openAPIInput = null ;
60- try {
61- if (StringUtils .isNotBlank (openapiFilePath )) {
62- Path openapiPath = Paths .get (openapiFilePath );
63- if (openapiPath .toFile ().exists () && openapiPath .toFile ().isFile ()) {
64- String openapiFileContent = new String (Files .readAllBytes (openapiPath ), encoding );
65- if (StringUtils .isNotBlank (openapiFileContent )) {
66- try {
67- openAPIInput = Json .mapper ().readValue (openapiFileContent , OpenAPI .class );
68- } catch (Exception e ) {
69- try {
70- openAPIInput = Yaml .mapper ().readValue (openapiFileContent , OpenAPI .class );
71- } catch (Exception e1 ) {
72- getLog ().error ( "Error reading/deserializing openapi file" , e );
73- throw new MojoFailureException (e .getMessage (), e );
74- }
75- }
76- }
77- }
78- }
79- } catch (Exception e ) {
80- getLog ().error ( "Error reading/deserializing openapi file" , e );
81- throw new MojoFailureException (e .getMessage (), e );
82- }
61+ // read swagger configuration if one was provided
62+ Optional <SwaggerConfiguration > swaggerConfiguration =
63+ readStructuredDataFromFile (configurationFilePath , SwaggerConfiguration .class , "configurationFilePath" );
64+
65+ // read openApi config, if one was provided
66+ Optional <OpenAPI > openAPIInput =
67+ readStructuredDataFromFile (openapiFilePath , OpenAPI .class , "openapiFilePath" );
68+
69+ config = mergeConfig (openAPIInput .orElse (null ), swaggerConfiguration .orElse (new SwaggerConfiguration ()));
70+
71+ setDefaultsIfMissing (config );
8372
84- SwaggerConfiguration config = new SwaggerConfiguration ()
85- .filterClass (filterClass )
86- .ignoredRoutes (ignoredRoutes )
87- .prettyPrint (prettyPrint )
88- .readAllResources (readAllResources )
89- .readerClass (readerClass )
90- .scannerClass (scannerClass )
91- .resourceClasses (resourceClasses )
92- .openAPI (openAPIInput )
93- .resourcePackages (resourcePackages )
94- .objectMapperProcessorClass (objectMapperProcessorClass )
95- .modelConverterClasses (modelConverterClasses );
9673 try {
9774 GenericOpenApiContextBuilder builder = new JaxrsOpenApiContextBuilder ()
9875 .openApiConfiguration (config );
@@ -147,6 +124,172 @@ public void execute() throws MojoExecutionException, MojoFailureException
147124 }
148125 }
149126
127+ private void setDefaultsIfMissing (SwaggerConfiguration config ) {
128+
129+ if (prettyPrint == null ) {
130+ prettyPrint = Boolean .FALSE ;
131+ }
132+ if (readAllResources == null ) {
133+ readAllResources = Boolean .TRUE ;
134+ }
135+ if (config .isPrettyPrint () == null ) {
136+ config .prettyPrint (prettyPrint );
137+ }
138+ if (config .isReadAllResources () == null ) {
139+ config .readAllResources (readAllResources );
140+ }
141+ }
142+
143+ /**
144+ * Read the content of given file as either json or yaml and maps it to given class
145+ *
146+ * @param filePath to read content from
147+ * @param outputClass to map to
148+ * @param configName for logging, what user config will be read
149+ * @param <T> mapped type
150+ * @return empty optional if not path was given or the file was empty, read instance otherwis
151+ * @throws MojoFailureException if given path is not file, could not be read or is not proper json or yaml
152+ */
153+ private <T > Optional <T > readStructuredDataFromFile (String filePath , Class <T > outputClass , String configName )
154+ throws MojoFailureException {
155+ try {
156+ // ignore if config is not provided
157+ if (StringUtils .isBlank (filePath )) {
158+ return Optional .empty ();
159+ }
160+
161+ Path pathObj = Paths .get (filePath );
162+
163+ // if file does not exist or is not an actual file, finish with error
164+ if (!pathObj .toFile ().exists () || !pathObj .toFile ().isFile ()) {
165+ throw new IllegalArgumentException (
166+ format ("passed path does not exist or is not a file: '%s'" , filePath ));
167+ }
168+
169+ String fileContent = new String (Files .readAllBytes (pathObj ), encoding );
170+
171+ // if provided file is empty, log warning and finish
172+ if (StringUtils .isBlank (fileContent )) {
173+ getLog ().warn (format ("It seems that file '%s' defined in config %s is empty" ,
174+ pathObj .toString (), configName ));
175+ return Optional .empty ();
176+ }
177+
178+ // get mappers in the order based on file extension
179+ List <BiFunction <String , Class <T >, T >> mappers = getSortedMappers (pathObj );
180+
181+ T instance = null ;
182+ Throwable caughtEx = null ;
183+
184+ // iterate through mappers and see if one is able to parse
185+ for (BiFunction <String , Class <T >, T > mapper : mappers ) {
186+ try {
187+ instance = mapper .apply (fileContent , outputClass );
188+ break ;
189+ } catch (Exception e ) {
190+ caughtEx = e ;
191+ }
192+ }
193+
194+ // if no mapper could read the content correctly, finish with error
195+ if (instance == null ) {
196+ if (caughtEx == null ) {
197+ caughtEx = new IllegalStateException ("undefined state" );
198+ }
199+ getLog ().error (format ("Could not read file '%s' for config %s" , pathObj .toString (), configName ), caughtEx );
200+ throw new IllegalStateException (caughtEx .getMessage (), caughtEx );
201+ }
202+
203+ return Optional .of (instance );
204+ } catch (Exception e ) {
205+ getLog ().error (format ("Error reading/deserializing config %s file" , configName ), e );
206+ throw new MojoFailureException (e .getMessage (), e );
207+ }
208+ }
209+
210+ /**
211+ * Get sorted list of mappers based on given filename.
212+ * <p>
213+ * Will sort the 2 supported mappers: json and yaml based on what file extension is used.
214+ *
215+ * @param pathObj to get extension from.
216+ * @param <T> mapped type
217+ * @return list of mappers
218+ */
219+ private <T > List <BiFunction <String , Class <T >, T >> getSortedMappers (Path pathObj ) {
220+ String ext = FileUtils .extension (pathObj .toString ());
221+ boolean yamlPreferred = false ;
222+ if (ext .equalsIgnoreCase ("yaml" ) || ext .equalsIgnoreCase ("yml" )) {
223+ yamlPreferred = true ;
224+ }
225+
226+ List <BiFunction <String , Class <T >, T >> list = new ArrayList <>(2 );
227+
228+ list .add ((content , typeClass ) -> {
229+ try {
230+ return Json .mapper ().readValue (content , typeClass );
231+ } catch (IOException e ) {
232+ throw new IllegalStateException (e );
233+ }
234+ });
235+ list .add ((content , typeClass ) -> {
236+ try {
237+ return Yaml .mapper ().readValue (content , typeClass );
238+ } catch (IOException e ) {
239+ throw new IllegalStateException (e );
240+ }
241+ });
242+
243+ if (yamlPreferred ) {
244+ Collections .reverse (list );
245+ }
246+
247+ return Collections .unmodifiableList (list );
248+ }
249+
250+ private SwaggerConfiguration mergeConfig (OpenAPI openAPIInput , SwaggerConfiguration config ) {
251+ // overwrite all settings provided by other maven config
252+ if (StringUtils .isNotBlank (filterClass )) {
253+ config .filterClass (filterClass );
254+ }
255+ if (isCollectionNotBlank (ignoredRoutes )) {
256+ config .ignoredRoutes (ignoredRoutes );
257+ }
258+ if (prettyPrint != null ) {
259+ config .prettyPrint (prettyPrint );
260+ }
261+ if (readAllResources != null ) {
262+ config .readAllResources (readAllResources );
263+ }
264+ if (StringUtils .isNotBlank (readerClass )) {
265+ config .readerClass (readerClass );
266+ }
267+ if (StringUtils .isNotBlank (scannerClass )) {
268+ config .scannerClass (scannerClass );
269+ }
270+ if (isCollectionNotBlank (resourceClasses )) {
271+ config .resourceClasses (resourceClasses );
272+ }
273+ if (openAPIInput != null ) {
274+ config .openAPI (openAPIInput );
275+ }
276+ if (isCollectionNotBlank (resourcePackages )) {
277+ config .resourcePackages (resourcePackages );
278+ }
279+ if (StringUtils .isNotBlank (objectMapperProcessorClass )) {
280+ config .objectMapperProcessorClass (objectMapperProcessorClass );
281+ }
282+ if (isCollectionNotBlank (modelConverterClasses )) {
283+ config .modelConverterClasses (modelConverterClasses );
284+ }
285+
286+ return config ;
287+ }
288+
289+ private boolean isCollectionNotBlank (Collection <?> collection ) {
290+ return collection != null && !collection .isEmpty ();
291+ }
292+
150293 @ Parameter ( property = "resolve.outputFileName" , defaultValue = "openapi" )
151294 private String outputFileName = "openapi" ;
152295
@@ -173,10 +316,10 @@ public void execute() throws MojoExecutionException, MojoFailureException
173316 */
174317 @ Parameter ( property = "resolve.objectMapperProcessorClass" )
175318 private String objectMapperProcessorClass ;
176- @ Parameter ( property = "resolve.prettyPrint" )
177- private Boolean prettyPrint = false ;
178- @ Parameter ( property = "resolve.readAllResources" )
179- private Boolean readAllResources = Boolean . TRUE ;
319+ @ Parameter (property = "resolve.prettyPrint" )
320+ private Boolean prettyPrint ;
321+ @ Parameter (property = "resolve.readAllResources" )
322+ private Boolean readAllResources ;
180323 @ Parameter ( property = "resolve.ignoredRoutes" )
181324 private Collection <String > ignoredRoutes ;
182325 /**
@@ -191,19 +334,38 @@ public void execute() throws MojoExecutionException, MojoFailureException
191334 @ Parameter ( property = "resolve.openapiFilePath" )
192335 private String openapiFilePath ;
193336
337+ /**
338+ * @since 2.0.8
339+ */
340+ @ Parameter (property = "resolve.configurationFilePath" )
341+ private String configurationFilePath ;
342+
194343 @ Parameter (defaultValue = "${project}" , readonly = true )
195344 private MavenProject project ;
196345
197346 @ Parameter ( property = "resolve.encoding" )
198347 private String encoding ;
199348
200349 private String projectEncoding = "UTF-8" ;
350+ private SwaggerConfiguration config ;
201351
202352 public String getOutputPath () {
203353 return outputPath ;
204354 }
355+
205356 public String getOpenapiFilePath () {
206357 return openapiFilePath ;
207358 }
208359
360+ String getConfigurationFilePath () {
361+ return configurationFilePath ;
362+ }
363+
364+ void setContextId (String contextId ) {
365+ this .contextId = contextId ;
366+ }
367+
368+ SwaggerConfiguration getInternalConfiguration () {
369+ return config ;
370+ }
209371}
0 commit comments