I want to customize Teamcity notification email with freemarker template.
It's feasible to get Teamcity data model from freemarker.
The data model is look like below
<#-- #ftlvariable name="project" type="jetbrains.buildServer.serverSide.SProject" -->
<#-- #ftlvariable name="buildType" type="jetbrains.buildServer.serverSide.SBuildType" -->
<#-- #ftlvariable name="build" type="jetbrains.buildServer.serverSide.SBuild" -->
<#-- #ftlvariable name="agentName" type="java.lang.String" -->
<#-- #ftlvariable name="buildServer" type="jetbrains.buildServer.serverSide.SBuildServer" -->
<#-- #ftlvariable name="webLinks" type="jetbrains.buildServer.serverSide.WebLinks" -->
<#-- #ftlvariable name="var.buildFailedTestsErrors" type="java.lang.String" -->
<#-- #ftlvariable name="var.buildShortStatusDescription" type="java.lang.String" -->
<#-- #ftlvariable name="var.buildChanges" type="java.lang.String" -->
<#-- #ftlvariable name="var.buildCompilationErrors" type="java.lang.String" -->
<#-- #ftlvariable name="link.editNotificationsLink" type="java.lang.String" -->
<#-- #ftlvariable name="link.buildResultsLink" type="java.lang.String" -->
<#-- #ftlvariable name="link.buildChangesLink" type="java.lang.String" -->
<#-- #ftlvariable name="responsibility" type="jetbrains.buildServer.responsibility.ResponsibilityEntry" -->
<#-- #ftlvariable name="oldResponsibility" type="jetbrains.buildServer.responsibility.ResponsibilityEntry" -->
I know it's possible to get bean property by its name directly, for example, there is inherited getName() method in jetbrains.buildServer.serverSide.SBuildType, so I can get the name property by buildType.name from freemarker template.
However, for method below, I have no idea how to get buildArtifacts with different parameters from template.
The method doc is as below:
getArtifacts
#NotNull
BuildArtifacts getArtifacts(#NotNull
BuildArtifactsViewMode mode)
Returns accessor for build artifacts. This accessor checks all necessary permissions for accessing files.
Parameters:
mode - view mode to see artifacts
Returns:
build artifacts viewer
The BuildArtifactsViewMode's definition is as below,
Enum BuildArtifactsViewMode
VIEW_ALL
Will show all build artifacts i.e.
VIEW_ALL_WITH_ARCHIVES_CONTENT
Will show all build artifacts and archives content
VIEW_DEFAULT
Will show all user-published artifacts
VIEW_DEFAULT_WITH_ARCHIVES_CONTENT
Will show all user-published artifacts and archives content
VIEW_HIDDEN_ONLY
Will show only hidden build artifacts
Thanks for your help in advance.
I don't know anything about Teamcity, but as a template author, you are reliant on what was exposed to you, and classes and enums aren't exposed in a minimal FreeMarker setup. If you have the power to configurte FreeMarker in Teamcity, or to add Java code that populates the data-model, then you can do something like this (and here I use the Configuration-based approach, but you could add these to the data-model similarly as well):
cfg.setSharedVariable(
"enums",
((BeansWrapper) cfg.getObjectWrapper()).getEnumModels());
and then in the template:
${someMethod(enums['com.example.MyEnum'].FOO)}
and/or you could do this:
cfg.setSharedVariable(
"MyEnum",
((BeansWrapper) cfg.getObjectWrapper()).getEnumModels().get(MyEnum.class.getName()));
and then in the template:
${someMethod(MyEnum.FOO)}
If Teamcity doesn't allow you to do such things, then it's on them to ensure some similar facility to you, and let's hope someone else knows about that...
Related
I'm using VS Code for Java development and working with other developers who use IntelliJ. I'd like to use the Organize Imports command (Shift+Alt+O) to clean up my imports, but I don't want to fight over import order with every commit. So I'd like to configure VS Code to organize the imports in the same order as IntelliJ's default. Does anybody have a configuration that would do this?
If this is not possible, is there a workspace configuration I can apply to both VS Code and IntelliJ so that the two IDEs will agree, even if they aren't agreeing on IntelliJ's default?
We were able to get it the almost identical with the following config tweaks.
VS Code:
{
"java.completion.importOrder": [
"",
"javax",
"java",
"#"
]
}
IntelliJ
The only difference from the IntelliJ default is a new line between import javax... and import java....
It's possible to get VS Code and IntelliJ to agree on a standard format, as long as that standard format:
Puts static imports at the top*
Separates all specific sections with empty lines
Puts everything not in its own specific section in a catch-all section at the end*
Never uses wildcard imports
Not actually true; static imports can be positioned in VS Code with '#', and everything else can be position in VS Code with ''.
IntelliJ's default settings don't work for this, but it is flexible enough to be reconfigured. Here are the files to add to a project to make just that project set up consistent rules for both IDEs (make sure they are not excluded in .gitignore).
Rule: The following groups separated by empty lines: Static imports, java.*, javax.*, everything else.
.vscode/settings.json:
{
"java.completion.importOrder": ["java", "javax"],
}
.idea/codeStyles/codeStyleConfig.xml:
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>
.idea/codeStyles/Project.xml
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JavaCodeStyleSettings>
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
<option name="IMPORT_LAYOUT_TABLE">
<value>
<package name="" withSubpackages="true" static="true" />
<emptyLine />
<package name="java" withSubpackages="true" static="false" />
<emptyLine />
<package name="javax" withSubpackages="true" static="false" />
<emptyLine />
<package name="" withSubpackages="true" static="false" />
</value>
</option>
</JavaCodeStyleSettings>
</code_scheme>
</component>
I'm testing for the first time Thymeleaf 3.x with a simples web app, and I'm having some issues doing form validation. I'm not able to change the graphical style of my form fields with the Thymeleaf attribute th:errorclass, and I don't know why.
Here is my controller:
#RequestMapping(value = {"/" , "" , "/home" , "/index" , "/login"} , method = RequestMethod.GET)
public String getLogin(final Model m)
{
if(m.containsAttribute("login") == false)
{
System.out.println("Creating login form...");
m.addAttribute("login", new For_Login());
}
return Util_Paginas.PAG_LOGIN.toString();
}
#RequestMapping(value = "/login" , method = RequestMethod.POST)
public String authenticate(
#Valid #ModelAttribute("login") final For_Login form ,
final BindingResult br ,
final Model m)
{
System.out.printf("Authentication received!%n");
System.out.printf("LOGIN: %s%n" , form.getLogin());
System.out.printf("PASS: %s%n" , form.getSenha());
if(br.hasErrors())
{
System.out.printf("Found %d fields!%n" , br.getErrorCount());
}
return Util_Paginas.PAG_LOGIN.toString();
}
My HTML file with Thymeleaf 3.x nature:
<!DOCTYPE html>
<html>
<head>
<title>Login page</title>
<link rel="stylesheet" type="text/css" media="all" th:href="#{/css/estilos.css}" />
</head>
<body>
<p>Welcome. Please login.</p>
<form action="#" th:action="#{/login}" th:object="${login}" method="post">
<fieldset>
<p>
Login:
<input
type="text"
th:field="*{login}"
th:class="campo"
th:errorclass="campo-invalido">
</p>
<p>
Password:
<input
type="password"
th:field="*{senha}"
th:class="campo"
th:errorclass="campo-invalido">
</p>
<p>
<input type="submit" value="Submit"/>
</p>
</fieldset>
</form>
</body>
</html>
My CSS file:
#CHARSET "ISO-8859-1";
.campo
{
background-color: gray;
border: 2px solid red;
}
.campo-invalido
{
background-color : #ff0000;
border: 1px solid #800000;
width: 230px;
color: white;
}
p
{
color: blue;
font-size: 22pt;
}
What is happening is that the style is not being applied. Errors are found on the server side, but when the page is returned, it is as if the th: errorclass attribute was not present. I do not know what I'm doing wrong.
I know that my css file is being read because the P tags are getting stylized.
I'm using:
Eclipse Mars.2 Release (4.5.2)
thymeleaf 3.0.3 RELEASE
thymeleaf-spring4 3.0.3 RELEASE
spring-webmvc 4.1.1 RELEASE
spring-context 4.1.1 RELEASE
validation-api 1.1.0 FINAL
hibernate-validator 5.3.4 FINAL
If in case someone wants to see some more code of my project, I'll be happy to present it here.
Thank you for your time and patience.
EDIT
I almost forgot something important too, my Spring MVC Application Context file:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd ">
<!-- ################################################################################### -->
<!-- MAPEAMENTO DE RECURSOS -->
<!-- ################################################################################### -->
<!-- Mapeamento de recursos (arquivos css, fontes, imagens, dentre outros). -->
<mvc:resources mapping="/css/**" location="/WEB-INF/recursos/css/" />
<mvc:resources mapping="/imagens/**" location="/WEB-INF/recursos/imagens/" />
<mvc:resources mapping="/fontes/**" location="/WEB-INF/recursos/fontes/" />
<!-- ################################################################################### -->
<!-- ANOTAÇÕES E RECURSOS SPRING -->
<!-- ################################################################################### -->
<!-- Possibilita o uso de anotações Spring Mvc. -->
<mvc:annotation-driven />
<!-- Define local para procura de componentes Spring (beans configurados
por anotações em classes). -->
<context:component-scan base-package="com.regra7.st.controle" />
<!-- ################################################################################### -->
<!-- INTERNACIONALIZAÇÃO -->
<!-- ################################################################################### -->
<!-- ################################################################################### -->
<!-- CONFIGURAÇÕES DO THYMELEAF -->
<!-- ################################################################################### -->
<!-- Template Resolver para Template Engine. -->
<!-- <bean id="templateResolver" class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">
<property name="prefix" value="/WEB-INF/templates/" /> <property name="suffix"
value=".html" /> <property name="templateMode" value="HTML5" /> </bean> -->
<!-- SpringResourceTemplateResolver automatically integrates with Spring's
own -->
<!-- resource resolution infrastructure, which is highly recommended. -->
<bean id="templateResolver"
class="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="/WEB-INF/templates/" />
<property name="suffix" value=".html" />
<!-- HTML is the default value, added here for the sake of clarity. -->
<property name="templateMode" value="HTML" />
<!-- Template cache is true by default. Set to false if you want -->
<!-- templates to be automatically updated when modified. -->
<property name="cacheable" value="true" />
</bean>
<!-- SpringTemplateEngine automatically applies SpringStandardDialect and -->
<!-- enables Spring's own MessageSource message resolution mechanisms. -->
<bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine">
<property name="templateResolver" ref="templateResolver" />
<!-- Enabling the SpringEL compiler with Spring 4.2.4 or newer can speed
up -->
<!-- execution in most scenarios, but might be incompatible with specific -->
<!-- cases when expressions in one template are reused across different
data -->
<!-- ypes, so this flag is "false" by default for safer backwards -->
<!-- compatibility. -->
<property name="enableSpringELCompiler" value="true" />
</bean>
<!-- ################################################################################### -->
<!-- CONFIGURAÇÕES DO SPRING MVC -->
<!-- ################################################################################### -->
<!-- View resolver do Thymeleaf. -->
<bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
<property name="templateEngine" ref="templateEngine" />
</bean>
<!-- Definição de HandlerMapping. -->
<!-- Cuida de classes controladoras. -->
<!-- <bean id="handlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
</bean> -->
<!-- ################################################################################### -->
<!-- INTERCEPTADORES -->
<!-- ################################################################################### -->
<!-- INTERCEPTADORES -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/usuario/*" />
<bean class="com.regra7.st.interceptadores.Login" />
</mvc:interceptor>
</mvc:interceptors>
</beans>
EDIT 2
After messing around a bit with the validation code, I changed the approach used and suddenly, things started to work.
Previously, I was using bean validation with my own annotations for data validation. I decided to change to the validation done with Spring, using org.springframework.validation.Validator, And magically everything went well.
However, I would not like to use the data validation approach with Spring, but rather with bean validation annotations. I've figured out where I'm wrong, but I do not understand why.
I'll put my login validation code:
// My bean validation annotation.
#Documented
#Constraint(validatedBy = Val_Login.class)
#Target({ElementType.METHOD , ElementType.FIELD , ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface Login
{
String message() default "{com.regra7.st.login}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
int min() default 10;
int max() default 20;
}
// Validator implementation.
public class Val_Login implements ConstraintValidator<Login , String>
{
private int min;
// private int max;
#Override
public void initialize(Login login)
{
this.min = login.min();
// this.max = login.max();
}
#Override
public boolean isValid(String value, ConstraintValidatorContext context)
{
return Util_Validador.isLoginValido(value , this.min);
}
}
// The form input object.
public class For_Login
{
#Login(min = 8)
private String login;
// Many things ommited
}
The Util_Validador class is able to return a true or false value, this has already been tested. If I do this:
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
import com.regra7.st.formularios.For_Login;
import com.regra7.st.util.Util_Texto;
public class ValidadorFormulario implements Validator
{
#Override
public boolean supports(Class<?> clazz)
{
return For_Login.class.isAssignableFrom(clazz);
}
#Override
public void validate(Object objeto, Errors erros)
{
For_Login log = (For_Login) objeto;
if(Util_Texto.isVazia(log.getLogin()) ||
log.getLogin().length() < 8)
{
erros.rejectValue("login", "Login errado!");
}
if(Util_Texto.isVazia(log.getSenha()) ||
log.getSenha().length() < 14)
{
erros.rejectValue("senha", "Senha errada!");
}
}
}
Everything works out. Can you guys help me a bit, please? I think this can help many people who get on the same place as me.
As always, thank you.
I did two things wrong:
I forgot to import some libraries needed by Hibernate Validator.
I was using prefixes for form fields (For_Login). The data binding
was not happening properly. Actually, it was, but Thymeleaf was not
recognizing it properly. It's no use having a field named _myField
(for instance), and setting setMyField and getMyField methods. Either
you write the field with the name myField, or the methods with the
names get_myField and set_myField.
I have a custom content model I created for Alfresco that has type with a d:date property. I am able to build the repository and share projects with seemingly no errors. However, I am unable to search by the properties using the data type d:date or d:int. I resolved the d:int problem by changing the data type to d:text and adding a regex constraint, but I'm not sure if that would be prudent for the d:date property.
Is there some additional configuration that I need to supply or create in order to search by properties that are not d:text?
Here is a snippet showing the type declaration:
<types>
<!-- Enterprise-wide generic document type -->
<type name="gl:x">
<title>Document</title>
<parent>cm:content</parent>
<properties>
<property name="gl:period">
<type>d:text</type>
</property>
<property name="gl:year">
<type>d:text</type>
<constraints>
<constraint ref="gl:documentYears" />
</constraints>
</property>
<property name="gl:docType">
<type>d:text</type>
<constraints>
<constraint ref="gl:documentTypeList" />
</constraints>
</property>
<property name="gl:date">
<type>d:date</type>
</property>
</properties>
</type>
</types>
The share search forms and properties forms seem to be rendering correctly, so I don't think that there is any problem within those.
The advanced search page accepts two types of parameters.
One is simply the "keywords" field. This performs a full text search, i.e. it looks for the provided keywords in ANY text property. There is no need to configure the full text search for custom types (e.g. your gl:x) - it automatically picks up any text property in any model in the system.
The other is the group of single parameters: name, title, description, mime-type, modified-date, modifier. These properties can be of any type. A d:date property would be perfectly acceptable here, as the modified-date parameter testifies.
But here custom properties are not picked-up automatically. They need to be configured explicitly.
Notice that in the upper part of the advanced search page is a drop-down called "Look for" with two options: content and folders. The best approach would be to add an option for your content type gl:x and to configure a search form for it.
You can find the definition of the two standard search forms in tomcat/webapps/share/WEB-INF/classes/alfresco/share-form-config.xml. The file is rather long so here are the two sections to look for:
<config evaluator="model-type" condition="cm:content">
<forms>
<!-- Default Create Content form -->
<form>
</form>
<!-- Document Library Create Google Doc form -->
<form id="doclib-create-googledoc">
</form>
<!-- Search form -->
<form id="search">
</form>
</forms>
</config>
<!-- cm:folder type (creating nodes) -->
<config evaluator="model-type" condition="cm:folder">
<forms>
<!-- Document Library Common form -->
<form id="doclib-common">
</form>
<!-- Search form -->
<form id="search">
</form>
</forms>
</config>
I've skipped the details, but what is important is that "cm:content" and "cm:folder" each defines a <form id="search"> with the desired search properties/parameters.
As an experiment you could modify share-form-config.xml directly and add your own definition:
<config evaluator="model-type" condition="gl:x">
<forms>
<!-- Search form -->
<form id="search">
<field-visibility>
<show id="gl:date" />
</field-visibility>
<appearance>
<field id="gl:date">
<control template="/org/alfresco/components/form/controls/daterange.ftl" />
</field>
</appearance>
</form>
</forms>
</config>
Also you have to add the new search form to the AdvancedSearch configuration found in tomcat/webapps/share/WEB-INF/classes/alfresco/share-config.xml:
<config evaluator="string-compare" condition="AdvancedSearch">
<advanced-search>
<forms>
<form labelId="search.form.label.cm_content" descriptionId="search.form.desc.cm_content">cm:content</form>
<form labelId="search.form.label.cm_folder" descriptionId="search.form.desc.cm_folder">cm:folder</form>
<form labelId="search.form.label.gl_x" descriptionId="search.form.desc.gl_x">gl:x</form>
</forms>
</advanced-search>
</config>
Remember to restart alfresco after every change.
When you're satisfied with the results, it would be better to move your custom definitions to a separate share-config-custom.xml in your project (share-config.xml and share-form-config.xml should never be modified directly).
For more details: https://wiki.alfresco.com/wiki/Share_Advanced_Search
I'm trying to extract the URL's of a website that doesn't have a sitemap. I'm using the Web Harvest tool
I have no idea about Java or coding. Could someone please help me out with using this tool.
I want it to run on a specific website (e.g. example.com) and extract every single URL from that website.
Example.com is not a very good example, as it has only one link! :)
Here's my code with some annotations:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<!-- 1: provide inputs -->
<script><![CDATA[
url="http://stackoverflow.com/questions/17635763/trying-to-extract-urls-from-a-website-using-web-harvest";
output_path = "C:/webharvest/";
file_name = "urllist.txt";
output_file = output_path + file_name;
]]></script>
<!-- 5 : save the resulting list in a variable -->
<var-def name="urls">
<!-- 4 : select only links (outputs a list variable) -->
<xpath expression='//a/#href'>
<!-- 3 : convert it to XML, for querying -->
<html-to-xml>
<!-- 2 : load the page -->
<http url="${url}"/>
</html-to-xml>
</xpath>
</var-def>
<!-- 7: write to output file -->
<file action="write" path="${output_file}">
<!-- 6 : convert the list variable into a string with each link on a new line -->
<text delimiter="${sys.cr}${sys.lf}">
<var name="urls" />
</text>
</file>
</config>
You should go through Web harvest user manual at http://web-harvest.sourceforge.net/manual.php which has multiple number of examples.
I recently deployed inadvertently a debug version of our game typrX
(typing races at www.typrx.com - try it it's fun).
It was quickly corrected but I know it may happen again. After digging
on Google I found some info how to create 2 different profiles, one
for development mode that has the debug functions and one used for
deployment. Here is what I found from a Google IO presentation. Does
anyone have this setup? Can someone explains how to run this?
MyAppCommon.gwt.xml
<module>
...
<define-property values="debug, release" name="app.config" />
<replace-with class="myapp.debug.DebugConsole">
<when-type-is class="myapp.Console" />
<when-property-is name="app.config" value="debug" />
</replace-with>
...
</module>
MyAppDebug.gwt.xml
<module>
...
<set-property name="app.config" value="debug" />
</module>
The idea of using a specific module for debugging has been floating around for some times, and was also mentioned in this Google I/O presentation (see slide 33 from PDF or at 0h31m in the video).
The basic idea is that you have a standard GWT module, and a second debug module that inherits this standard module, configures some properties, and uses GWT's deferred binding to replace some classes with specific instances when debugging.
Then, you only have to configure your Maven / ant build to compile the appropriate module depending on wether you are in development mode or in release mode.
In my project, I did not create an "app.config" deferred binding property, but I might do that later on. What I did was the following:
Created a standard module
com/example/MainModule.gwt.xml:
<module rename-to="mainModule">
<inherits name="com.smartgwt.SmartGwt" />
<!-- (other configurations) -->
<!-- gwt-log configuration -->
<define-property name="log_level" values="OFF,DEBUG" />
<inherits name="com.allen_sauer.gwt.log.gwt-log-common" />
<!-- Locales we want to compile for -->
<extend-property name="locale" values="en" />
<extend-property name="locale" values="fr_FR" />
</module>
Created a "debug" module, that inherits the standard module and configures some additional properties for development
com/example/MainModuleDebug.gwt.xml:
<module rename-to="mainModule">
<inherits name="com.example.MainModule" />
<set-property name="user.agent" value="gecko1_8" />
<set-property name="locale" value="fr_FR"/>
<set-property name="log_level" value="DEBUG" />
</module>
Note: the rename-to attribute is very important here, since you want both modules to be deployed under the exact same name. When you compile during development, you do not want to have to change all your html host pages to point to the debug module.
Configured Maven and the gwt-maven-plugin to compile the right module
<project>
(...)
<properties>
(...)
<!--
Suffix appended to the names of the GWT modules we compile in our child projects.
Empty by default, this suffix is overriden by some profiles to specify an alternative module to compile.
-->
<gwt.module.suffix></gwt.module.suffix>
<!-- We force GWT-recompilation by default (except when using the "gwtDebug" profile - see below for more info) -->
<gwt.compiler.force>true</gwt.compiler.force>
</properties>
(...)
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>gwt-maven-plugin</artifactId>
<configuration>
(...)
<module>com.example.MainModule${gwt.module.suffix}</module>
</configuration>
</plugin>
(...)
<profiles>
<!-- This profile should be used during *DEVELOPMENT* -->
<profile>
<id>gwtDebug</id>
<properties>
<gwt.module.suffix>Debug</gwt.module.suffix>
<!-- Tells gwt-maven-plugin to recompile GWT modules only when necessary -->
<gwt.compiler.force>false</gwt.compiler.force>
</properties>
<activation>
<property>
<name>gwtDebug</name>
</property>
</activation>
</profile>
</profiles>
</project>
Simply doing "maven clean install" will compile the production module. In development, you use "mvn clean install -DgwtDebug" to activate the gwtDebug profile, which in turn compiles the debug module.
Of course, you could configure your ~/.m2/settings.xml to always define the "gwtDebug" property...
The same idea would also apply to Ant. But I'm not well versed with it.
When you starts to toy with the idea of overriding your real module with a debug module, you start to envision some very cool possibilities:
You could add performance logs, which would be pruned from the code when in production.
You could configure all your toString() methods to return something useful when in debug mode, and the empty string when in production (and thus reduce the .js size).
You may reduce the number of permutations by specifying only one locale / one browser / one log level, to speed up the compilation (but do not forget to test for other locales / browsers from time to time).